Skip to content

Commit ee0d275

Browse files
committed
Refactor GroupAccumulatorsPanel to integrate AccumulatorTranslationsField
- Removed the AccumulatorAboutField component and replaced it with AccumulatorTranslationsField for managing multilingual titles and descriptions. - Updated state management to handle titles and descriptions for multiple languages. - Adjusted form state handling to align with the new translations structure, ensuring proper integration with existing functionality.
1 parent c6675a6 commit ee0d275

5 files changed

Lines changed: 174 additions & 234 deletions

File tree

‎src/components/routes/groups/api/groupAccumulatorsApi.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export interface GroupAccumulatorImage {
99

1010
export interface GroupAccumulatorMetadataDTO {
1111
language: LanguageCode;
12+
/** Per-language title. The EN entry (or the first translated one) also
13+
* becomes the accumulator's default `title`. */
14+
title?: string | null;
1215
description: string | null;
1316
}
1417

@@ -33,6 +36,7 @@ export interface GroupAccumulatorDTO {
3336
id: string;
3437
preset_accumulator_id: string | null;
3538
group_id: string;
39+
/** Default title; per-language titles live in `metadata`. */
3640
title: string | null;
3741
image: GroupAccumulatorImage | null;
3842
image_key: string | null;

‎src/components/routes/groups/components/AccumulatorAboutField.tsx‎

Lines changed: 0 additions & 167 deletions
This file was deleted.

‎src/components/routes/groups/components/GroupAccumulatorsPanel.tsx‎

Lines changed: 34 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { uploadImageToS3 } from "@/components/routes/task/api/taskApi";
1111
import { getApiErrorMessage } from "@/lib/apiErrors";
1212
import { canChangeContentStatus } from "@/lib/contentPermissions";
1313
import { isReviewer, shouldShowCmsActionsColumn } from "@/lib/platformAccess";
14-
import { normalizeLanguageCode } from "@/lib/languageCodes";
1514
import { fromBackendMidnightISO, toBackendMidnightISO } from "@/lib/utils";
1615
import { capitalizeFirstLetter } from "@/lib/textUtils";
1716
import { 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";
3234
import AccumulatorLinksField from "./AccumulatorLinksField";
3335
import {
3436
isLinkRowFilled,
@@ -52,56 +54,40 @@ type GroupAccumulatorsPanelProps = {
5254
};
5355

5456
type 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

6971
const 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-
9986
function 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

Comments
 (0)