From 826cb23a70bc16c2fa1aa342631ad996a553f853 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Mon, 7 Sep 2026 22:20:25 +0700 Subject: [PATCH] feat : update --- app/admin/[character]/[page]/page.tsx | 2 + components/admin/guide-section-editor.tsx | 129 +- components/public/artifact-image-export.tsx | 10 +- components/public/structured-guide.tsx | 14 +- components/public/team-search.tsx | 2 +- db/schema.ts | 4 +- drizzle/0039_flowery_smiling_tiger.sql | 2 + drizzle/meta/0039_snapshot.json | 3959 +++++++++++++++++++ drizzle/meta/_journal.json | 7 + lib/guides/mutations.ts | 4 +- lib/guides/queries.ts | 4 +- lib/guides/schemas.ts | 6 +- 12 files changed, 4095 insertions(+), 48 deletions(-) create mode 100644 drizzle/0039_flowery_smiling_tiger.sql create mode 100644 drizzle/meta/0039_snapshot.json diff --git a/app/admin/[character]/[page]/page.tsx b/app/admin/[character]/[page]/page.tsx index 66860df..e2f61ba 100644 --- a/app/admin/[character]/[page]/page.tsx +++ b/app/admin/[character]/[page]/page.tsx @@ -118,6 +118,7 @@ export default async function StructuredEditorPage({ artifactProfile: content.artifactProfile, constellationGroups: content.constellationGroups.map((group) => ({ id: group.id, + name: group.name, note: group.note, rows: group.rows, })), @@ -138,6 +139,7 @@ export default async function StructuredEditorPage({ })), teamGroups: content.teamGroups.map((group) => ({ id: group.id, + name: group.name, note: group.note, teams: group.teams.map((team) => ({ id: team.id, diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx index afa30f5..4f3e8f0 100644 --- a/components/admin/guide-section-editor.tsx +++ b/components/admin/guide-section-editor.tsx @@ -241,6 +241,7 @@ type ArtifactProfile = { substats: string[]; targets: Array<{ stat: string; value: string }>; targetConditions: Array<{ + name?: string | null; note: string | null; rows: Array<{ stat: string; value: string }>; }>; @@ -255,6 +256,7 @@ type ConstellationValue = { }; type ConstellationGroupValue = { id: string; + name: string | null; note: string | null; rows: ConstellationValue[]; }; @@ -286,6 +288,7 @@ type TeamValue = { }; type TeamGroupValue = { id: string; + name: string | null; note: string | null; teams: TeamValue[]; }; @@ -315,6 +318,47 @@ function createClientId(prefix: string): string { return `${prefix}-${Date.now()}-${clientIdCounter}`; } +function ConditionRenameDialog({ + open, + fallback, + value, + onOpenChange, + onChange, +}: { + open: boolean; + fallback: string; + value: string; + onOpenChange: (open: boolean) => void; + onChange: (value: string) => void; +}) { + return ( + + + + Rename condition + + Leave blank to use {fallback}. + + + + Condition name + onChange(event.target.value)} + /> + + + + + + + ); +} + function parseEditorDraft( value: unknown, arrayKeys: string[], @@ -1122,6 +1166,7 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { })); }); const [activeGroupIndex, setActiveGroupIndex] = useState(0); + const [renamingWeaponGroupIndex, setRenamingWeaponGroupIndex] = useState(null); const rows = groups[activeGroupIndex]?.rows ?? []; const setRows = (update: typeof rows | ((current: typeof rows) => typeof rows)) => setGroups((current) => current.map((group, index) => index === activeGroupIndex @@ -1206,6 +1251,7 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { key={group.clientId} value={String(index)} className="aria-pressed:bg-primary aria-pressed:text-primary-foreground" + onDoubleClick={() => setRenamingWeaponGroupIndex(index)} > {group.name.trim() || `Condition ${index + 1}`} @@ -1242,18 +1288,13 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { Duplicate Condition - - - Condition name (optional) - - setGroups(groups.map((group, index) => index === activeGroupIndex ? { ...group, name: event.target.value } : group))} - /> - + { if (!open) setRenamingWeaponGroupIndex(null); }} + onChange={(name) => setGroups(groups.map((group, index) => index === renamingWeaponGroupIndex ? { ...group, name } : group))} + /> Condition note (optional) @@ -1583,7 +1624,7 @@ function ArtifactEditor({ const [targetGroups, setTargetGroups] = useState(() => { const source = profile?.targetConditions.length ? profile.targetConditions - : [{ note: null, rows: profile?.targets ?? [] }]; + : [{ name: null, note: null, rows: profile?.targets ?? [] }]; return source.map((group, index) => { const existing = new Map(group.rows.flatMap((target) => { const stat = normalizeTargetStat(target.stat); @@ -1591,12 +1632,14 @@ function ArtifactEditor({ })); return { clientId: `target-condition-${index}`, + name: group.name ?? "", note: group.note ?? "", rows: targetStatDefaults.map((target) => ({ stat: target.stat, value: existing.get(target.stat) ?? target.value })), }; }); }); const [activeTargetGroupIndex, setActiveTargetGroupIndex] = useState(0); + const [renamingTargetGroupIndex, setRenamingTargetGroupIndex] = useState(null); const targets = targetGroups[activeTargetGroupIndex]?.rows ?? []; const setTargets = (rows: typeof targets) => setTargetGroups((current) => current.map((group, index) => index === activeTargetGroupIndex ? { ...group, rows } : group)); const [talents, setTalents] = useState(() => { @@ -1635,6 +1678,7 @@ function ArtifactEditor({ circlet, substats, targetGroups: targetGroups.map((group) => ({ + name: group.name.trim() || null, note: group.note.trim() || null, rows: group.rows.filter((target) => target.value.trim()).map((target) => ({ stat: target.stat, value: target.value.trim() })), })), @@ -1686,7 +1730,7 @@ function ArtifactEditor({ setGoblet(draft.goblet); setCirclet(draft.circlet); setSubstats(draft.substats); - setTargetGroups(draft.targetGroups); + setTargetGroups(draft.targetGroups.map((group) => ({ ...group, name: group.name ?? "" }))); setActiveTargetGroupIndex(draft.activeTargetGroupIndex ?? 0); setTalents(draft.talents); setNote(draft.note); @@ -2054,19 +2098,26 @@ function ArtifactEditor({ if (values[0]) setActiveTargetGroupIndex(Number(values[0])); }}> {targetGroups.map((group, index) => ( - - Condition {index + 1} + setRenamingTargetGroupIndex(index)}> + {group.name.trim() || `Condition ${index + 1}`} ))} + { if (!open) setRenamingTargetGroupIndex(null); }} + onChange={(name) => setTargetGroups(targetGroups.map((group, index) => index === renamingTargetGroupIndex ? { ...group, name } : group))} + /> @@ -2278,17 +2329,20 @@ function ConstellationEditor({ const [groups, setGroups] = useState(() => { const source = initial.constellationGroups.length ? initial.constellationGroups - : [{ id: "constellation-condition-default", note: null, rows: [] }]; + : [{ id: "constellation-condition-default", name: null, note: null, rows: [] }]; return source.map((group) => ({ clientId: group.id, + name: group.name ?? "", note: group.note ?? "", rows: makeRows(group.rows), })); }); const [activeConstellationGroupIndex, setActiveConstellationGroupIndex] = useState(0); + const [renamingConstellationGroupIndex, setRenamingConstellationGroupIndex] = useState(null); const value = { note: note.trim() || null, groups: groups.map((group) => ({ + name: group.name.trim() || null, note: group.note.trim() || null, rows: group.rows .filter((row) => row.description.trim() && row.cumulativePercent !== "") @@ -2319,7 +2373,7 @@ function ConstellationEditor({ parseEditorDraft(candidate, ["groups"], ["note"]), restoreLocalDraft: (draft) => { setNote(draft.note); - setGroups(draft.groups); + setGroups(draft.groups.map((group) => ({ ...group, name: group.name ?? "" }))); }, }); return ( @@ -2355,11 +2409,19 @@ function ConstellationEditor({ key={group.clientId} value={String(index)} className="aria-pressed:bg-primary aria-pressed:text-primary-foreground" + onDoubleClick={() => setRenamingConstellationGroupIndex(index)} > - Condition {index + 1} + {group.name.trim() || `Condition ${index + 1}`} ))} + { if (!open) setRenamingConstellationGroupIndex(null); }} + onChange={(name) => setGroups(groups.map((group, index) => index === renamingConstellationGroupIndex ? { ...group, name } : group))} + /> {groups[activeConstellationGroupIndex] ? [groups[activeConstellationGroupIndex]].map((group) => { const groupIndex = activeConstellationGroupIndex; return ( @@ -2536,6 +2598,7 @@ function ConstellationEditor({ ...groups, { clientId: createClientId("constellation-condition"), + name: "", note: "", rows: makeRows(), }, @@ -2586,9 +2649,10 @@ function TeamEditor({ }), ); const [teamGroups, setTeamGroups] = useState(() => initial.teamGroups.length - ? initial.teamGroups.map((group) => ({ clientId: group.id, note: group.note ?? "", teams: group.teams.map(({ id, ...team }) => ({ clientId: id, ...team })) })) - : [{ clientId: "team-condition-default", note: "", teams: [] as TeamToolValue[] }]); + ? initial.teamGroups.map((group) => ({ clientId: group.id, name: group.name ?? "", note: group.note ?? "", teams: group.teams.map(({ id, ...team }) => ({ clientId: id, ...team })) })) + : [{ clientId: "team-condition-default", name: "", note: "", teams: [] as TeamToolValue[] }]); const [activeTeamGroupIndex, setActiveTeamGroupIndex] = useState(0); + const [renamingTeamGroupIndex, setRenamingTeamGroupIndex] = useState(null); const teams = useMemo(() => teamGroups[activeTeamGroupIndex]?.teams ?? [], [activeTeamGroupIndex, teamGroups]); const setTeams = (update: SetStateAction) => setTeamGroups((current) => current.map((group, index) => index === activeTeamGroupIndex ? { ...group, teams: typeof update === "function" ? update(group.teams) : update } : group)); const [selectedTeamIds, setSelectedTeamIds] = useState>( @@ -2636,7 +2700,7 @@ function TeamEditor({ }, searchQuery, searchTarget)); }, [artifacts, characters, searchQuery, searchTarget, teams, weapons]); const value = { - groups: teamGroups.map((group) => ({ note: group.note.trim() || null, teams: group.teams.map((team) => ({ + groups: teamGroups.map((group) => ({ name: group.name.trim() || null, note: group.note.trim() || null, teams: group.teams.map((team) => ({ title: team.title, rotationSeconds: Number(team.rotationSeconds), combo: team.combo, @@ -2735,7 +2799,7 @@ function TeamEditor({ parseLocalDraft: (candidate) => parseEditorDraft(candidate, ["teamGroups"]), restoreLocalDraft: (draft) => { - setTeamGroups(draft.teamGroups); + setTeamGroups(draft.teamGroups.map((group) => ({ ...group, name: group.name ?? "" }))); setActiveTeamGroupIndex(draft.activeTeamGroupIndex ?? 0); }, }); @@ -2962,13 +3026,20 @@ function TeamEditor({ }} > {teamGroups.map((group, index) => ( - - Condition {index + 1} + setRenamingTeamGroupIndex(index)} className="aria-pressed:bg-primary aria-pressed:text-primary-foreground"> + {group.name.trim() || `Condition ${index + 1}`} ))} + { if (!open) setRenamingTeamGroupIndex(null); }} + onChange={(name) => setTeamGroups(teamGroups.map((group, index) => index === renamingTeamGroupIndex ? { ...group, name } : group))} + />