"use client"; import { useEffect, useState, useCallback } from "react"; import { motion } from "motion/react"; import { Users, Calendar, Hash, Zap, ArrowUpDown, ArrowDown, ArrowUp } from "lucide-react"; import { TimeRangeSelector } from "@/components/TimeRangeSelector"; import { type TimeRange, getTimeRange, buildQuery, formatNumber, formatTokens } from "@/lib/utils"; import { useI18n } from "@/lib/i18n"; interface AggItem { rank: number; name: string; calls: number; prompt_tokens: number; completion_tokens: number; total_tokens: number; } type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens"; export default function AggregationPage() { const { t } = useI18n(); const [range, setRange] = useState("30d"); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [sortKey, setSortKey] = useState("total_tokens"); const [sortAsc, setSortAsc] = useState(false); const fetchData = useCallback(async () => { setLoading(true); const { start, end } = getTimeRange(range); const res = await fetch(buildQuery("/api/aggregation", { start, end })); setData(await res.json()); setLoading(false); }, [range]); useEffect(() => { fetchData(); }, [fetchData]); const sorted = [...data].sort((a, b) => { const diff = (a[sortKey] as number) - (b[sortKey] as number); return sortAsc ? diff : -diff; }); const totals = data.reduce( (acc, d) => ({ calls: acc.calls + d.calls, tokens: acc.tokens + d.total_tokens }), { calls: 0, tokens: 0 } ); function handleSort(key: SortKey) { if (sortKey === key) setSortAsc(!sortAsc); else { setSortKey(key); setSortAsc(false); } } function SortIcon({ col }: { col: SortKey }) { if (sortKey !== col) return ; return sortAsc ? : ; } const sortHeaders: { key: SortKey; label: string }[] = [ { key: "calls", label: t("th.calls") }, { key: "prompt_tokens", label: t("th.input") }, { key: "completion_tokens", label: t("th.output") }, { key: "total_tokens", label: t("th.totalToken") }, ]; return (

{t("agg.title")}

{!loading && data.length > 0 && (
{t("agg.userCount")} {data.length}
{t("agg.totalCalls")} {formatNumber(totals.calls)}
{t("agg.totalToken")} {formatTokens(totals.tokens)}
)} {sortHeaders.map(h => ( ))} {loading ? ( ) : sorted.map((item, i) => { const pct = totals.tokens > 0 ? (item.total_tokens / totals.tokens * 100) : 0; return ( ); })}
# {t("th.user")} handleSort(h.key)}> {h.label} {t("common.share")}
{i + 1} {item.name} {formatNumber(item.calls)} {formatTokens(item.prompt_tokens)} {formatTokens(item.completion_tokens)} {formatTokens(item.total_tokens)}
{pct.toFixed(1)}%
); }