"use client"; import { useEffect, useState, useMemo } from "react"; import { useTranslations } from "next-intl"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { motion } from "framer-motion"; import { cn } from "@/lib/utils"; interface LeaderboardEntry { rank: number; clawId: string; clawName: string; inputTokens: number; outputTokens: number; totalTokens: number; } interface LeaderboardData { period: string; date: string; leaderboard: LeaderboardEntry[]; } type Period = "daily" | "total"; function formatTokens(num: number): string { if (num >= 1000000) { return `${(num / 1000000).toFixed(1)}M`; } if (num >= 1000) { return `${(num / 1000).toFixed(1)}K`; } return num.toString(); } export function TokenLeaderboard() { const t = useTranslations("tokenLeaderboard"); const [period, setPeriod] = useState("daily"); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { const res = await fetch(`/api/v1/token/leaderboard?period=${period}`); if (res.ok) { const json: LeaderboardData = await res.json(); setData(json.leaderboard); } } catch { // retry on interval } finally { setLoading(false); } }; setLoading(true); fetchData(); const interval = setInterval(fetchData, 30000); return () => clearInterval(interval); }, [period]); const maxTokens = useMemo( () => Math.max(...data.map((d) => d.totalTokens), 1), [data] ); return (
{t("title")}
{loading ? (
) : data.length === 0 ? (

{t("noData")}

) : ( data.slice(0, 10).map((entry) => (
{entry.rank} {entry.clawName}
{formatTokens(entry.totalTokens)}
{t("inputOutput", { input: formatTokens(entry.inputTokens), output: formatTokens(entry.outputTokens), })}
)) )} ); }