"use client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { closestCenter, DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent } from "@dnd-kit/core"; import { arrayMove, rectSortingStrategy, SortableContext, sortableKeyboardCoordinates, useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { ArrowUpRightIcon, GripVerticalIcon, PlusIcon, } from "lucide-react"; import { useState, type ComponentProps } from "react"; import { toast } from "sonner"; import { createGuideExtra, reorderGuideExtras, } from "@/app/admin/actions"; import { Button } from "@/components/ui/button"; import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"; import { ClearableInput as Input } from "@/components/admin/ui/clearable-input"; import { Spinner } from "@/components/ui/spinner"; import { cn } from "@/lib/utils"; type ExtraLink = { id: string; slug: string; title: string }; function SortableExtraCard({ extra, index, characterKey, disabled }: { extra: ExtraLink; index: number; characterKey: string; disabled: boolean }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: extra.id, disabled }); return ( {extra.title} Extra section {index + 1} ); } export function AddExtraButton({ guideId, characterKey, variant = "ghost", size = "sm", label = "เพิ่ม Extra", }: { guideId: string; characterKey: string; variant?: ComponentProps["variant"]; size?: ComponentProps["size"]; label?: string; }) { const router = useRouter(); const [open, setOpen] = useState(false); const [title, setTitle] = useState(""); const [pending, setPending] = useState(false); const create = async () => { const trimmedTitle = title.trim(); if (!trimmedTitle) return; const browserWindow = window as Window & { __guideHasUnsavedChanges?: boolean }; if ( browserWindow.__guideHasUnsavedChanges && !window.confirm("ข้อมูลยังบันทึกไม่เสร็จ ต้องการสร้าง Extra ใหม่หรือไม่?") ) { return; } setPending(true); const result = await createGuideExtra(guideId, { title: trimmedTitle }); setPending(false); if (result.status === "error") { toast.error(result.message); return; } setOpen(false); setTitle(""); toast.success("สร้าง Extra แล้ว"); router.push(`/admin/${characterKey}/${result.section.slug}`); router.refresh(); }; return ( <> เพิ่ม Extra section ตั้งชื่อส่วนเสริม ระบบจะสร้างและเปิดหน้าแก้ไขให้ทันที
{ event.preventDefault(); void create(); }} > ชื่อ Extra setTitle(event.target.value)} />
); } export function ExtraOverview({ guideId, characterKey, initialVersion, initialExtras, }: { guideId: string; characterKey: string; initialVersion: number; initialExtras: ExtraLink[]; }) { const router = useRouter(); const [extras, setExtras] = useState(initialExtras); const [version, setVersion] = useState(initialVersion); const [pending, setPending] = useState(false); const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })); const reorder = async (event: DragEndEvent) => { if (pending || !event.over || event.active.id === event.over.id) return; const oldIndex = extras.findIndex((extra) => extra.id === event.active.id); const newIndex = extras.findIndex((extra) => extra.id === event.over?.id); if (oldIndex < 0 || newIndex < 0) return; const previous = extras; const next = arrayMove(extras, oldIndex, newIndex); setExtras(next); setPending(true); const result = await reorderGuideExtras(guideId, { expectedVersion: version, sectionIds: next.map((extra) => extra.id), }); setPending(false); if (result.status !== "saved") { setExtras(previous); toast.error(result.message); if (result.status === "conflict") window.location.reload(); return; } if (result.version) setVersion(result.version); router.refresh(); }; return (

Extra sections

แต่ละ Extra มีหน้าแก้ไขและบันทึกแยกจากกัน

{extras.length ? ( void reorder(event)}> extra.id)} strategy={rectSortingStrategy}>
{extras.map((extra, index) => )}
) : ( ยังไม่มี Extra กดเพิ่ม Extra เพื่อสร้างส่วนเสริมแรกของ Guide นี้ )}
); }