@@ -11,7 +11,6 @@ import { uploadImageToS3 } from "@/components/routes/task/api/taskApi";
1111import { getApiErrorMessage } from "@/lib/apiErrors" ;
1212import { canChangeContentStatus } from "@/lib/contentPermissions" ;
1313import { isReviewer , shouldShowCmsActionsColumn } from "@/lib/platformAccess" ;
14- import { normalizeLanguageCode } from "@/lib/languageCodes" ;
1514import { fromBackendMidnightISO , toBackendMidnightISO } from "@/lib/utils" ;
1615import { capitalizeFirstLetter } from "@/lib/textUtils" ;
1716import { useUserInfo } from "@/hooks/useUserInfo" ;
@@ -26,9 +25,12 @@ import {
2625 type GroupAccumulatorDetailDTO ,
2726 type GroupAccumulatorDTO ,
2827 type GroupAccumulatorLinkInput ,
29- type GroupAccumulatorMetadataDTO ,
3028} from "../api/groupAccumulatorsApi" ;
31- import AccumulatorAboutField from "./AccumulatorAboutField" ;
29+ import AccumulatorTranslationsField from "./AccumulatorTranslationsField" ;
30+ import {
31+ metadataFromTranslationState ,
32+ translationStateFromAccumulator ,
33+ } from "./accumulatorTranslations" ;
3234import AccumulatorLinksField from "./AccumulatorLinksField" ;
3335import {
3436 isLinkRowFilled ,
@@ -52,56 +54,40 @@ type GroupAccumulatorsPanelProps = {
5254} ;
5355
5456type AccumulatorFormState = {
55- title : string ;
5657 target_count : string ;
5758 start_date : string | null ;
5859 end_date : string | null ;
5960 image_key : string | null ;
6061 image_preview : string | null ;
6162 preset : FkOption | null ;
62- about_languages : string [ ] ;
63- about_descriptions : Record < string , string > ;
63+ languages : string [ ] ;
64+ titles : Record < string , string > ;
65+ descriptions : Record < string , string > ;
6466 links : AccumulatorLinkRow [ ] ;
6567 metadata_loaded : boolean ;
6668 links_loaded : boolean ;
6769} ;
6870
6971const emptyFormState = ( ) : AccumulatorFormState => ( {
70- title : "" ,
7172 target_count : "" ,
7273 start_date : null ,
7374 end_date : null ,
7475 image_key : null ,
7576 image_preview : null ,
7677 preset : null ,
77- about_languages : [ ] ,
78- about_descriptions : { } ,
78+ languages : [ ] ,
79+ titles : { } ,
80+ descriptions : { } ,
7981 links : [ ] ,
8082 metadata_loaded : true ,
8183 links_loaded : true ,
8284} ) ;
8385
84- /** Turns the CMS `metadata` array into the About field's per-language state. */
85- function aboutStateFromMetadata (
86- metadata : GroupAccumulatorMetadataDTO [ ] | null | undefined ,
87- ) {
88- const languages : string [ ] = [ ] ;
89- const descriptions : Record < string , string > = { } ;
90- ( metadata ?? [ ] ) . forEach ( ( entry ) => {
91- const lang = normalizeLanguageCode ( String ( entry . language ?? "" ) ) ;
92- if ( ! lang || languages . includes ( lang ) ) return ;
93- languages . push ( lang ) ;
94- descriptions [ lang ] = entry . description ?? "" ;
95- } ) ;
96- return { languages, descriptions } ;
97- }
98-
9986function formStateFromAccumulator (
10087 accumulator : GroupAccumulatorDTO ,
10188) : AccumulatorFormState {
102- const about = aboutStateFromMetadata ( accumulator . metadata ) ;
89+ const translations = translationStateFromAccumulator ( accumulator ) ;
10390 return {
104- title : accumulator . title ?? "" ,
10591 target_count :
10692 accumulator . target_count != null ? String ( accumulator . target_count ) : "" ,
10793 start_date : accumulator . start_date ,
@@ -111,8 +97,9 @@ function formStateFromAccumulator(
11197 preset : accumulator . preset_accumulator_id
11298 ? { id : accumulator . preset_accumulator_id , title : "Linked preset" }
11399 : null ,
114- about_languages : about . languages ,
115- about_descriptions : about . descriptions ,
100+ languages : translations . languages ,
101+ titles : translations . titles ,
102+ descriptions : translations . descriptions ,
116103 links : linkRowsFromDetail ( accumulator ) ,
117104 metadata_loaded : Array . isArray ( accumulator . metadata ) ,
118105 links_loaded : Array . isArray ( accumulator . links ) ,
@@ -258,12 +245,7 @@ const GroupAccumulatorsPanel = ({
258245
259246 // Both arrays are a full replace server-side, so always send the complete
260247 // current list — a partial list deletes whatever is left out.
261- const metadata : GroupAccumulatorMetadataDTO [ ] = form . about_languages
262- . map ( ( lang ) => ( {
263- language : lang ,
264- description : ( form . about_descriptions [ lang ] ?? "" ) . trim ( ) ,
265- } ) )
266- . filter ( ( entry ) => entry . description . length > 0 ) ;
248+ const metadata = metadataFromTranslationState ( form ) ;
267249
268250 const links : GroupAccumulatorLinkInput [ ] = form . links
269251 . filter ( isLinkRowFilled )
@@ -274,7 +256,8 @@ const GroupAccumulatorsPanel = ({
274256
275257 return {
276258 accumulator_id : form . preset ?. id ?? null ,
277- title : form . title . trim ( ) || null ,
259+ // No `title`: the server keeps the row's default title in step with the
260+ // metadata titles below (English, or the first language that has one).
278261 image_key : form . image_key ,
279262 target_count : Number . isFinite ( target_count ) ? target_count : null ,
280263 start_date : form . start_date ,
@@ -539,25 +522,23 @@ const GroupAccumulatorsPanel = ({
539522 className = "flex min-h-0 flex-1 flex-col"
540523 >
541524 < div className = "min-h-0 flex-1 space-y-6 overflow-y-auto px-6 py-5" >
542- < div className = "grid gap-4 sm:grid-cols-[2fr_1fr]" >
543- < div className = "space-y-2" >
544- < label
545- className = "text-sm font-bold"
546- htmlFor = "accumulator-title"
547- >
548- Title
549- </ label >
550- < Pecha . Input
551- id = "accumulator-title"
552- value = { form . title }
553- onChange = { ( e ) =>
554- setForm ( ( prev ) => ( { ...prev , title : e . target . value } ) )
555- }
556- placeholder = "e.g. 100 Million Mani Retreat"
557- className = "h-11 bg-white dark:bg-[#262626]"
558- />
559- </ div >
525+ < div className = "pb-1" >
526+ < AccumulatorTranslationsField
527+ activeLanguages = { form . languages }
528+ titles = { form . titles }
529+ descriptions = { form . descriptions }
530+ onChange = { ( { activeLanguages, titles, descriptions } ) =>
531+ setForm ( ( prev ) => ( {
532+ ...prev ,
533+ languages : activeLanguages ,
534+ titles,
535+ descriptions,
536+ } ) )
537+ }
538+ />
539+ </ div >
560540
541+ < div className = "grid gap-4 sm:grid-cols-2 border-t pt-5" >
561542 < div className = "space-y-2" >
562543 < label
563544 className = "text-sm font-bold"
@@ -772,20 +753,6 @@ const GroupAccumulatorsPanel = ({
772753 imageClassName = "w-full max-w-xs h-32 rounded object-cover border"
773754 />
774755
775- < div className = "border-t pt-5" >
776- < AccumulatorAboutField
777- activeLanguages = { form . about_languages }
778- descriptions = { form . about_descriptions }
779- onChange = { ( about_languages , about_descriptions ) =>
780- setForm ( ( prev ) => ( {
781- ...prev ,
782- about_languages,
783- about_descriptions,
784- } ) )
785- }
786- />
787- </ div >
788-
789756 < div className = "border-t pt-5" >
790757 < AccumulatorLinksField
791758 rows = { form . links }
0 commit comments