diff --git a/app/admin/[character]/[page]/page.tsx b/app/admin/[character]/[page]/page.tsx index cb1bacf..99b0f59 100644 --- a/app/admin/[character]/[page]/page.tsx +++ b/app/admin/[character]/[page]/page.tsx @@ -22,5 +22,5 @@ export default async function StructuredEditorPage({ params }: { params: Promise const [catalog, content] = await Promise.all([getActiveCatalog(), getPublicGuide(guide.slug)]); if (!catalog || !content) notFound(); const guideCharacter = catalog.characters.find((item) => item.key === guide.characterKey); - return

{guide.name}

{guide.sections.find((section) => section.slug === page)?.title ?? page}

section.slug === page)?.note ?? null, weapons: content.weapons, artifactGroups: content.artifactGroups.map((group) => ({ id: group.id, name: group.name, rows: group.rows.map((row) => ({ firstArtifactKey: row.firstArtifactKey, secondArtifactKey: row.secondArtifactKey, percent: row.percent, note: row.note })) })), artifactProfile: content.artifactProfile, constellationGroups: content.constellationGroups.map((group) => ({ id: group.id, note: group.note, rows: group.rows })), teams: content.teams.map((team) => ({ id: team.id, title: team.title, rotationSeconds: team.rotationSeconds, combo: team.combo, members: team.members.map((member) => ({ characterKey: member.characterKey, weaponKey: member.weaponKey, artifactKey: member.artifactKey, secondArtifactKey: member.secondArtifactKey, constellation: member.constellation, refinement: member.refinement, damage: member.damage })) })), extras: content.extras.map((section) => ({ title: section.title, rows: section.rows.map((row) => ({ mediaId: row.mediaId, ratio: row.ratio === "1:1" ? "1:1" : "16:9", heading: row.heading, body: row.body, percent: row.percent, imageUrl: row.imageUrl })) })) }} />
; + return

{guide.name}

{guide.sections.find((section) => section.slug === page)?.title ?? page}

section.slug === page)?.note ?? null, weaponGroups: content.weaponGroups, artifactGroups: content.artifactGroups.map((group) => ({ id: group.id, name: group.name, rows: group.rows.map((row) => ({ firstArtifactKey: row.firstArtifactKey, secondArtifactKey: row.secondArtifactKey, percent: row.percent, note: row.note })) })), artifactProfile: content.artifactProfile, constellationGroups: content.constellationGroups.map((group) => ({ id: group.id, note: group.note, rows: group.rows })), teams: content.teams.map((team) => ({ id: team.id, title: team.title, rotationSeconds: team.rotationSeconds, combo: team.combo, members: team.members.map((member) => ({ characterKey: member.characterKey, weaponKey: member.weaponKey, artifactKey: member.artifactKey, secondArtifactKey: member.secondArtifactKey, constellation: member.constellation, refinement: member.refinement, damage: member.damage })) })), extras: content.extras.map((section) => ({ title: section.title, rows: section.rows.map((row) => ({ mediaId: row.mediaId, ratio: row.ratio === "1:1" ? "1:1" : "16:9", heading: row.heading, body: row.body, percent: row.percent, imageUrl: row.imageUrl })) })) }} />
; } diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx index df38493..67dc9b0 100644 --- a/components/admin/guide-section-editor.tsx +++ b/components/admin/guide-section-editor.tsx @@ -169,6 +169,11 @@ type WeaponValue = { note: string | null; weapon: CatalogItem; }; +type WeaponGroupValue = { + id: string; + note: string | null; + rows: WeaponValue[]; +}; type ArtifactValue = { id: string; name: string; @@ -265,7 +270,7 @@ export interface GuideSectionEditorProps { catalogTalents: CatalogTalent[]; initial: { sectionNote: string | null; - weapons: WeaponValue[]; + weaponGroups: WeaponGroupValue[]; artifactGroups: ArtifactValue[]; artifactProfile: ArtifactProfile; constellationGroups: ConstellationGroupValue[]; @@ -696,26 +701,40 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { }), ); const [note, setNote] = useState(initial.sectionNote ?? ""); - const [rows, setRows] = useState( - initial.weapons.map((row) => ({ + const [groups, setGroups] = useState(() => { + const source = initial.weaponGroups.length + ? initial.weaponGroups + : [{ id: "weapon-condition-default", note: null, rows: [] }]; + return source.map((group) => ({ + clientId: group.id, + note: group.note ?? "", + rows: group.rows.map((row) => ({ clientId: row.id, weaponKey: row.weaponKey, refinement: row.refinement, overallPercent: row.overallPercent ?? "", personalPercent: row.personalPercent ?? "", note: row.note ?? "", - })), - ); + })), + })); + }); + const [activeGroupIndex, setActiveGroupIndex] = useState(0); + const rows = groups[activeGroupIndex]?.rows ?? []; + const setRows = (update: typeof rows | ((current: typeof rows) => typeof rows)) => + setGroups((current) => current.map((group, index) => index === activeGroupIndex + ? { ...group, rows: typeof update === "function" ? update(group.rows) : update } + : group)); const value = { note: note.trim() || null, - rows: rows.map((row) => ({ - weaponKey: row.weaponKey, - refinement: row.refinement, - overallPercent: - row.overallPercent === "" ? null : Number(row.overallPercent), - personalPercent: - row.personalPercent === "" ? null : Number(row.personalPercent), - note: row.note.trim() || null, + groups: groups.map((group) => ({ + note: group.note.trim() || null, + rows: group.rows.map((row) => ({ + weaponKey: row.weaponKey, + refinement: row.refinement, + overallPercent: row.overallPercent === "" ? null : Number(row.overallPercent), + personalPercent: row.personalPercent === "" ? null : Number(row.personalPercent), + note: row.note.trim() || null, + })), })), }; const validation = weaponSectionSchema.safeParse({ @@ -724,7 +743,7 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { }); const weaponIssue = (...path: (string | number)[]) => !validation.success && hasIssuePrefix(validation.error.issues, ...path); - const localDraft = { note, rows }; + const localDraft = { note, groups, activeGroupIndex }; const autosave = useAutosave({ value, valid: validation.success, @@ -734,10 +753,11 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { storageKey: adminDraftKey(guide.id, "weapon"), localDraft, parseLocalDraft: (candidate) => - parseEditorDraft(candidate, ["rows"], ["note"]), + parseEditorDraft(candidate, ["groups"], ["note"]), restoreLocalDraft: (draft) => { setNote(draft.note); - setRows(draft.rows); + setGroups(draft.groups); + setActiveGroupIndex(draft.activeGroupIndex ?? 0); }, }); const handleWeaponDragEnd = ({ active, over }: DragEndEvent) => { @@ -768,6 +788,29 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { onChange={(event) => setNote(event.target.value)} /> +
+ {groups.map((group, index) => ( + + ))} + +
+ + + Condition note (optional) + +