feat : update
CI / Verify (push) Successful in 1m16s
CI / Build immutable images and deploy (push) Successful in 1m58s

This commit is contained in:
2026-09-05 22:36:31 +07:00 Unverified
parent 66238795bf
commit 2de07c4917
12 changed files with 637 additions and 146 deletions
+37 -65
View File
@@ -2,6 +2,7 @@
import dynamic from "next/dynamic";
import Image from "next/image";
import { useRouter } from "next/navigation";
import {
ChartNoAxesCombinedIcon,
ClipboardCopyIcon,
@@ -43,9 +44,10 @@ import {
import { CSS } from "@dnd-kit/utilities";
import {
deleteGuideExtra,
saveArtifactSection,
saveConstellationSection,
saveExtraSectionList,
saveGuideExtra,
saveTeamSection,
saveWeaponSection,
} from "@/app/admin/actions";
@@ -317,6 +319,7 @@ function hasIssuePrefix(
export interface GuideSectionEditorProps {
section: "weapon" | "artifact" | "constellations" | "team" | "extra";
extraSectionId?: string;
guide: Guide;
weapons: CatalogItem[];
artifacts: CatalogItem[];
@@ -340,6 +343,7 @@ export interface GuideSectionEditorProps {
body: string;
percent: string | null;
backgroundColor: string | null;
textColor: string | null;
sheet: ExtraSheet | null;
catalogItems: ExtraCatalogItem[];
highlights: Array<{ text: string; color: string }>;
@@ -3523,7 +3527,9 @@ function ExtraCatalogItemsEditor({
);
}
function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorBase) {
function ExtraEditor({ guide, extraSectionId, initial, characters, weapons, artifacts }: EditorBase) {
const router = useRouter();
const sectionId = extraSectionId ?? initial.extras[0]?.id ?? "";
const extraSensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
@@ -3557,6 +3563,7 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
? null
: Number(row.percent),
backgroundColor: row.backgroundColor,
textColor: row.textColor,
sheet: row.sheet,
catalogItems: row.catalogItems,
highlights: [],
@@ -3575,8 +3582,11 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
valid: validation.success,
initialVersion: guide.version,
persist: (next, expectedVersion) =>
saveExtraSectionList(guide.id, { ...next, expectedVersion }),
storageKey: adminDraftKey(guide.id, "extra"),
saveGuideExtra(guide.id, sectionId, {
section: next.sections[0],
expectedVersion,
}),
storageKey: adminDraftKey(guide.id, `extra:${sectionId}`),
localDraft,
parseLocalDraft: (candidate) =>
parseEditorDraft<typeof localDraft>(candidate, ["sections"]),
@@ -3590,6 +3600,7 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
id: row.id || createClientId("extra-row-record"),
body: normalizeExtraRichText(row.body, row.highlights ?? []),
backgroundColor: row.backgroundColor ?? null,
textColor: row.textColor ?? null,
sheet: row.sheet ?? null,
catalogItems: row.catalogItems ?? [],
highlights: [],
@@ -3600,14 +3611,6 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
setOpenSectionIds(restoredSections.map((section) => section.clientId));
},
});
const handleSectionDragEnd = ({ active, over }: DragEndEvent) => {
if (!over || active.id === over.id) return;
setSections((current) => {
const from = current.findIndex((section) => section.clientId === active.id);
const to = current.findIndex((section) => section.clientId === over.id);
return from === -1 || to === -1 ? current : arrayMove(current, from, to);
});
};
const handleRowDragEnd = (sectionId: string, { active, over }: DragEndEvent) => {
if (!over || active.id === over.id) return;
setSections((current) => current.map((section) => {
@@ -3622,36 +3625,21 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
return (
<Card>
<CardHeader>
<CardTitle>Extra sections</CardTitle>
<CardTitle>{sections[0]?.title ?? "Extra section"}</CardTitle>
<CardDescription>
เพิ่มส่วนเสริมแบบภาพ หัวข้อ ข้อความที่จัดรูปแบบได้ และเปอร์เซ็นต์
ระบบจะบันทึกอัตโนมัติ
</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-5">
<DndContext
sensors={extraSensors}
collisionDetection={closestCenter}
onDragEnd={handleSectionDragEnd}
<Accordion
multiple
value={openSectionIds}
onValueChange={setOpenSectionIds}
className="gap-5"
>
<Accordion
multiple
value={openSectionIds}
onValueChange={setOpenSectionIds}
className="gap-5"
>
<SortableContext
items={sections.map((section) => section.clientId)}
strategy={verticalListSortingStrategy}
>
{sections.map((section, sectionIndex) => (
<SortableExtraItem
key={section.clientId}
id={section.clientId}
label={`ลากเพื่อจัดลำดับ ${section.title || `Extra section ${sectionIndex + 1}`}`}
>
{(sectionDragHandle) => (
<AccordionItem value={section.clientId} className="border-0">
<AccordionItem key={section.clientId} value={section.clientId} className="border-0">
<Card
size="sm"
className={cn(
@@ -3676,18 +3664,24 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
/>
</CardTitle>
<CardAction className="flex items-center gap-1">
{sectionDragHandle}
<DeleteConfirmationDialog
trigger={
<Button type="button" variant="ghost" size="icon-sm" />
}
title={`ลบ ${section.title || `Extra section ${sectionIndex + 1}`}?`}
description="แถว ข้อความ รูปภาพ และเปอร์เซ็นต์ทั้งหมดในส่วนนี้จะถูกลบออกจาก Guide"
onConfirm={() =>
setSections(
sections.filter((_, index) => index !== sectionIndex),
)
}
showSuccessToast={false}
onConfirm={async () => {
const result = await deleteGuideExtra(guide.id, section.id);
if (result.status !== "saved") {
toast.error(result.message);
return;
}
window.localStorage.removeItem(adminDraftKey(guide.id, `extra:${section.id}`));
toast.success("ลบ Extra แล้ว");
router.push(`/admin/${guide.characterKey}`);
router.refresh();
}}
>
<Trash2Icon />
<span className="sr-only">ลบส่วน</span>
@@ -4168,6 +4162,7 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
body: "",
percent: null,
backgroundColor: null,
textColor: null,
sheet: null,
catalogItems: [],
highlights: [],
@@ -4187,33 +4182,10 @@ function ExtraEditor({ guide, initial, characters, weapons, artifacts }: EditorB
</AccordionContent>
</Card>
</AccordionItem>
)}
</SortableExtraItem>
))}
</SortableContext>
</Accordion>
</DndContext>
</Accordion>
</CardContent>
<CardFooter className="flex-wrap justify-between gap-3">
<Button
variant="outline"
onClick={() => {
const clientId = createClientId("extra-section");
setSections([
...sections,
{
id: createClientId("extra-section-record"),
title: "Extra",
rows: [],
clientId,
},
]);
setOpenSectionIds([...openSectionIds, clientId]);
}}
>
<PlusIcon data-icon="inline-start" />
เพิ่ม Extra section
</Button>
<CardFooter className="flex-wrap justify-end gap-3">
<AutosaveStatus
state={autosave.state}
issues={