From 5700b55d78696bf4469dd7e4ce249d0707f93e61 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Thu, 3 Sep 2026 15:39:44 +0700 Subject: [PATCH] feat(admin) : show Lunaris version and link guide cards --- app/admin/page.tsx | 2 +- components/admin/structured-dashboard.tsx | 10 ++++++---- lib/guides/queries.ts | 10 +++++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/admin/page.tsx b/app/admin/page.tsx index f0eff8a..d347d56 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -23,7 +23,7 @@ export default async function AdminPage() {

Guide Dashboard

จัดการ Guide แบบภาพ-first และบันทึกขึ้นเว็บไซต์ทันที

- + ); diff --git a/components/admin/structured-dashboard.tsx b/components/admin/structured-dashboard.tsx index dab5dd1..67cbb15 100644 --- a/components/admin/structured-dashboard.tsx +++ b/components/admin/structured-dashboard.tsx @@ -17,9 +17,10 @@ import { Spinner } from "@/components/ui/spinner"; type GuideCard = { id: string; characterKey: string; name: string; overview: string; coverUrl: string; version: number; updatedAt: Date; trashedAt: Date | null }; -export function StructuredDashboard({ guides, trash, catalogReady }: { guides: GuideCard[]; trash: GuideCard[]; catalogReady: boolean }) { +export function StructuredDashboard({ guides, trash, catalogVersion }: { guides: GuideCard[]; trash: GuideCard[]; catalogVersion: string | null }) { const router = useRouter(); const [pending, startTransition] = useTransition(); + const catalogReady = Boolean(catalogVersion); function run(action: () => Promise<{ status: string; message?: string }>, success: string) { startTransition(async () => { @@ -32,16 +33,17 @@ export function StructuredDashboard({ guides, trash, catalogReady }: { guides: G return (
-
Lunaris {catalogReady ? "พร้อมใช้งาน" : "ยังไม่ Sync"}
+
Lunaris {catalogVersion ? `v${catalogVersion}` : "ยังไม่ Sync"}
{guides.length ? (
{guides.map((guide) => ( - + + แก้ไข Guide {guide.name}
{`Guide
{guide.name}{guide.overview}v{guide.version}

แก้ไขล่าสุด {new Date(guide.updatedAt).toLocaleString("th-TH")}

- } title={`ย้าย ${guide.name} ไปถังขยะ?`} description="Guide จะหายจากหน้าเว็บไซต์ทันที และสามารถกู้คืนได้ภายหลัง" confirmLabel="ย้ายไปถังขยะ" onConfirm={() => run(() => moveGuideToTrash(guide.id), "ย้ายไปถังขยะแล้ว")}>ย้าย {guide.name} ไปถังขยะ + } title={`ย้าย ${guide.name} ไปถังขยะ?`} description="Guide จะหายจากหน้าเว็บไซต์ทันที และสามารถกู้คืนได้ภายหลัง" confirmLabel="ย้ายไปถังขยะ" onConfirm={() => run(() => moveGuideToTrash(guide.id), "ย้ายไปถังขยะแล้ว")}>ย้าย {guide.name} ไปถังขยะ
))}
) : ยังไม่มี GuideSync Catalog แล้วเลือกตัวละครเพื่อสร้าง Guide แรก} diff --git a/lib/guides/queries.ts b/lib/guides/queries.ts index cfddd62..ee24ad6 100644 --- a/lib/guides/queries.ts +++ b/lib/guides/queries.ts @@ -1,6 +1,7 @@ import "server-only"; import { and, asc, eq, inArray, isNotNull, isNull } from "drizzle-orm"; +import * as z from "zod"; import { getDb } from "@/db"; import { @@ -20,7 +21,9 @@ import { teamMembers, weaponRecommendations, } from "@/db/schema"; -import { publicMediaUrl } from "@/lib/media/storage"; +import { getMediaStorage, publicMediaUrl } from "@/lib/media/storage"; + +const catalogVersionSchema = z.object({ version: z.string() }); function catalogCharacterWithUrls(item: typeof catalogCharacters.$inferSelect) { return { @@ -48,7 +51,12 @@ export async function getActiveCatalog() { db.select().from(catalogArtifacts).orderBy(asc(catalogArtifacts.name)), ]); if (!characters.length) return null; + const storage = await getMediaStorage(); + const { version } = catalogVersionSchema.parse( + await storage.file("catalog/current.json").json(), + ); return { + version, characters: characters.map(catalogCharacterWithUrls), weapons: weapons.map((item) => ({ ...item, imageUrl: publicMediaUrl(item.imageKey) })), artifacts: artifacts.map((item) => ({ ...item, imageUrl: publicMediaUrl(item.imageKey) })),