import { Fragment, type ReactNode } from "react"; import Link from "next/link"; import { ArrowLeft, DatabaseZap, RadioTower, Trophy } from "lucide-react"; import { LeaderboardSearch, LeaderboardTabs } from "./client"; import { ThemeToggle } from "@/components/theme-toggle"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { formatInteger, formatVoiceDuration, resolveLeaderboardName, shortenDiscordId, type LeaderboardPage, type VoiceLeaderboardRow, type XpLeaderboardRow, } from "@/lib/leaderboard/ranking"; type LeaderboardBoardProps = | { kind: "xp"; data: LeaderboardPage; searchQuery: string; channelId?: never; } | { kind: "vc"; data: LeaderboardPage; searchQuery: string; channelId?: string; }; function rankVariant(rank: number) { if (rank === 1) return "default"; if (rank === 2) return "secondary"; return "outline"; } function Identity({ displayName, username, userId, }: { displayName: string | null; username: string | null; userId: string; }) { const unresolved = !displayName?.trim() && !username?.trim(); return (
{resolveLeaderboardName({ displayName, username, userId })} {unresolved ? `Discord ID: ${shortenDiscordId(userId)}` : displayName?.trim() && username?.trim() ? `@${username}` : `Discord ID: ${shortenDiscordId(userId)}`}
); } function Podium({ data }: LeaderboardBoardProps) { if (data.podium.length === 0) return null; return (

สามอันดับนำ

{data.podium.map((row) => ( {resolveLeaderboardName(row)} {row.rank === 1 && } #{row.rank} {"level" in row ? `เลเวล ${row.level}` : "เวลารวมในห้องเสียง"} {"totalXp" in row ? `${formatInteger(row.totalXp)} XP` : formatVoiceDuration(row.totalSeconds)} ))}
); } function visiblePages(page: number, totalPages: number) { const pages = new Set([1, totalPages, page - 1, page, page + 1]); return [...pages] .filter((value) => value >= 1 && value <= totalPages) .sort((a, b) => a - b); } function pageHref( path: string, page: number, channelId?: string, searchQuery?: string, ) { const params = new URLSearchParams(); if (channelId) params.set("channel", channelId); if (searchQuery) params.set("q", searchQuery); if (page > 1) params.set("page", String(page)); const query = params.toString(); return query ? `${path}?${query}` : path; } function LeaderboardPagination({ path, page, totalPages, channelId, searchQuery, }: { path: string; page: number; totalPages: number; channelId?: string; searchQuery?: string; }) { if (totalPages <= 1) return null; const pages = visiblePages(page, totalPages); return ( {page > 1 && ( )} {pages.map((value, index) => ( {index > 0 && value - pages[index - 1] > 1 && ( )} {value} ))} {page < totalPages && ( )} ); } export function LeaderboardBoard(props: LeaderboardBoardProps) { const { kind, data, channelId, searchQuery } = props; const firstRow = data.totalRows === 0 ? 0 : (data.page - 1) * data.pageSize + 1; const lastRow = Math.min(data.page * data.pageSize, data.totalRows); return ( <> {kind === "xp" ? "อันดับ XP ทั้งหมด" : "อันดับเวลาในห้องเสียง"} {data.totalRows > 0 ? `แสดง ${firstRow}–${lastRow} จาก ${formatInteger(String(data.totalRows))} คน` : searchQuery ? "ไม่พบสมาชิกที่ตรงกับคำค้นหานี้" : "ยังไม่มีข้อมูลในขอบเขตนี้"} {kind === "xp" ? "ตารางอันดับจาก XP รวมของสมาชิกเลเวล 1 ขึ้นไป" : "ตารางอันดับจากเวลารวมในห้องเสียงและห้องเวทีปัจจุบัน"} อันดับ สมาชิก {kind === "xp" && เลเวล} {kind === "xp" ? "XP รวม" : "เวลารวม"} {data.rows.map((row) => ( #{row.rank} {"level" in row && ( {row.level} )} {"totalXp" in row ? formatInteger(row.totalXp) : formatVoiceDuration(row.totalSeconds)} ))}
); } export function LeaderboardError({ source }: { source: "database" | "discord" }) { const Icon = source === "database" ? DatabaseZap : RadioTower; return ( {source === "database" ? "โหลดข้อมูลอันดับไม่สำเร็จ" : "โหลดรายชื่อห้อง Discord ไม่สำเร็จ"} {source === "database" ? "ฐานข้อมูลอันดับยังไม่พร้อมใช้งาน ลองเปิดหน้านี้ใหม่อีกครั้ง" : "ยังเลือกขอบเขตห้องเสียงไม่ได้ ลองเปิดหน้านี้ใหม่เมื่อ Discord พร้อมใช้งาน"} หน้านี้ไม่มีการดึงข้อมูลซ้ำอัตโนมัติระหว่างที่ระบบขัดข้อง ); } type LeaderboardShellProps = { active: "xp" | "vc"; title: string; description: string; searchQuery: string; children: ReactNode; }; export function LeaderboardShell({ active, title, description, searchQuery, children, }: LeaderboardShellProps) { return (

{title}

{description}
{children}
); }