From fcf6a2c187a2360c0357a55bb263516ea83920c7 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Sun, 6 Sep 2026 17:40:06 +0700 Subject: [PATCH] feat(guides) : add selectable target and team conditions --- app/admin/[character]/[page]/page.tsx | 19 + components/admin/guide-section-editor.tsx | 146 +- components/public/condition-switcher.tsx | 23 + components/public/structured-guide.tsx | 21 +- components/public/team-search.tsx | 13 +- db/schema.ts | 16 + drizzle/0037_right_rockslide.sql | 15 + drizzle/meta/0037_snapshot.json | 3941 +++++++++++++++++++++ drizzle/meta/_journal.json | 7 + lib/guides/mutations.ts | 16 +- lib/guides/queries.ts | 10 +- lib/guides/schemas.test.ts | 25 +- lib/guides/schemas.ts | 18 +- 13 files changed, 4207 insertions(+), 63 deletions(-) create mode 100644 components/public/condition-switcher.tsx create mode 100644 drizzle/0037_right_rockslide.sql create mode 100644 drizzle/meta/0037_snapshot.json diff --git a/app/admin/[character]/[page]/page.tsx b/app/admin/[character]/[page]/page.tsx index adf3dc6..14d9902 100644 --- a/app/admin/[character]/[page]/page.tsx +++ b/app/admin/[character]/[page]/page.tsx @@ -133,6 +133,25 @@ export default async function StructuredEditorPage({ damage: member.damage, })), })), + teamGroups: content.teamGroups.map((group) => ({ + id: group.id, + note: group.note, + teams: group.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.filter((section) => !extraSection || section.id === extraSection.id).map((section) => ({ id: section.id, title: section.title, diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx index 1fd41f9..72d4438 100644 --- a/components/admin/guide-section-editor.tsx +++ b/components/admin/guide-section-editor.tsx @@ -22,7 +22,7 @@ import { UserRoundIcon, XIcon, } from "lucide-react"; -import { useEffect, useMemo, useState, type ReactNode } from "react"; +import { useEffect, useMemo, useState, type ReactNode, type SetStateAction } from "react"; import { toast } from "sonner"; import { closestCenter, @@ -232,6 +232,10 @@ type ArtifactProfile = { circlet: string[]; substats: string[]; targets: Array<{ stat: string; value: string }>; + targetConditions: Array<{ + note: string | null; + rows: Array<{ stat: string; value: string }>; + }>; talents: TalentPriority[]; note: string | null; } | null; @@ -272,6 +276,11 @@ type TeamValue = { damage: string; }>; }; +type TeamGroupValue = { + id: string; + note: string | null; + teams: TeamValue[]; +}; let clientIdCounter = 0; const DEFAULT_EXTRA_BACKGROUND_COLOR = "#24232C"; @@ -333,6 +342,7 @@ export interface GuideSectionEditorProps { artifactProfile: ArtifactProfile; constellationGroups: ConstellationGroupValue[]; teams: TeamValue[]; + teamGroups: TeamGroupValue[]; extras: Array<{ id: string; title: string; @@ -1345,18 +1355,25 @@ function ArtifactEditor({ const [goblet, setGoblet] = useState(profile?.goblet ?? []); const [circlet, setCirclet] = useState(profile?.circlet ?? []); const [substats, setSubstats] = useState(profile?.substats ?? []); - const [targets, setTargets] = useState(() => { - const existing = new Map( - (profile?.targets ?? []).flatMap((target) => { + const [targetGroups, setTargetGroups] = useState(() => { + const source = profile?.targetConditions.length + ? profile.targetConditions + : [{ note: null, rows: profile?.targets ?? [] }]; + return source.map((group, index) => { + const existing = new Map(group.rows.flatMap((target) => { const stat = normalizeTargetStat(target.stat); return stat ? [[stat, target.value] as const] : []; - }), - ); - return targetStatDefaults.map((target) => ({ - stat: target.stat, - value: existing.get(target.stat) ?? target.value, - })); + })); + return { + clientId: `target-condition-${index}`, + note: group.note ?? "", + rows: targetStatDefaults.map((target) => ({ stat: target.stat, value: existing.get(target.stat) ?? target.value })), + }; + }); }); + const [activeTargetGroupIndex, setActiveTargetGroupIndex] = useState(0); + 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(() => { const source: TalentPriority[] = profile?.talents.length ? profile.talents @@ -1392,9 +1409,10 @@ function ArtifactEditor({ goblet, circlet, substats, - targets: targets - .filter((target) => target.value.trim()) - .map((target) => ({ stat: target.stat, value: target.value.trim() })), + targetGroups: targetGroups.map((group) => ({ + note: group.note.trim() || null, + rows: group.rows.filter((target) => target.value.trim()).map((target) => ({ stat: target.stat, value: target.value.trim() })), + })), talents, note: note || null, }; @@ -1410,7 +1428,8 @@ function ArtifactEditor({ goblet, circlet, substats, - targets, + targetGroups, + activeTargetGroupIndex, talents, note, }; @@ -1431,7 +1450,7 @@ function ArtifactEditor({ "goblet", "circlet", "substats", - "targets", + "targetGroups", "talents", ], ["note"], @@ -1442,7 +1461,8 @@ function ArtifactEditor({ setGoblet(draft.goblet); setCirclet(draft.circlet); setSubstats(draft.substats); - setTargets(draft.targets); + setTargetGroups(draft.targetGroups); + setActiveTargetGroupIndex(draft.activeTargetGroupIndex ?? 0); setTalents(draft.talents); setNote(draft.note); }, @@ -1794,6 +1814,27 @@ function ArtifactEditor({ />
Target Stats +
+ {targetGroups.map((group, index) => ( + + ))} + + +
+ + Condition note (optional) +