"use client"; import { useEffect, useState, useCallback, startTransition } from "react"; import { useParams } from "next/navigation"; import { motion } from "motion/react"; import { ArrowLeft, Hash, Zap, MessageSquare } from "lucide-react"; import Link from "next/link"; import { StatsCard } from "@/components/StatsCard"; import { TimeRangeSelector } from "@/components/TimeRangeSelector"; import { TrendChart } from "@/components/charts/TrendChart"; import { buildQuery, formatNumber, formatTokens } from "@/lib/utils"; import { useTimeRange } from "@/lib/time-range-context"; import { useI18n } from "@/lib/i18n"; interface DetailData { calls: number; prompt_tokens: number; completion_tokens: number; total_tokens: number; quota: number; models?: { name: string; calls: number; total_tokens: number; quota: number }[]; users?: { name: string; calls: number; total_tokens: number; quota: number }[]; channel_name?: string; } export default function DetailPage() { const { t } = useI18n(); const params = useParams(); const segments = Array.isArray(params.slug) ? params.slug : []; const type = segments[0] || ""; const id = segments[1] || ""; const decodedId = decodeURIComponent(id); const { getEffectiveRange } = useTimeRange(); const [data, setData] = useState(null); const [trends, setTrends] = useState<{ date: string; calls: number; total_tokens: number; prompt_tokens: number; completion_tokens: number }[]>([]); const [loading, setLoading] = useState(true); const fetchData = useCallback(async () => { startTransition(() => setLoading(true)); const { start, end } = getEffectiveRange(); const tp = { start, end }; const [detail, tr] = await Promise.all([ fetch(buildQuery(`/api/detail/${type}/${encodeURIComponent(decodedId)}`, tp)).then(r => r.json()), fetch(buildQuery("/api/trends", { ...tp, granularity: "day", ...(type === "user" ? { username: decodedId } : {}), ...(type === "model" ? { model: decodedId } : {}), ...(type === "channel" ? { channel_id: decodedId } : {}), })).then(r => r.json()), ]); startTransition(() => { setData(detail); setTrends(tr); setLoading(false); }); }, [type, decodedId, getEffectiveRange]); useEffect(() => { fetchData(); }, [fetchData]); const title = type === "channel" ? (data?.channel_name || decodedId) : decodedId; const typeLabel = { user: t("detail.user"), model: t("detail.model"), channel: t("detail.channel") }[type] || type; const breakdownItems = data?.models || data?.users || []; const breakdownLabel = data?.models ? t("detail.modelDist") : t("detail.userDist"); return (
{t("common.backToRankings")}
{typeLabel}

{title}

{loading ? (
) : data ? ( <>

{t("detail.trend")}

{breakdownItems.length > 0 && (

{breakdownLabel}

{breakdownItems.map((item) => ( ))}
{t("th.name")} {t("th.calls")} {t("th.totalToken")}
{item.name} {formatNumber(item.calls)} {formatTokens(item.total_tokens)}
)} ) : null}
); }