feat : update admin ui
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import Image from "next/image";
|
||||
import { CheckIcon, XIcon } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { LunarisText } from "@/components/public/lunaris-text";
|
||||
import { parseLunarisText } from "@/lib/catalog/lunaris";
|
||||
|
||||
export type StygianBossDisplay = {
|
||||
name: string;
|
||||
imageUrl: string;
|
||||
hp: number;
|
||||
description: string;
|
||||
mechanics: { name: string; description: string }[];
|
||||
recommendedMechanics: string[];
|
||||
recommendedMechanicTypes: string[];
|
||||
};
|
||||
|
||||
function RecommendedMechanic({
|
||||
mechanic,
|
||||
type,
|
||||
}: {
|
||||
mechanic: string;
|
||||
type: string;
|
||||
}) {
|
||||
const parts = mechanic.split(/(\{SPRITE_PRESET#\d+\})/gu);
|
||||
return (
|
||||
<span className="inline-flex flex-wrap items-center gap-1">
|
||||
{parts.map((part, index) => {
|
||||
const preset = /^\{SPRITE_PRESET#(\d+)\}$/u.exec(part)?.[1];
|
||||
if (preset)
|
||||
return (
|
||||
<Image
|
||||
key={`${part}-${index}`}
|
||||
src={`https://api.lunaris.moe/data/assets/icons/${preset}.png`}
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
className="size-5 shrink-0 object-contain"
|
||||
/>
|
||||
);
|
||||
return part ? (
|
||||
<LunarisText
|
||||
key={`${part}-${index}`}
|
||||
nodes={parseLunarisText(part)}
|
||||
fallback={part}
|
||||
/>
|
||||
) : null;
|
||||
})}
|
||||
{type === "Disadvantage" ? (
|
||||
<XIcon className="size-4 text-red-500" aria-hidden="true" />
|
||||
) : type === "Advantage" ? (
|
||||
<CheckIcon className="size-4 text-emerald-500" aria-hidden="true" />
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function StygianBossTab({
|
||||
boss,
|
||||
position,
|
||||
priority = false,
|
||||
}: {
|
||||
boss: StygianBossDisplay | undefined;
|
||||
position: number;
|
||||
priority?: boolean;
|
||||
}) {
|
||||
const positionLabel = ["บน", "กลาง", "ล่าง"][position];
|
||||
return (
|
||||
<>
|
||||
{boss ? (
|
||||
<Image
|
||||
src={boss.imageUrl}
|
||||
alt=""
|
||||
width={256}
|
||||
height={256}
|
||||
sizes="256px"
|
||||
className="size-64 shrink-0 object-contain"
|
||||
priority={priority}
|
||||
/>
|
||||
) : (
|
||||
<span>{position + 1}</span>
|
||||
)}
|
||||
<span className="text-base font-semibold">{positionLabel}</span>
|
||||
{boss?.recommendedMechanics.some(Boolean) ? (
|
||||
<span className="mt-2 flex flex-col items-center gap-1 text-xs font-normal whitespace-normal text-muted-foreground">
|
||||
{boss.recommendedMechanics.map((mechanic, index) =>
|
||||
mechanic ? (
|
||||
<RecommendedMechanic
|
||||
key={`${mechanic}-${index}`}
|
||||
mechanic={mechanic}
|
||||
type={
|
||||
boss.recommendedMechanicTypes[index] ??
|
||||
(index === 0
|
||||
? "Advantage"
|
||||
: index === 1
|
||||
? "Disadvantage"
|
||||
: "None")
|
||||
}
|
||||
/>
|
||||
) : null,
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function StygianBossCard({
|
||||
boss,
|
||||
level,
|
||||
children,
|
||||
}: {
|
||||
boss: StygianBossDisplay;
|
||||
level: 105 | 110;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="flex flex-col gap-6">
|
||||
<header className="text-center">
|
||||
<h2 className="font-heading text-lg font-semibold">{boss.name}</h2>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Level {level} · HP {Math.round(boss.hp).toLocaleString("en-US")}
|
||||
</p>
|
||||
</header>
|
||||
<div className="flex min-w-0 flex-col gap-5">
|
||||
<Accordion>
|
||||
<AccordionItem value="overview">
|
||||
<AccordionTrigger>Boss overview</AccordionTrigger>
|
||||
<AccordionContent className="whitespace-pre-line text-muted-foreground">
|
||||
<LunarisText
|
||||
nodes={parseLunarisText(boss.description)}
|
||||
fallback={boss.description}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
{boss.mechanics.map((mechanic, index) => (
|
||||
<AccordionItem
|
||||
key={`${mechanic.name}-${index}`}
|
||||
value={`mechanic-${index}`}
|
||||
>
|
||||
<AccordionTrigger>{mechanic.name}</AccordionTrigger>
|
||||
<AccordionContent className="text-muted-foreground">
|
||||
<LunarisText
|
||||
nodes={parseLunarisText(mechanic.description)}
|
||||
fallback={mechanic.description}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
{children}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user