import type { SerializedBlock } from "@/lib/blocks"; import type { DataSourceSnapshot, PageSnapshot } from "@/lib/content/types"; export interface EditorDraft { pageId: string; title: string; slug: string; visible: boolean; publicNote: string | null; blocks: SerializedBlock[]; } export function draftFromSnapshot(snapshot: PageSnapshot): EditorDraft { return { pageId: snapshot.page.id, title: snapshot.page.title, slug: snapshot.page.slug, visible: snapshot.page.visible, publicNote: snapshot.page.publicNote, blocks: structuredClone(snapshot.blocks), }; } export function normalizeBlockOrder( blocks: readonly SerializedBlock[], ): SerializedBlock[] { return blocks.map((block, sortOrder) => ({ ...block, sortOrder })); } export function moveBlock( blocks: readonly SerializedBlock[], blockId: string, direction: -1 | 1, ): SerializedBlock[] { const current = blocks.findIndex((block) => block.id === blockId); const target = current + direction; if (current < 0 || target < 0 || target >= blocks.length) return [...blocks]; const next = [...blocks]; [next[current], next[target]] = [next[target], next[current]]; return normalizeBlockOrder(next); } export function moveBlockTo( blocks: readonly SerializedBlock[], blockId: string, targetId: string, ): SerializedBlock[] { const from = blocks.findIndex((block) => block.id === blockId); const target = blocks.findIndex((block) => block.id === targetId); if (from < 0 || target < 0 || from === target) return [...blocks]; const next = [...blocks]; const [moved] = next.splice(from, 1); next.splice(target, 0, moved); return normalizeBlockOrder(next); } export function duplicateBlock( blocks: readonly SerializedBlock[], blockId: string, id = crypto.randomUUID(), ): SerializedBlock[] { const index = blocks.findIndex((block) => block.id === blockId); if (index < 0) return [...blocks]; const clone = structuredClone(blocks[index]); clone.id = id; const next = [...blocks]; next.splice(index + 1, 0, clone); return normalizeBlockOrder(next); } function sourceFields(source: DataSourceSnapshot | undefined) { const text = source?.fields.find((field) => field.type !== "number")?.key ?? "name"; const numbers = source?.fields.filter((field) => field.type === "number" || field.formula) ?? []; return { text, numbers }; } export function createEditorBlock( type: string, source?: DataSourceSnapshot, id = crypto.randomUUID(), ): SerializedBlock | null { const nestedId = () => crypto.randomUUID(); const { text, numbers } = sourceFields(source); const numeric = numbers[0]?.key ?? "value"; const configs: Record | null> = { profile: { title: "หัวข้อโปรไฟล์", summary: "เพิ่มคำแนะนำสั้น ๆ", badges: [] }, "rich-text": { content: [{ type: "paragraph", text: "เริ่มเขียนเนื้อหาที่นี่" }] }, heading: { text: "หัวข้อใหม่", level: 2 }, callout: { title: "ข้อควรรู้", body: "เพิ่มรายละเอียด", tone: "info" }, "stat-cards": { title: "ค่าสำคัญ", items: [{ id: nestedId(), label: "ค่า", value: "-" }], }, "key-value": { items: [{ id: nestedId(), label: "หัวข้อ", value: "รายละเอียด" }], columns: 2, }, ranking: source ? { title: "อันดับ", dataSourceId: source.id, labelField: text, valueField: numeric, higherIsBetter: true, } : null, "team-composition": source ? { title: "ทีมแนะนำ", dataSourceId: source.id, teamField: text, memberFields: [text], } : null, rotation: { title: "ลำดับการเล่น", steps: [{ id: nestedId(), action: "เพิ่มขั้นตอน" }], }, table: source ? { dataSourceId: source.id, columns: source.fields.slice(0, 4).map((field) => ({ field: field.key, label: field.label, align: field.type === "number" ? "end" : "start", })), } : null, "comparison-chart": source && numbers.length > 0 ? { title: "กราฟเปรียบเทียบ", dataSourceId: source.id, categoryField: text, series: [{ field: numeric, label: numbers[0].label, color: "chart-1" }], presentation: "horizontal-bar", normalization: "raw", sort: "authored", showValues: true, showLegend: false, tableFallback: true, } : null, image: null, gallery: null, tabs: { tabs: [ { id: nestedId(), label: "ตัวเลือก 1", content: "เพิ่มรายละเอียด" }, { id: nestedId(), label: "ตัวเลือก 2", content: "เพิ่มรายละเอียด" }, ], }, accordion: { items: [{ id: nestedId(), title: "หัวข้อ", content: "เพิ่มรายละเอียด" }], }, section: { title: "ส่วนใหม่" }, columns: { columns: 2, gap: "normal" }, links: { links: [{ id: nestedId(), label: "ลิงก์", href: "/", description: "เพิ่มคำอธิบาย" }], }, separator: {}, }; const config = configs[type]; if (!config) return null; return { id, type, schemaVersion: 1, config, responsiveLayout: { width: ["ranking", "table", "comparison-chart", "team-composition", "gallery"].includes(type) ? "wide" : "content", mobileSpan: 4, tabletSpan: 8, desktopSpan: 12, hideOnMobile: false, }, sortOrder: 0, }; }