import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { ContentCard, EmptyState, RankBadge } from "@/components/public/ui"; import { avatarImageUrl } from "@/lib/format"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; export const revalidate = 300; export const metadata = { title: "Leaderboard" }; // Currency type ids (see prisma/schema.prisma UsersCurrency): // Credits = -1 (lives on users.credits), Duckets = 0, Diamonds = 5. const TABS = [ { key: "credits", label: "Credits", coin: "credits" }, { key: "diamonds", label: "Diamonds", coin: "diamonds" }, { key: "duckets", label: "Duckets", coin: "duckets" }, { key: "achievement", label: "Achievement", coin: "" }, { key: "respects", label: "Respects", coin: "" }, { key: "onlinetime", label: "Online Time", coin: "" }, ] as const; type TabKey = (typeof TABS)[number]["key"]; const CURRENCY_TYPE: Record<"diamonds" | "duckets", number> = { diamonds: 5, duckets: 0, }; function formatValue(key: TabKey, value: number): string { if (key === "onlinetime") { const hours = Math.floor(value / 3600); const days = Math.floor(hours / 24); if (days > 0) return `${days}d ${hours % 24}h`; return `${hours}h`; } return value.toLocaleString(); } type Row = { username: string; look: string; value: number }; async function loadCreditsRows(): Promise { try { const users = await prisma.user.findMany({ orderBy: { credits: "desc" }, take: 20, select: { username: true, look: true, credits: true }, }); return users.map((u) => ({ username: u.username, look: u.look, value: u.credits, })); } catch { return []; } } async function loadCurrencyRows(type: number): Promise { try { const top = await prisma.usersCurrency.findMany({ where: { type }, orderBy: { amount: "desc" }, take: 20, select: { userId: true, amount: true }, }); if (top.length === 0) return []; const users = await prisma.user.findMany({ where: { id: { in: top.map((t) => t.userId) } }, select: { id: true, username: true, look: true }, }); const byId = new Map(users.map((u) => [u.id, u])); return top .map((t) => { const u = byId.get(t.userId); if (!u) return null; return { username: u.username, look: u.look, value: t.amount }; }) .filter((r): r is Row => r !== null); } catch { return []; } } async function loadSettingsRows( field: "achievementScore" | "respectsReceived" | "onlineTime" | "hofPoints", ): Promise { try { const top = await prisma.usersSettings.findMany({ orderBy: { [field]: "desc" }, take: 20, select: { userId: true, [field]: true }, }); if (top.length === 0) return []; const users = await prisma.user.findMany({ where: { id: { in: top.map((t) => t.userId) } }, select: { id: true, username: true, look: true }, }); const byId = new Map(users.map((u) => [u.id, u])); return (top as unknown as Array<{ userId: number; [k: string]: unknown }>) .map((t) => { const u = byId.get(t.userId as number); if (!u) return null; // eslint-disable-next-line security/detect-object-injection -- field is union of known keys return { username: u.username, look: u.look, value: Number(t[field] ?? 0), }; }) .filter((r): r is Row => r !== null); } catch { return []; } } const SETTINGS_FIELD: Record< "achievement" | "respects" | "onlinetime", "achievementScore" | "respectsReceived" | "onlineTime" > = { achievement: "achievementScore", respects: "respectsReceived", onlinetime: "onlineTime", }; export default async function LeaderboardPage({ searchParams, }: { searchParams: Promise<{ type?: string }>; }) { const t = await getTranslations("pages.leaderboard"); const { type } = await searchParams; const active: TabKey = TABS.some((t) => t.key === type) ? (type as TabKey) : "credits"; const activeTab = TABS.find((t) => t.key === active) ?? TABS[0]; const [rows, imagerBase] = await Promise.all([ active === "credits" ? loadCreditsRows() : active === "diamonds" || active === "duckets" ? // eslint-disable-next-line security/detect-object-injection -- active validated as "diamonds"|"duckets" in this branch loadCurrencyRows(CURRENCY_TYPE[active]) : loadSettingsRows( SETTINGS_FIELD[active as keyof typeof SETTINGS_FIELD], ), siteSettings.get( "habbo_imaging_url", "https://www.habbo.com/habbo-imaging/avatarimage", ), ]); const valueOnly = active === "achievement" || active === "respects" || active === "onlinetime"; return (
{TABS.map((t2) => ( {t(`tab_${t2.key}`)} ))}
{rows.length === 0 ? ( {t("emptyState")} ) : ( {rows.map((row, i) => { const avatar = avatarImageUrl(imagerBase ?? "", row.look, { size: "s", headOnly: true, }); return ( ); })}
# {t("colAvatar")} {t("colPlayer")} {t(`tab_${active}`)}
{/* eslint-disable-next-line @next/next/no-img-element */} {`${row.username} {row.username} {valueOnly ? ( {formatValue(active, row.value)} ) : ( {row.value.toLocaleString()} )}
)}
); }