From db554228bc902b368d7d9e97d2a4f0d4854aec54 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Tue, 29 Sep 2026 17:13:24 +0700 Subject: [PATCH] feat(admin-images) : add standalone export generators --- app/admin/generate/image/page.tsx | 56 --- app/admin/image/constellation/page.tsx | 22 ++ app/admin/image/layout.tsx | 23 ++ app/admin/image/overview/page.tsx | 47 +++ app/admin/image/page.tsx | 5 + app/admin/image/team/page.tsx | 26 ++ app/admin/image/weapon/page.tsx | 25 ++ components/admin/admin-header.tsx | 4 +- .../admin/constellation-image-generator.tsx | 225 ++++++++++++ components/admin/image-generator-fields.tsx | 93 +++++ components/admin/image-generator-nav.tsx | 49 +++ components/admin/team-image-generator.tsx | 77 ++++ components/admin/use-image-generator-draft.ts | 56 +++ components/admin/weapon-image-generator.tsx | 343 ++++++++++++++++++ components/public/constellation-image.tsx | 17 +- components/public/team-search.tsx | 29 +- components/public/weapon-image-export.tsx | 20 +- lib/guides/image-export.test.tsx | 44 +++ lib/image-generator-draft.test.ts | 26 ++ lib/image-generator-draft.ts | 21 ++ lib/image-generator-empty.test.tsx | 35 ++ 21 files changed, 1179 insertions(+), 64 deletions(-) delete mode 100644 app/admin/generate/image/page.tsx create mode 100644 app/admin/image/constellation/page.tsx create mode 100644 app/admin/image/layout.tsx create mode 100644 app/admin/image/overview/page.tsx create mode 100644 app/admin/image/page.tsx create mode 100644 app/admin/image/team/page.tsx create mode 100644 app/admin/image/weapon/page.tsx create mode 100644 components/admin/constellation-image-generator.tsx create mode 100644 components/admin/image-generator-fields.tsx create mode 100644 components/admin/image-generator-nav.tsx create mode 100644 components/admin/team-image-generator.tsx create mode 100644 components/admin/use-image-generator-draft.ts create mode 100644 components/admin/weapon-image-generator.tsx create mode 100644 lib/image-generator-draft.test.ts create mode 100644 lib/image-generator-draft.ts create mode 100644 lib/image-generator-empty.test.tsx diff --git a/app/admin/generate/image/page.tsx b/app/admin/generate/image/page.tsx deleted file mode 100644 index cfabbcd..0000000 --- a/app/admin/generate/image/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { redirect } from "next/navigation"; -import { connection } from "next/server"; - -import { AdminHeader } from "@/components/admin/admin-header"; -import { OverviewImageGenerator } from "@/components/admin/overview-image-generator"; -import { getAdminSession } from "@/lib/auth/server"; -import { getActiveCatalog } from "@/lib/guides/queries"; -import { elementOrder } from "@/lib/overview-image"; - -export default async function OverviewImagePage() { - await connection(); - if (!(await getAdminSession())) redirect("/admin/login"); - - const catalog = await getActiveCatalog(); - if (!catalog) redirect("/admin?sync=required"); - - const elementIconUrls = Object.fromEntries( - elementOrder.map((element) => [ - element, - catalog.characters.find( - (character) => - character.element === element && character.elementIconUrl, - )?.elementIconUrl ?? null, - ]), - ); - - return ( -
- -
-
-

Image generator

-

- สร้างภาพ Overview -

-

- สร้างและดาวน์โหลดภาพ PNG ขนาด 3000×3000 โดยไม่เปลี่ยนภาพใน Guide -

-
- ({ - key: character.key, - name: character.name, - aliases: character.aliases, - element: character.element, - rarity: character.rarity, - weaponType: character.weaponType, - imageUrl: character.imageUrl, - nameCardImageUrl: character.nameCardImageUrl, - }))} - elementIconUrls={elementIconUrls} - /> -
-
- ); -} diff --git a/app/admin/image/constellation/page.tsx b/app/admin/image/constellation/page.tsx new file mode 100644 index 0000000..bde5a65 --- /dev/null +++ b/app/admin/image/constellation/page.tsx @@ -0,0 +1,22 @@ +import { redirect } from "next/navigation"; + +import { ConstellationImageGenerator } from "@/components/admin/constellation-image-generator"; +import { getActiveCatalog } from "@/lib/guides/queries"; + +export default async function ConstellationImagePage() { + const catalog = await getActiveCatalog(); + if (!catalog) redirect("/admin?sync=required"); + + return ( +
+
+

Image generator

+

สร้างภาพ Constellation

+

+ สร้างกราฟดาเมจ Constellation จาก Catalog โดยไม่บันทึกข้อมูลลง Guide +

+
+ +
+ ); +} diff --git a/app/admin/image/layout.tsx b/app/admin/image/layout.tsx new file mode 100644 index 0000000..966ac90 --- /dev/null +++ b/app/admin/image/layout.tsx @@ -0,0 +1,23 @@ +import { redirect } from "next/navigation"; +import { connection } from "next/server"; + +import { AdminHeader } from "@/components/admin/admin-header"; +import { ImageGeneratorNav } from "@/components/admin/image-generator-nav"; +import { getAdminSession } from "@/lib/auth/server"; + +export default async function ImageGeneratorLayout({ + children, +}: LayoutProps<"/admin/image">) { + await connection(); + if (!(await getAdminSession())) redirect("/admin/login"); + + return ( +
+ +
+ +
{children}
+
+
+ ); +} diff --git a/app/admin/image/overview/page.tsx b/app/admin/image/overview/page.tsx new file mode 100644 index 0000000..272e210 --- /dev/null +++ b/app/admin/image/overview/page.tsx @@ -0,0 +1,47 @@ +import { redirect } from "next/navigation"; + +import { OverviewImageGenerator } from "@/components/admin/overview-image-generator"; +import { getActiveCatalog } from "@/lib/guides/queries"; +import { elementOrder } from "@/lib/overview-image"; + +export default async function OverviewImagePage() { + const catalog = await getActiveCatalog(); + if (!catalog) redirect("/admin?sync=required"); + + const elementIconUrls = Object.fromEntries( + elementOrder.map((element) => [ + element, + catalog.characters.find( + (character) => + character.element === element && character.elementIconUrl, + )?.elementIconUrl ?? null, + ]), + ); + + return ( +
+
+

Image generator

+

+ สร้างภาพ Overview +

+

+ สร้างและดาวน์โหลดภาพ PNG ขนาด 3000×3000 โดยไม่เปลี่ยนภาพใน Guide +

+
+ ({ + key: character.key, + name: character.name, + aliases: character.aliases, + element: character.element, + rarity: character.rarity, + weaponType: character.weaponType, + imageUrl: character.imageUrl, + nameCardImageUrl: character.nameCardImageUrl, + }))} + elementIconUrls={elementIconUrls} + /> +
+ ); +} diff --git a/app/admin/image/page.tsx b/app/admin/image/page.tsx new file mode 100644 index 0000000..2f9f25c --- /dev/null +++ b/app/admin/image/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function ImageGeneratorPage() { + redirect("/admin/image/overview"); +} diff --git a/app/admin/image/team/page.tsx b/app/admin/image/team/page.tsx new file mode 100644 index 0000000..634ff8d --- /dev/null +++ b/app/admin/image/team/page.tsx @@ -0,0 +1,26 @@ +import { redirect } from "next/navigation"; + +import { TeamImageGenerator } from "@/components/admin/team-image-generator"; +import { getActiveCatalog } from "@/lib/guides/queries"; + +export default async function TeamImagePage() { + const catalog = await getActiveCatalog(); + if (!catalog) redirect("/admin?sync=required"); + + return ( +
+
+

Image generator

+

สร้างภาพ Team Calc

+

+ สร้างภาพ Team แบบเดียวกับหน้า Guide โดยไม่บันทึกข้อมูลลง Guide +

+
+ +
+ ); +} diff --git a/app/admin/image/weapon/page.tsx b/app/admin/image/weapon/page.tsx new file mode 100644 index 0000000..da5a2b3 --- /dev/null +++ b/app/admin/image/weapon/page.tsx @@ -0,0 +1,25 @@ +import { redirect } from "next/navigation"; + +import { WeaponImageGenerator } from "@/components/admin/weapon-image-generator"; +import { getActiveCatalog } from "@/lib/guides/queries"; + +export default async function WeaponImagePage() { + const catalog = await getActiveCatalog(); + if (!catalog) redirect("/admin?sync=required"); + + return ( +
+
+

Image generator

+

สร้างภาพ Weapon

+

+ สร้างภาพเปรียบเทียบอาวุธจาก Catalog โดยไม่บันทึกข้อมูลลง Guide +

+
+ +
+ ); +} diff --git a/components/admin/admin-header.tsx b/components/admin/admin-header.tsx index 88383fa..138fde7 100644 --- a/components/admin/admin-header.tsx +++ b/components/admin/admin-header.tsx @@ -79,11 +79,11 @@ export function AdminHeader() { + + + + {!draft.rows.length ? ( + + + + ยังไม่มี Constellation + สร้างรายการ C1–C6 เพื่อเริ่มกรอกเปอร์เซ็นต์ดาเมจ + + + + + + ) : ( + <> + + + ตัวละคร + setDraft((current) => ({ ...current, characterKey }))} + /> + + + โน้ตท้ายภาพ (ถ้ามี) +