feat : update
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
|
||||
const destinations = [
|
||||
{ href: "/", label: "หน้ารวม", icon: null },
|
||||
{ href: "/commission", label: "ขอคำนวณทีม", icon: "/icon/commission.webp" },
|
||||
{ href: "/commission", label: "จ้างคำนวณทีม", icon: "/icon/commission.webp" },
|
||||
{ href: "/stygian", label: "ไกด์ Stygian", icon: "/icon/stygian.webp" },
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -55,9 +55,7 @@ import {
|
||||
import {
|
||||
normalizeTalentPriorities,
|
||||
talentRelationSymbol,
|
||||
talentTypeLabel,
|
||||
} from "@/lib/guides/talents";
|
||||
import { comparisonDelta, comparisonReferenceNames } from "@/lib/guides/comparison";
|
||||
import { parseYouTubeUrl } from "@/lib/guides/youtube";
|
||||
import { normalizeExtraRichText } from "@/lib/rich-text";
|
||||
import {
|
||||
@@ -224,7 +222,6 @@ function GuideOverview({
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<GuideBuildSummary guide={guide} baseHref={baseHref} />
|
||||
<div className="grid items-start gap-6 lg:grid-cols-2">
|
||||
<div className="order-1 relative aspect-square w-full overflow-hidden rounded-xl border bg-muted lg:col-start-1 lg:row-start-1">
|
||||
<Image
|
||||
@@ -307,75 +304,6 @@ function GuideOverview({
|
||||
);
|
||||
}
|
||||
|
||||
function GuideBuildSummary({ guide, baseHref }: { guide: PublicGuide; baseHref: string }) {
|
||||
const weaponGroup = guide.weaponGroups[0];
|
||||
const summarizeWeapon = (row: PublicGuide["weapons"][number] | undefined) => row ? {
|
||||
name: row.weapon.name, refinement: row.refinement, note: row.note,
|
||||
conditionName: weaponGroup?.name, conditionNote: weaponGroup?.note,
|
||||
} : null;
|
||||
const artifactGroup = guide.artifactGroups[0];
|
||||
const artifact = artifactGroup?.tiers.flatMap((tier) => tier.rows)[0];
|
||||
// Complete admin previews already contain this data; keep their reads unchanged.
|
||||
const summary = guide.buildSummary ?? {
|
||||
weapon: summarizeWeapon(weaponGroup?.rows[0]),
|
||||
fourStarWeapon: summarizeWeapon(weaponGroup?.rows.find((row) => row.weapon.rarity === 4)),
|
||||
artifact: artifact ? {
|
||||
name: artifact.first.name, secondKey: artifact.secondArtifactKey, secondName: artifact.second?.name,
|
||||
note: artifact.note, conditionName: artifactGroup?.name, conditionNote: artifactGroup?.note,
|
||||
} : null,
|
||||
profile: guide.artifactProfile,
|
||||
};
|
||||
if (!summary || (!summary.weapon && !summary.artifact && !summary.profile)) return null;
|
||||
const profile = summary.profile;
|
||||
const recommended = profile?.recommendedConditions[0] ?? profile;
|
||||
const target = profile?.targetConditions[0];
|
||||
const targets = target?.rows ?? profile?.targets ?? [];
|
||||
const talents = normalizeTalentPriorities(profile?.talents ?? []);
|
||||
return <Card>
|
||||
<CardHeader>
|
||||
<CardTitle>สรุปคำแนะนำในไกด์</CardTitle>
|
||||
<CardDescription>สรุปจากเงื่อนไขแรกของแต่ละหัวข้อ เปิดรายละเอียดเพื่อเลือกเงื่อนไขที่ตรงกับทีมของคุณ</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-5 sm:grid-cols-2">
|
||||
{summary.weapon && <div className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold">อาวุธ</h2>
|
||||
<p>{summary.weapon.name} R{summary.weapon.refinement}</p>
|
||||
{summary.fourStarWeapon && <p className="text-sm text-muted-foreground">ตัวเลือก 4 ดาวในไกด์: {summary.fourStarWeapon.name} R{summary.fourStarWeapon.refinement}</p>}
|
||||
{summary.weapon.conditionName && <Badge variant="outline">{summary.weapon.conditionName}</Badge>}
|
||||
{summary.weapon.conditionNote && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{summary.weapon.conditionNote}</p>}
|
||||
{summary.weapon.note && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{summary.weapon.note}</p>}
|
||||
<Link href={`${baseHref}/weapon`} prefetch={false} className="text-sm text-primary underline underline-offset-4">เปรียบเทียบอาวุธทั้งหมด</Link>
|
||||
</div>}
|
||||
{summary.artifact && <div className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold">อาร์ติแฟกต์</h2>
|
||||
<p>{summary.artifact.secondKey ? `2 ${summary.artifact.name} + 2 ${summary.artifact.secondName ?? "ชุดที่สองตามไกด์"}` : `4 ${summary.artifact.name}`}</p>
|
||||
{summary.artifact.conditionName && <Badge variant="outline">{summary.artifact.conditionName}</Badge>}
|
||||
{summary.artifact.conditionNote && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{summary.artifact.conditionNote}</p>}
|
||||
{summary.artifact.note && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{summary.artifact.note}</p>}
|
||||
</div>}
|
||||
{recommended && [recommended.sands, recommended.goblet, recommended.circlet].some((values) => values.length) && <div className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold">ออฟหลัก</h2>
|
||||
{profile?.recommendedConditions[0]?.name && <Badge variant="outline">{profile.recommendedConditions[0].name}</Badge>}
|
||||
{profile?.recommendedConditions[0]?.note && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{profile.recommendedConditions[0].note}</p>}
|
||||
<dl className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1">
|
||||
{([['นาฬิกา', recommended.sands], ['ถ้วย', recommended.goblet], ['หัว', recommended.circlet]] as const).map(([label, values]) =>
|
||||
values.length ? <div key={label} className="contents"><dt className="text-muted-foreground">{label}</dt><dd>{values.map((value) => statLabel(value)).join(" / ")}</dd></div> : null)}
|
||||
</dl>
|
||||
</div>}
|
||||
{(targets.length > 0 || talents.length > 0) && <div className="flex flex-col gap-2">
|
||||
{targets.length > 0 && <>
|
||||
<h2 className="font-semibold">ค่าสถานะเป้าหมาย</h2>
|
||||
{target?.name && <Badge variant="outline">{target.name}</Badge>}
|
||||
{target?.note && <p className="whitespace-pre-wrap text-sm text-muted-foreground">{target.note}</p>}
|
||||
<dl className="grid grid-cols-[1fr_auto] gap-x-3 gap-y-1">{targets.map((row, index) => <div key={index} className="contents"><dt className="text-muted-foreground">{statLabel(row.stat)}</dt><dd>{row.value}</dd></div>)}</dl>
|
||||
</>}
|
||||
{talents.length > 0 && <><h2 className="font-semibold">ลำดับพรสวรรค์</h2><p>{talents.map((talent, index) => <span key={talent.type}>{talentTypeLabel(talent.type)}{talent.level ? ` Lv.${talent.level}` : ""}{index < talents.length - 1 ? ` ${talentRelationSymbol(talent.relationToNext)} ` : ""}</span>)}</p></>}
|
||||
<Link href={`${baseHref}/artifact`} prefetch={false} className="text-sm text-primary underline underline-offset-4">ดูค่าสถานะและเงื่อนไขทั้งหมด</Link>
|
||||
</div>}
|
||||
</CardContent>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
function GuideNavigation({
|
||||
guide,
|
||||
sections,
|
||||
@@ -501,9 +429,6 @@ function WeaponSectionContent({
|
||||
) : null}
|
||||
<header className="relative z-10 flex items-center justify-center text-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<p className={cn("text-sm font-medium text-primary", exporting && "drop-shadow-[0_3px_2px_rgba(0,0,0,0.9)]")}>
|
||||
อาวุธที่แนะนำ
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<Image
|
||||
src="/icon/nav/Weapon.png"
|
||||
@@ -518,7 +443,7 @@ function WeaponSectionContent({
|
||||
)}
|
||||
/>
|
||||
<h1 className={cn("whitespace-nowrap font-heading text-4xl font-semibold", exporting && "drop-shadow-[0_3px_2px_rgba(0,0,0,0.9)]")}>
|
||||
{exportLabel(exporting, "อาวุธ", "Weapons")}
|
||||
อาวุธ
|
||||
</h1>
|
||||
</div>
|
||||
{note ? (
|
||||
@@ -551,41 +476,11 @@ function WeaponSectionContent({
|
||||
) : null}
|
||||
</div>
|
||||
{group.note ? <p className="whitespace-pre-wrap text-muted-foreground">{group.note}</p> : null}
|
||||
{!exporting && <Alert>
|
||||
<AlertTitle>เปอร์เซ็นต์นี้หมายถึงอะไร?</AlertTitle>
|
||||
<AlertDescription>
|
||||
<p>100% คือค่าฐานของเงื่อนไขนี้ เช่น 109% คือดาเมจมากกว่าค่าฐาน 9% เปรียบเทียบภายในทีมและเงื่อนไขเดียวกัน</p>
|
||||
{([['overallPercent', 'ดาเมจทั้งทีม'], ['personalPercent', 'ดาเมจส่วนตัว']] as const).map(([key, label]) => {
|
||||
if (!group.rows.some((row) => Number(row[key]) > 0)) return null;
|
||||
const references = comparisonReferenceNames(group.rows, key);
|
||||
return <p key={key}>{label} · แถวที่แสดง 100%: {references.length ? references.join(", ") : "ผู้เขียนยังไม่ได้ระบุแถว 100%"}</p>;
|
||||
})}
|
||||
</AlertDescription>
|
||||
</Alert>}
|
||||
<WeaponDisplayMode exporting={exporting} rows={group.rows}>
|
||||
<div
|
||||
className={cn("grid gap-3", useCompactExportGrid && "grid-cols-2")}
|
||||
>
|
||||
{group.rows.map((row, index) => (!exporting ? (
|
||||
<Card key={row.id} size="sm">
|
||||
<CardContent className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3">
|
||||
<a href={lunarisItemUrl("weapon", row.weapon.key)} target="_blank" rel="noreferrer"
|
||||
aria-label={`เปิดข้อมูลอาวุธ ${row.weapon.name} บน Lunaris`}
|
||||
className="flex min-w-0 items-center gap-3 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||||
<span className={cn("relative size-10 shrink-0 rounded-md sm:size-14", rarityGradientClass(row.weapon.rarity))}>
|
||||
<Image src={row.weapon.imageUrl} alt="" fill sizes="(min-width: 640px) 56px, 40px" className="object-contain p-1" />
|
||||
</span>
|
||||
<span className="min-w-0 font-semibold">{index + 1}. {row.weapon.name} R{row.refinement}</span>
|
||||
</a>
|
||||
<div className="flex flex-col gap-2 text-right sm:flex-row sm:gap-5">
|
||||
{([['overallPercent', 'ทั้งทีม'], ['personalPercent', 'ส่วนตัว']] as const).map(([key, label]) => comparisonDelta(row[key]) !== null ?
|
||||
<div key={key}><p className="text-lg font-semibold text-primary sm:text-2xl">{metric(row[key])}</p><p className="text-xs text-muted-foreground">{label}</p><p className="text-xs text-muted-foreground">{comparisonDelta(row[key])}</p></div> : null)}
|
||||
{comparisonDelta(row.overallPercent) === null && comparisonDelta(row.personalPercent) === null && <span className="text-xs text-muted-foreground">ยังไม่มีผลคำนวณ</span>}
|
||||
</div>
|
||||
{row.note && <p className="col-span-2 whitespace-pre-wrap text-sm text-muted-foreground">{row.note}</p>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
{group.rows.map((row, index) => (
|
||||
<Card key={row.id} size="sm" className="bg-white py-0">
|
||||
<CardContent
|
||||
className={cn(
|
||||
@@ -719,7 +614,7 @@ function WeaponSectionContent({
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)))}
|
||||
))}
|
||||
</div>
|
||||
</WeaponDisplayMode>
|
||||
</div>
|
||||
@@ -834,9 +729,6 @@ function ArtifactSectionContent({
|
||||
) : null}
|
||||
<header className={cn("relative z-10 flex items-center justify-center text-center", exporting && kanitSemiBold.className)}>
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<p className={cn("text-sm font-medium text-primary", exporting && "drop-shadow-[0_3px_2px_rgba(0,0,0,0.9)]")}>
|
||||
อาร์ติแฟกต์และสแตทที่แนะนำ
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<Image
|
||||
src="/icon/nav/Artifact.webp"
|
||||
@@ -870,7 +762,7 @@ function ArtifactSectionContent({
|
||||
{recommendedCards}
|
||||
</ConditionSwitcher>
|
||||
)}
|
||||
<div data-artifact-export-section="tierlist" className={exporting ? "order-first" : undefined}>
|
||||
<div data-artifact-export-section="tierlist" className="order-first">
|
||||
{(() => {
|
||||
const tierCards = guide.artifactGroups.map((condition, conditionIndex) => (
|
||||
<div key={condition.id} data-tier-condition={conditionIndex} className="artifact-export-tierlist flex flex-col gap-5">
|
||||
@@ -1275,9 +1167,6 @@ function ConstellationSection({ guide }: { guide: PublicGuide }) {
|
||||
return (
|
||||
<section className="flex flex-col gap-6">
|
||||
<header className="flex flex-col items-center gap-3 text-center">
|
||||
<p className="text-sm font-medium text-primary">
|
||||
กลุ่มดาวที่แนะนำ
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<Image
|
||||
src="/icon/nav/Constellations.webp"
|
||||
@@ -1364,7 +1253,6 @@ function TeamSection({ guide }: { guide: PublicGuide }) {
|
||||
return (
|
||||
<section className="flex flex-col gap-6">
|
||||
<header className="text-center">
|
||||
<p className="text-sm font-medium text-primary">คำนวณความเสียหาย</p>
|
||||
<h1 className="font-heading text-3xl font-semibold sm:text-4xl">คำนวณทีม</h1>
|
||||
</header>
|
||||
<TeamSearch groups={guide.teamGroups} />
|
||||
@@ -1415,7 +1303,6 @@ function ExtraSectionContent({
|
||||
/>
|
||||
) : null}
|
||||
<header className="relative z-10 flex flex-col items-center gap-3 text-center">
|
||||
<p className="text-sm font-medium text-primary">คู่มือเสริม</p>
|
||||
<h1 className="font-heading text-4xl font-semibold">{extra.title}</h1>
|
||||
{action}
|
||||
</header>
|
||||
|
||||
@@ -46,15 +46,15 @@ export function WeaponDisplayMode({
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col gap-3">
|
||||
<ToggleGroup
|
||||
aria-label="รูปแบบการแสดงอาวุธ"
|
||||
aria-label="Weapon display mode"
|
||||
variant="outline"
|
||||
value={[mode]}
|
||||
onValueChange={(values) => {
|
||||
if (values[0]) setMode(values[0]);
|
||||
}}
|
||||
>
|
||||
<ToggleGroupItem value="normal">รายการ</ToggleGroupItem>
|
||||
<ToggleGroupItem value="chart">กราฟ</ToggleGroupItem>
|
||||
<ToggleGroupItem value="normal">List</ToggleGroupItem>
|
||||
<ToggleGroupItem value="chart">Chart</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
{mode === "chart" ? <LazyWeaponChart rows={rows} /> : children}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user