feat(content): add explicit demo mode
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
import type {
|
||||
PageSnapshot,
|
||||
PublicCharacterCard,
|
||||
} from "@/lib/content/types";
|
||||
|
||||
const ids = {
|
||||
character: "10000000-0000-4000-8000-000000000001",
|
||||
infoPage: "10000000-0000-4000-8000-000000000002",
|
||||
comboPage: "10000000-0000-4000-8000-000000000003",
|
||||
source: "10000000-0000-4000-8000-000000000004",
|
||||
profile: "10000000-0000-4000-8000-000000000005",
|
||||
section: "10000000-0000-4000-8000-000000000006",
|
||||
stats: "10000000-0000-4000-8000-000000000007",
|
||||
ranking: "10000000-0000-4000-8000-000000000008",
|
||||
chart: "10000000-0000-4000-8000-000000000009",
|
||||
rotation: "10000000-0000-4000-8000-000000000010",
|
||||
callout: "10000000-0000-4000-8000-000000000011",
|
||||
richText: "10000000-0000-4000-8000-000000000012",
|
||||
table: "10000000-0000-4000-8000-000000000013",
|
||||
links: "10000000-0000-4000-8000-000000000014",
|
||||
statOne: "10000000-0000-4000-8000-000000000015",
|
||||
statTwo: "10000000-0000-4000-8000-000000000016",
|
||||
statThree: "10000000-0000-4000-8000-000000000017",
|
||||
stepOne: "10000000-0000-4000-8000-000000000018",
|
||||
stepTwo: "10000000-0000-4000-8000-000000000019",
|
||||
stepThree: "10000000-0000-4000-8000-000000000020",
|
||||
rowOne: "10000000-0000-4000-8000-000000000021",
|
||||
rowTwo: "10000000-0000-4000-8000-000000000022",
|
||||
rowThree: "10000000-0000-4000-8000-000000000023",
|
||||
linkOne: "10000000-0000-4000-8000-000000000024",
|
||||
} as const;
|
||||
|
||||
const character = {
|
||||
id: ids.character,
|
||||
slug: "astra",
|
||||
nameTh: "แอสตรา",
|
||||
nameEn: "Astra",
|
||||
description: "นักวางกลยุทธ์ธาตุแสงสำหรับทีมที่เน้นจังหวะสกิล",
|
||||
portraitMediaId: null,
|
||||
element: "แสง",
|
||||
role: "สนับสนุน",
|
||||
rarity: 5,
|
||||
sortOrder: 0,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
const pages = [
|
||||
{
|
||||
id: ids.infoPage,
|
||||
characterId: ids.character,
|
||||
slug: "info",
|
||||
title: "ภาพรวม",
|
||||
navOrder: 0,
|
||||
visible: true,
|
||||
publicNote: "ตัวเลขตัวอย่างนี้ใช้เพื่อสาธิตโครงสร้างเท่านั้น",
|
||||
version: 3,
|
||||
},
|
||||
{
|
||||
id: ids.comboPage,
|
||||
characterId: ids.character,
|
||||
slug: "combo",
|
||||
title: "คอมโบ",
|
||||
navOrder: 1,
|
||||
visible: true,
|
||||
publicNote: null,
|
||||
version: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const dataSource = {
|
||||
id: ids.source,
|
||||
name: "อุปกรณ์ตัวอย่าง",
|
||||
version: 2,
|
||||
fields: [
|
||||
{ key: "name", label: "ชื่อ", type: "text" as const },
|
||||
{ key: "score", label: "คะแนน", type: "number" as const, displayPrecision: 1 },
|
||||
{ key: "base", label: "ค่าฐาน", type: "number" as const },
|
||||
{
|
||||
key: "boosted",
|
||||
label: "เมื่อเสริมพลัง",
|
||||
type: "number" as const,
|
||||
formula: "base * 1.25",
|
||||
displayPrecision: 2,
|
||||
},
|
||||
{ key: "detail", label: "รายละเอียด", type: "text" as const },
|
||||
{ key: "tag", label: "ระดับ", type: "badge" as const },
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: ids.rowOne,
|
||||
values: {
|
||||
name: "ดาบดาวเหนือ",
|
||||
score: "98.5",
|
||||
base: "120",
|
||||
boosted: null,
|
||||
detail: "เหมาะกับรอบการเล่นสั้นและการฟื้นพลังงาน",
|
||||
tag: "แนะนำ",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.rowTwo,
|
||||
values: {
|
||||
name: "คทาสายหมอก",
|
||||
score: "91.25",
|
||||
base: "108",
|
||||
boosted: null,
|
||||
detail: "ตัวเลือกสมดุลที่ใช้ได้กับหลายทีม",
|
||||
tag: "ดีมาก",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.rowThree,
|
||||
values: {
|
||||
name: "หอกฝึกหัด",
|
||||
score: "76",
|
||||
base: "88",
|
||||
boosted: null,
|
||||
detail: "ตัวเลือกเข้าถึงง่ายสำหรับช่วงเริ่มต้น",
|
||||
tag: "เริ่มต้น",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function snapshot(pageSlug: string): PageSnapshot | null {
|
||||
const page = pages.find((candidate) => candidate.slug === pageSlug);
|
||||
if (!page) return null;
|
||||
|
||||
const overviewBlocks: PageSnapshot["blocks"] = [
|
||||
{
|
||||
id: ids.profile,
|
||||
type: "profile",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 0,
|
||||
config: {
|
||||
eyebrow: "คู่มือตัวละคร",
|
||||
title: "แอสตรา — ผู้กำหนดจังหวะสนามรบ",
|
||||
subtitle: "ตัวสนับสนุนที่เพิ่มพลังให้ทีมผ่านการจัดลำดับสกิล",
|
||||
summary:
|
||||
"ตัวอย่างนี้แสดงว่าคู่มือสามารถอ่านง่ายบนมือถือและยังรองรับข้อมูลคำนวณที่ตรวจสอบได้",
|
||||
mediaId: null,
|
||||
badges: [
|
||||
{ label: "ธาตุ", value: "แสง", tone: "element" },
|
||||
{ label: "ตำแหน่ง", value: "สนับสนุน", tone: "neutral" },
|
||||
{ label: "ระดับ", value: "5 ดาว", tone: "rarity" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.richText,
|
||||
type: "rich-text",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 1,
|
||||
config: {
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
text: "วางสกิลสนับสนุนก่อนตัวทำความเสียหายหลัก และเผื่อช่วงเวลาให้เอฟเฟกต์ครอบคลุมท่าไม้ตาย",
|
||||
},
|
||||
{
|
||||
type: "bulleted-list",
|
||||
items: [
|
||||
"ให้ความสำคัญกับความเร็วและการฟื้นพลังงาน",
|
||||
"ปรับลำดับสกิลตามระยะเวลาบัฟของทีม",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.section,
|
||||
type: "section",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 2,
|
||||
config: {
|
||||
title: "สรุปค่าสำคัญ",
|
||||
description: "ค่าที่ควรตรวจสอบก่อนปรับอุปกรณ์",
|
||||
anchor: "key-stats",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.stats,
|
||||
type: "stat-cards",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 3,
|
||||
config: {
|
||||
title: "เป้าหมายเบื้องต้น",
|
||||
items: [
|
||||
{ id: ids.statOne, label: "ความเร็ว", value: "134", suffix: "+" },
|
||||
{ id: ids.statTwo, label: "ฟื้นพลังงาน", value: "120", suffix: "%" },
|
||||
{ id: ids.statThree, label: "รอบบัฟ", value: "3", suffix: " เทิร์น" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.ranking,
|
||||
type: "ranking",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 4,
|
||||
responsiveLayout: { width: "wide" },
|
||||
config: {
|
||||
title: "อันดับอุปกรณ์",
|
||||
description: "แตะหรือชี้ที่ชื่อเพื่อดูเหตุผลประกอบ",
|
||||
dataSourceId: ids.source,
|
||||
labelField: "name",
|
||||
valueField: "score",
|
||||
detailField: "detail",
|
||||
badgeField: "tag",
|
||||
higherIsBetter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.chart,
|
||||
type: "comparison-chart",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 5,
|
||||
responsiveLayout: { width: "wide" },
|
||||
config: {
|
||||
title: "ผลก่อนและหลังเสริมพลัง",
|
||||
description: "ค่าทั้งหมดมาจากแหล่งข้อมูลเวอร์ชันเดียวกัน",
|
||||
dataSourceId: ids.source,
|
||||
categoryField: "name",
|
||||
series: [
|
||||
{ field: "base", label: "ค่าฐาน", color: "chart-1" },
|
||||
{ field: "boosted", label: "เมื่อเสริมพลัง", color: "chart-2" },
|
||||
],
|
||||
presentation: "grouped-bar",
|
||||
normalization: "raw",
|
||||
sort: "authored",
|
||||
showValues: true,
|
||||
showLegend: true,
|
||||
tableFallback: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.callout,
|
||||
type: "callout",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 6,
|
||||
config: {
|
||||
title: "หมายเหตุ",
|
||||
body: "ผลลัพธ์จริงเปลี่ยนตามเพื่อนร่วมทีมและลำดับการออกแอ็กชัน",
|
||||
tone: "tip",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.table,
|
||||
type: "table",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 7,
|
||||
responsiveLayout: { width: "wide" },
|
||||
config: {
|
||||
title: "ข้อมูลคำนวณแบบละเอียด",
|
||||
dataSourceId: ids.source,
|
||||
columns: [
|
||||
{ field: "name", label: "อุปกรณ์", align: "start" },
|
||||
{ field: "base", label: "ค่าฐาน", align: "end" },
|
||||
{ field: "boosted", label: "เสริมพลัง", align: "end" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.links,
|
||||
type: "links",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 8,
|
||||
config: {
|
||||
title: "อ่านต่อ",
|
||||
links: [
|
||||
{
|
||||
id: ids.linkOne,
|
||||
label: "ไปยังหน้าคอมโบ",
|
||||
href: "/astra/combo",
|
||||
description: "ดูลำดับสกิลและจังหวะการเล่น",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const comboBlocks: PageSnapshot["blocks"] = [
|
||||
{
|
||||
id: ids.profile,
|
||||
type: "profile",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 0,
|
||||
config: {
|
||||
eyebrow: "ลำดับการเล่น",
|
||||
title: "คอมโบพื้นฐาน",
|
||||
subtitle: "หนึ่งรอบที่จำง่ายและปรับใช้ได้กับหลายทีม",
|
||||
mediaId: null,
|
||||
badges: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ids.rotation,
|
||||
type: "rotation",
|
||||
schemaVersion: 1,
|
||||
sortOrder: 1,
|
||||
config: {
|
||||
title: "รอบการใช้สกิล",
|
||||
description: "เรียงจากการเตรียมสนามไปจนถึงช่วงทำความเสียหาย",
|
||||
steps: [
|
||||
{ id: ids.stepOne, actor: "ตัวสนับสนุน", action: "เปิดบัฟทีม", duration: "1 เทิร์น" },
|
||||
{ id: ids.stepTwo, actor: "แอสตรา", action: "ใช้สกิลหลัก", detail: "เก็บพลังงานไว้สำหรับท่าไม้ตาย" },
|
||||
{ id: ids.stepThree, actor: "ตัวทำความเสียหาย", action: "ใช้ท่าไม้ตายและโจมตีต่อเนื่อง" },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
character,
|
||||
page,
|
||||
pages,
|
||||
blocks: pageSlug === "info" ? overviewBlocks : comboBlocks,
|
||||
dataSources: { [ids.source]: dataSource },
|
||||
versionVector: { [ids.source]: dataSource.version },
|
||||
};
|
||||
}
|
||||
|
||||
export const demoDirectory: PublicCharacterCard[] = [
|
||||
{
|
||||
id: character.id,
|
||||
slug: character.slug,
|
||||
nameTh: character.nameTh,
|
||||
nameEn: character.nameEn,
|
||||
portraitMediaId: character.portraitMediaId,
|
||||
element: character.element,
|
||||
role: character.role,
|
||||
rarity: character.rarity,
|
||||
firstPageSlug: pages[0].slug,
|
||||
},
|
||||
];
|
||||
|
||||
export function getDemoPage(
|
||||
characterSlug: string,
|
||||
pageSlug?: string,
|
||||
):
|
||||
| { type: "page"; snapshot: PageSnapshot }
|
||||
| { type: "redirect"; location: string }
|
||||
| { type: "not-found" } {
|
||||
if (characterSlug !== character.slug) return { type: "not-found" };
|
||||
if (!pageSlug) return { type: "redirect", location: "/astra/info" };
|
||||
const pageSnapshot = snapshot(pageSlug);
|
||||
return pageSnapshot
|
||||
? { type: "page", snapshot: pageSnapshot }
|
||||
: { type: "not-found" };
|
||||
}
|
||||
|
||||
export function getDemoAdminPage(
|
||||
characterSlug: string,
|
||||
pageSlug: string,
|
||||
): PageSnapshot | null {
|
||||
if (characterSlug !== character.slug) return null;
|
||||
return snapshot(pageSlug);
|
||||
}
|
||||
|
||||
export const demoAdminNavigation = {
|
||||
characters: [{ ...character, pages }],
|
||||
templates: [],
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import "server-only";
|
||||
|
||||
import {
|
||||
demoAdminNavigation,
|
||||
demoDirectory,
|
||||
getDemoAdminPage,
|
||||
getDemoPage,
|
||||
} from "@/lib/content/demo";
|
||||
import {
|
||||
getAdminNavigation,
|
||||
getAdminPage,
|
||||
getPublicPage,
|
||||
listPublicCharacters,
|
||||
} from "@/lib/content/queries";
|
||||
|
||||
function demoMode(): boolean {
|
||||
return process.env.BUZZ_DEMO_MODE === "true";
|
||||
}
|
||||
|
||||
export async function loadPublicDirectory() {
|
||||
return demoMode() ? demoDirectory : listPublicCharacters();
|
||||
}
|
||||
|
||||
export async function loadPublicPage(characterSlug: string, pageSlug?: string) {
|
||||
return demoMode()
|
||||
? getDemoPage(characterSlug, pageSlug)
|
||||
: getPublicPage(characterSlug, pageSlug);
|
||||
}
|
||||
|
||||
export async function loadAdminPage(characterSlug: string, pageSlug: string) {
|
||||
return demoMode()
|
||||
? getDemoAdminPage(characterSlug, pageSlug)
|
||||
: getAdminPage(characterSlug, pageSlug);
|
||||
}
|
||||
|
||||
export async function loadAdminNavigation() {
|
||||
return demoMode() ? demoAdminNavigation : getAdminNavigation();
|
||||
}
|
||||
Reference in New Issue
Block a user