"use client"; import { useEffect, useState } from "react"; import { Trophy } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import type { PresenceParticipant } from "@/hooks/useRealtimePresence"; type LeaderboardTab = "score" | "accuracy" | "messages" | "time"; interface ApiUser { id: number; username?: string | null; displayName?: string | null; payloadDisplayName?: string | null; } interface ApiProfile { id: number; profileTitle?: string | null; user?: ApiUser | number | null; progression?: { minigames?: { radioTraffic?: { totalMessages?: number | null; bestScore?: number | null; bestAccuracy?: number | null; bestSprintTimeMs?: number | null; } | null; } | null; } | null; } interface LeaderboardRow { id: number; name: string; totalMessages: number; bestScore: number; bestAccuracy: number; bestSprintTimeMs: number; isCurrentUser: boolean; isOutsideTopTen?: boolean; } interface TabConfig { readonly label: string; readonly sort: string; readonly matches: (row: LeaderboardRow) => boolean; readonly empty: string; } interface RadioLeaderboardProps { refreshKey?: number; currentUserId?: number | string; activeOperators?: PresenceParticipant[]; } const TAB_ORDER = ["score", "accuracy", "messages", "time"] as const satisfies readonly LeaderboardTab[]; const TAB_CONFIG = { score: { label: "Top Score", sort: "-progression.minigames.radioTraffic.bestScore", matches: (row) => row.bestScore > 0, empty: "No scored transmissions yet. Take the first slot!", }, accuracy: { label: "Top Accuracy", sort: "-progression.minigames.radioTraffic.bestAccuracy", matches: (row) => row.bestAccuracy > 0, empty: "No accuracy records yet. Relay a perfect transmission!", }, messages: { label: "Most Messages", sort: "-progression.minigames.radioTraffic.totalMessages", matches: (row) => row.totalMessages > 0, empty: "No accurate messages yet. Get on frequency!", }, time: { label: "Fastest Sprint", sort: "progression.minigames.radioTraffic.bestSprintTimeMs", matches: (row) => row.bestSprintTimeMs > 0, empty: "No sprint times yet. Complete a 10-message sprint!", }, } satisfies Record; function rankClass(index: number) { if (index === 0) return "font-bold text-amber-400"; if (index === 1) return "font-semibold text-slate-300"; if (index === 2) return "font-semibold text-amber-700"; return "text-muted-foreground"; } export function RadioLeaderboard({ refreshKey = 0, currentUserId, activeOperators = [], }: RadioLeaderboardProps) { const [tab, setTab] = useState("score"); const [rows, setRows] = useState(null); const [failed, setFailed] = useState(false); const tabConfig = TAB_CONFIG[tab]; useEffect(() => { let cancelled = false; setFailed(false); setRows(null); const params = new URLSearchParams({ sort: tabConfig.sort, limit: "100", depth: "1" }); fetch(`/api/profiles?${params.toString()}`) .then((res) => { if (!res.ok) throw new Error("leaderboard fetch failed"); return res.json() as Promise<{ docs?: ApiProfile[] }>; }) .then((data) => { if (cancelled) return; const mapped: LeaderboardRow[] = (data.docs ?? []).map((doc) => { const radioTraffic = doc.progression?.minigames?.radioTraffic ?? {}; const user = typeof doc.user === "object" && doc.user !== null ? doc.user : null; return { id: doc.id, name: user?.displayName || doc.profileTitle || user?.username || `Operator #${doc.id}`, totalMessages: radioTraffic.totalMessages ?? 0, bestScore: radioTraffic.bestScore ?? 0, bestAccuracy: radioTraffic.bestAccuracy ?? 0, bestSprintTimeMs: radioTraffic.bestSprintTimeMs ?? 0, isCurrentUser: user ? String(user.id) === String(currentUserId) : false, }; }); const visible = mapped.filter(tabConfig.matches).slice(0, 10); const current = mapped.find((row) => row.isCurrentUser); if (current && !visible.some((row) => row.id === current.id)) { visible.push({ ...current, isOutsideTopTen: true }); } setRows(visible); }) .catch(() => { if (!cancelled) setFailed(true); }); return () => { cancelled = true; }; }, [currentUserId, refreshKey, tabConfig.matches, tabConfig.sort]); return (

Radio Traffic Leaderboard

Top 10
{TAB_ORDER.map((value) => ( ))}
{activeOperators.length} {activeOperators.length === 1 ? "operator" : "operators"} on frequency {activeOperators.length > 0 && ( ({activeOperators.map((operator) => operator.name).join(", ")}) )}
{failed ? (

Could not load the leaderboard.

) : rows === null ? (
) : rows.length === 0 ? (

{tabConfig.empty}

) : (
{rows.map((row, index) => ( ))}
# Operator Score Accuracy Time Messages
{row.isOutsideTopTen ? "—" : index + 1} {row.name} {row.isCurrentUser && ( (you) )} {row.isOutsideTopTen && ( unranked )} {activeOperators.some((operator) => operator.id === row.id) && ( )} {row.bestScore.toFixed(2)} {(row.bestAccuracy * 100).toFixed(2)}% {row.bestSprintTimeMs > 0 ? `${(row.bestSprintTimeMs / 1000).toFixed(1)}s` : "—"} {row.totalMessages}
)}
); }