"use client"; import { useEffect, useState, useCallback } from "react"; import Link from "next/link"; import { motion } from "motion/react"; import { Trophy, Users, Cpu, Radio } from "lucide-react"; import { TimeRangeSelector } from "@/components/TimeRangeSelector"; import { type TimeRange, getTimeRange, buildQuery, formatNumber, formatTokens } from "@/lib/utils"; import { useI18n } from "@/lib/i18n"; type Tab = "user" | "model" | "channel"; interface RankItem { rank: number; name: string; id?: number; calls: number; prompt_tokens: number; completion_tokens: number; total_tokens: number; } export default function RankingsPage() { const { t } = useI18n(); const [range, setRange] = useState("30d"); const [tab, setTab] = useState("user"); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const tabConfig: Record = { user: { label: t("rank.user"), icon: Users }, model: { label: t("rank.model"), icon: Cpu }, channel: { label: t("rank.channel"), icon: Radio }, }; const fetchData = useCallback(async () => { setLoading(true); const { start, end } = getTimeRange(range); const res = await fetch(buildQuery("/api/rankings", { start, end, type: tab, limit: 100 })); setData(await res.json()); setLoading(false); }, [range, tab]); useEffect(() => { fetchData(); }, [fetchData]); function detailHref(item: RankItem): string { if (tab === "channel") return `/detail/channel/${item.id}`; if (tab === "model") return `/detail/model/${encodeURIComponent(item.name)}`; return `/detail/user/${encodeURIComponent(item.name)}`; } const headers = [t("th.rank"), t("th.name"), t("th.calls"), t("th.input"), t("th.output"), t("th.totalToken")]; return (

{t("rank.title")}

{(Object.keys(tabConfig) as Tab[]).map((key) => { const Icon = tabConfig[key].icon; return ( ); })}
{headers.map((h, i) => ( ))} {loading ? ( ) : data.map((item, i) => ( ))}
= 2 ? "text-right" : "text-left"}`} style={{ color: "var(--text-muted)" }}>{h}
{item.rank} {item.name} {formatNumber(item.calls)} {formatTokens(item.prompt_tokens)} {formatTokens(item.completion_tokens)} {formatTokens(item.total_tokens)}
); }