"use client"; import { useState } from "react"; import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts"; import { CheckIcon, MinusIcon } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty"; import { Field, FieldContent, FieldDescription, FieldLabel } from "@/components/ui/field"; import { Switch } from "@/components/ui/switch"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { selectAnalytics, type PublicAnalytics, type RankedAnalyticsItem } from "@/lib/analytics/model"; import { rarityColor } from "@/lib/catalog/rarity"; const formatCount = (count: number) => count.toLocaleString("th-TH"); const chartConfig = { count: { label: "จำนวนครั้ง", color: "var(--muted-foreground)" } }; const rarityLabel = (rarity: number | null) => rarity ? `${rarity} ดาว` : "ไม่ระบุดาว"; function ItemImageTick({ x, y, item }: { x: number; y: number; item: RankedAnalyticsItem }) { const [failedSource, setFailedSource] = useState(null); return {item.name} · {rarityLabel(item.rarity)} {item.imageUrl && failedSource !== item.imageUrl ? setFailedSource(item.imageUrl)} /> : {item.name.slice(0, 1)}} ; } function UsageRanking({ title, description, items }: { title: string; description: string; items: RankedAnalyticsItem[]; }) { const top = items.slice(0, 10).map((item) => ({ ...item, fill: rarityColor(item.rarity) })); const rarities = [...new Set(top.map((item) => item.rarity))].sort((a, b) => (b ?? 0) - (a ?? 0)); return (

{title}

{description}
{items.length ? <>
    {rarities.map((rarity) =>
  • )}
{ const item = top.find((entry) => entry.key === payload.value); return item ? : ; }} /> { const item = payload[0]?.payload; return item ? `${item.name} · ${rarityLabel(item.rarity)}` : ""; }} />} /> formatCount(Number(value))} />
ดูอันดับทั้งหมด ({formatCount(items.length)} รายการ) อันดับ ชื่อ ครั้ง {items.map((item, index) => {formatCount(index + 1)}
{item.imageUrl ? : null} {item.name.slice(0, 1)} {item.name} {rarityLabel(item.rarity)}
{formatCount(item.count)}
)}
: ยังไม่มีข้อมูลยังไม่มีรายการในแหล่งข้อมูลที่เลือก }
); } export function AnalyticsDashboard({ data }: { data: PublicAnalytics }) { const [includeStygian, setIncludeStygian] = useState(true); const [excludedGuideIds, setExcludedGuideIds] = useState>(() => new Set()); const [ignoreRepeatedTeams, setIgnoreRepeatedTeams] = useState(true); const selected = selectAnalytics(data, includeStygian, excludedGuideIds, ignoreRepeatedTeams); const toggleGuide = (id: string) => setExcludedGuideIds((previous) => { const next = new Set(previous); if (next.has(id)) next.delete(id); else next.add(id); return next; }); const summaries = [ { label: "ไกด์ตัวละคร", count: selected.guideCount }, { label: "เวอร์ชัน Stygian", count: selected.stygianScheduleCount }, { label: "ทีมทั้งหมด", count: selected.teamCount }, { label: "ตัวละครที่ใช้ในทีม", count: selected.team.character.length }, { label: "อาวุธที่ใช้ในทีม", count: selected.team.weapon.length }, { label: "เซ็ตอาร์ติแฟกต์ที่ใช้ในทีม", count: selected.team.artifact.length }, ]; return
รวมข้อมูล Stygian รวมทุกเวอร์ชัน ระดับบอส และทีมที่แสดงในไกด์ Stygian นับทีมซ้ำเพียงครั้งเดียว ทีมที่มีตัวละคร อาวุธ เซ็ตอาร์ติแฟกต์ กลุ่มดาว และขัดเกลาเหมือนกัน จะนับครั้งเดียว ไม่ขึ้นกับลำดับสมาชิกหรือไกด์ที่บันทึก
เลือกไกด์ที่นำมานับ ({formatCount(selected.guideCount + selected.stygianScheduleCount)} ไกด์)

กดปุ่มไกด์เพื่อไม่นับข้อมูล กดอีกครั้งเพื่อรวมกลับ

{(["guide", "stygian"] as const).map((source) =>

{source === "guide" ? "ไกด์ตัวละคร" : "เวอร์ชัน Stygian"}

{data.guides.filter((guide) => guide.source === source).map((guide) => { const included = !excludedGuideIds.has(guide.id); return ; })}
)}
{summaries.map(({ label, count }) => {label}

{formatCount(count)}

)}

นับจากข้อมูลที่บันทึกในไกด์สาธารณะ ไม่ใช่สถิติการเล่นของผู้เล่น แต่ละรายการแนะนำถูกนับแยกกัน รวมตัวเลือกซ้ำ {ignoreRepeatedTeams ? " ทีมที่มีตัวละครและบิลด์เหมือนกันจะนับเพียงครั้งเดียวจากไกด์ที่เลือก" : " แต่ละสมาชิกทีมถูกนับแยกกัน รวมทีมที่คัดลอกข้ามไกด์หรือเวอร์ชัน"} อาร์ติแฟกต์แบบผสมจะนับทั้งสองเซ็ต เซ็ตเดียวกันในรายการเดียวจะนับครั้งเดียว ไกด์ที่ซ่อน อยู่ในถังขยะ และส่วนที่ปิดการแสดงผลจะไม่ถูกนับ

ใช้ในทีมบ่อยที่สุด

นับจำนวนครั้งที่ตัวละครและอุปกรณ์ปรากฏในสมาชิกทีม

แนะนำบ่อยที่สุด

นับรายการแนะนำอาวุธและอาร์ติแฟกต์ แยกจากอุปกรณ์ที่ใช้ในทีม{includeStygian ? " รวมอุปกรณ์ของตัวละครแนะนำใน Stygian" : ""}

; }