"use client"; import Image from "@/components/ui/resilient-image"; import { SparklesIcon } from "lucide-react"; import { useState } from "react"; import { saveConstellationSection } from "@/app/admin/actions"; import { AutosaveStatus } from "@/components/admin/autosave-status"; import { LunarisText } from "@/components/public/lunaris-text"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Field, FieldLabel } from "@/components/ui/field"; import { ClearableInput as Input } from "@/components/admin/ui/clearable-input"; import { Textarea } from "@/components/ui/textarea"; import { Toggle } from "@/components/ui/toggle"; import { useAutosave } from "@/hooks/use-autosave"; import { adminDraftKey } from "@/lib/editor/local-draft"; import { constellationSectionSchema } from "@/lib/guides/schemas"; import { finalizePercentInput, normalizePercentInput } from "@/lib/guides/percent-input"; import { formatGuideValidationIssues } from "@/lib/guides/validation-messages"; import { cn } from "@/lib/utils"; import { type EditorBase, type ConstellationValue, hasIssuePrefix, parseEditorDraft, ConditionControls, reorderConditions, createClientId, ConditionRenameDialog, SELECTED_TOGGLE_CLASS } from "./shared"; export const DEFAULT_CONSTELLATION_CONDITION_NOTE = "C0 = 100%"; export function ConstellationEditor({ guide, initial, catalogConstellations, }: EditorBase) { const [note, setNote] = useState(initial.sectionNote ?? ""); const makeRows = (savedRows: ConstellationValue[] = []) => { const source = catalogConstellations.length ? catalogConstellations : Array.from({ length: 6 }, (_, index) => ({ level: index + 1, name: `C${index + 1}`, description: savedRows.find((row) => row.level === index + 1)?.detail ?? "", descriptionNodes: undefined, iconUrl: null, })); return source.map((constellation) => { const saved = savedRows.find((row) => row.level === constellation.level); return { ...constellation, cumulativePercent: saved?.cumulativePercent ?? "", isGreat: saved?.isGreat ?? false, }; }); }; const [groups, setGroups] = useState(() => { const source = initial.constellationGroups.length ? initial.constellationGroups : [{ id: "constellation-condition-default", name: null, note: DEFAULT_CONSTELLATION_CONDITION_NOTE, iconMediaId: null, iconUrl: null, rows: [] }]; return source.map((group) => ({ clientId: group.id, name: group.name ?? "", note: group.note ?? "", iconMediaId: group.iconMediaId ?? null, iconUrl: group.iconUrl ?? null, 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, iconMediaId: group.iconMediaId, rows: group.rows .filter((row) => row.description.trim() && row.cumulativePercent !== "") .map((row) => ({ level: row.level, detail: row.description, cumulativePercent: Number(row.cumulativePercent), isGreat: row.isGreat, })), })), }; const validation = constellationSectionSchema.safeParse({ ...value, expectedVersion: guide.version, }); const constellationIssue = (...path: (string | number)[]) => !validation.success && hasIssuePrefix(validation.error.issues, ...path); const localDraft = { note, groups }; const autosave = useAutosave({ guideId: guide.id, scope: "constellations", value, valid: validation.success, initialVersion: guide.version, persist: (next, expectedVersion, intent) => saveConstellationSection(guide.id, { ...next, expectedVersion }, intent), storageKey: adminDraftKey(guide.id, "constellations"), localDraft, parseLocalDraft: (candidate) => parseEditorDraft(candidate, ["groups"], ["note"]), restoreLocalDraft: (draft) => { setNote(draft.note); setGroups(draft.groups.map((group) => ({ ...group, name: group.name ?? "" }))); }, }); return ( Constellations ชื่อ รายละเอียด และไอคอนมาจาก Lunaris อัตโนมัติ กรอกเฉพาะเปอร์เซ็นต์เทียบกับ C0 = 100% Header note (optional)