diff --git a/components/admin/delete-confirmation-dialog.tsx b/components/admin/delete-confirmation-dialog.tsx index 7addc12..f1cb8df 100644 --- a/components/admin/delete-confirmation-dialog.tsx +++ b/components/admin/delete-confirmation-dialog.tsx @@ -37,7 +37,9 @@ export function DeleteConfirmationDialog({ }: DeleteConfirmationDialogProps) { return ( - {trigger ? {children} : null} + {trigger ? ( + {children} + ) : null} {title} @@ -45,7 +47,9 @@ export function DeleteConfirmationDialog({ ยกเลิก - {confirmLabel} + + {confirmLabel} + diff --git a/components/admin/editor-inspector.tsx b/components/admin/editor-inspector.tsx index 31752bf..72750eb 100644 --- a/components/admin/editor-inspector.tsx +++ b/components/admin/editor-inspector.tsx @@ -104,32 +104,48 @@ export function EditorInspector({
- Inspector - Live Preview + + Inspector + + + Live Preview +
{selected ? (
- {blockLabels[selected.type] ?? selected.type} -

{selected.id}

+ + {blockLabels[selected.type] ?? selected.type} + +

+ {selected.id} +

ความกว้าง onConfigChange({ ...config, dataSourceId })} + onValueChange={(dataSourceId) => + onConfigChange({ ...config, dataSourceId }) + } > - + + + {Object.values(sources).map((source) => ( - {source.name} · v{source.version} + + {source.name} · v{source.version} + ))} @@ -226,7 +259,11 @@ function DataSourceEditor({ }) { const [rowId, setRowId] = useState(null); const evaluated = useMemo(() => { - try { return evaluateDataSource(source); } catch { return null; } + try { + return evaluateDataSource(source); + } catch { + return null; + } }, [source]); const selectedRow = source.rows.find((row) => row.id === rowId); const formulaFields = source.fields.filter((field) => field.formula); @@ -246,10 +283,13 @@ function DataSourceEditor({
{source.name} - แหล่งข้อมูลทั่วโลก · เวอร์ชัน {source.version} + + แหล่งข้อมูลทั่วโลก · เวอร์ชัน {source.version} +
@@ -257,9 +297,15 @@ function DataSourceEditor({ const cell = previewRow?.cells[field.key]; return ( - {field.label} + + {field.label} + - fx + + + fx + + updateField(field.key, event.target.value)} /> - + + - {source.fields.filter((candidate) => candidate.key !== field.key).map((candidate) => )} + {source.fields + .filter((candidate) => candidate.key !== field.key) + .map((candidate) => ( + + ))} - {cell?.error ? สูตรนี้ยังคำนวณไม่ได้: ตรวจสอบชื่อฟิลด์และตัวหาร : ตัวอย่างผลลัพธ์: {cell?.display ?? "-"}} + {cell?.error ? ( + + สูตรนี้ยังคำนวณไม่ได้: ตรวจสอบชื่อฟิลด์และตัวหาร + + ) : ( + + ตัวอย่างผลลัพธ์:{" "} + + {cell?.display ?? "-"} + + + )} ); })}
- {source.fields.slice(0, 3).map((field) => {field.label})} - {source.rows.slice(0, 10).map((row) => {source.fields.slice(0, 3).map((field) => {row.values[field.key] ?? previewRow?.cells[field.key]?.display ?? "-"})})} + + + {source.fields.slice(0, 3).map((field) => ( + {field.label} + ))} + + + + + {source.rows.slice(0, 10).map((row) => ( + + {source.fields.slice(0, 3).map((field) => ( + + {row.values[field.key] ?? + previewRow?.cells[field.key]?.display ?? + "-"} + + ))} + + + + + ))} +
+ > + + เพิ่มแถว + - { if (!open) setRowId(null); }}> + { + if (!open) setRowId(null); + }} + > แก้ไขแถวข้อมูล - กรอกค่าต้นทาง ระบบจะคำนวณฟิลด์สูตรให้อัตโนมัติ + + กรอกค่าต้นทาง ระบบจะคำนวณฟิลด์สูตรให้อัตโนมัติ + {selectedRow ? ( {source.fields.map((field) => ( - {field.label} + + {field.label} + onChange({ - ...source, - rows: source.rows.map((row) => row.id === selectedRow.id ? { ...row, values: { ...row.values, [field.key]: event.target.value || null } } : row), - })} + onChange={(event) => + onChange({ + ...source, + rows: source.rows.map((row) => + row.id === selectedRow.id + ? { + ...row, + values: { + ...row.values, + [field.key]: event.target.value || null, + }, + } + : row, + ), + }) + } /> ))} ) : null} - + + + diff --git a/components/admin/guide-overview-form.tsx b/components/admin/guide-overview-form.tsx index af11921..4dd142e 100644 --- a/components/admin/guide-overview-form.tsx +++ b/components/admin/guide-overview-form.tsx @@ -6,19 +6,49 @@ import { useState } from "react"; import { saveGuideOverview } from "@/app/admin/actions"; import { AutosaveStatus } from "@/components/admin/autosave-status"; import { MediaUploader } from "@/components/admin/media-uploader"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; -import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Field, + FieldDescription, + FieldError, + FieldGroup, + FieldLabel, +} from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { useAutosave } from "@/hooks/use-autosave"; import { adminDraftKey } from "@/lib/editor/local-draft"; import { parseYouTubeUrl } from "@/lib/guides/youtube"; -export function GuideOverviewForm({ guide }: { guide: { id: string; version: number; overview: string; youtubeUrl: string | null; coverMediaId: string; coverUrl: string; name: string } }) { +export function GuideOverviewForm({ + guide, +}: { + guide: { + id: string; + version: number; + overview: string; + youtubeUrl: string | null; + coverMediaId: string; + coverUrl: string; + name: string; + }; +}) { const [overview, setOverview] = useState(guide.overview); const [youtubeUrl, setYoutubeUrl] = useState(guide.youtubeUrl ?? ""); - const [cover, setCover] = useState({ id: guide.coverMediaId, url: guide.coverUrl }); - const invalidYoutubeUrl = Boolean(youtubeUrl.trim() && !parseYouTubeUrl(youtubeUrl.trim())); + const [cover, setCover] = useState({ + id: guide.coverMediaId, + url: guide.coverUrl, + }); + const invalidYoutubeUrl = Boolean( + youtubeUrl.trim() && !parseYouTubeUrl(youtubeUrl.trim()), + ); const value = { coverMediaId: cover.id, overview, youtubeUrl }; const incompleteIssues = [ !cover.id ? "กรุณาอัปโหลด Guide Image" : null, @@ -30,19 +60,34 @@ export function GuideOverviewForm({ guide }: { guide: { id: string; version: num value, valid: Boolean(cover.id && overview.trim() && !invalidYoutubeUrl), initialVersion: guide.version, - persist: (next, expectedVersion) => saveGuideOverview(guide.id, { ...next, expectedVersion }), + persist: (next, expectedVersion) => + saveGuideOverview(guide.id, { ...next, expectedVersion }), storageKey: adminDraftKey(guide.id, "overview"), localDraft, parseLocalDraft: (candidate) => { if (!candidate || typeof candidate !== "object") return null; const draft = candidate as Record; - if (typeof draft.overview !== "string" || !draft.cover || typeof draft.cover !== "object") return null; + if ( + typeof draft.overview !== "string" || + !draft.cover || + typeof draft.cover !== "object" + ) + return null; const draftCover = draft.cover as Record; - if (typeof draftCover.id !== "string" || typeof draftCover.url !== "string") return null; - if (draft.youtubeUrl !== undefined && typeof draft.youtubeUrl !== "string") return null; + if ( + typeof draftCover.id !== "string" || + typeof draftCover.url !== "string" + ) + return null; + if ( + draft.youtubeUrl !== undefined && + typeof draft.youtubeUrl !== "string" + ) + return null; return { overview: draft.overview, - youtubeUrl: typeof draft.youtubeUrl === "string" ? draft.youtubeUrl : "", + youtubeUrl: + typeof draft.youtubeUrl === "string" ? draft.youtubeUrl : "", cover: { id: draftCover.id, url: draftCover.url }, }; }, @@ -52,5 +97,68 @@ export function GuideOverviewForm({ guide }: { guide: { id: string; version: num setCover(draft.cover); }, }); - return Overviewภาพ ข้อความ และวิดีโอนี้แสดงบนหน้าแรกของ GuideGuide Image
{`Guide
setCover(asset)} />
Overview