import { useState, useEffect, useCallback, useMemo } from "react"; import { useConfigStore } from "@/store/config-store"; import { useTranslation } from "@/lib/i18n"; import { useCurrency } from "@/lib/currency"; import { formatCost, formatNumber, cn, USD_TO_CNY } from "@/lib/utils"; import { Activity, DollarSign, BarChart3, ArrowUp, ArrowDown, Database, DollarSignIcon, RefreshCw, Download, Gauge, Layers3, Clock3, Zap, CircleDollarSign, Cpu, PieChart as PieChartIcon, } from "lucide-react"; import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Legend, BarChart, Bar, PieChart, Pie, Cell, } from "recharts"; // ─── Types ────────────────────────────────────────────── interface CodexUsageStatus { loggedIn: boolean; provider: "openai-codex"; planType?: string; primary?: { windowSeconds: number; usedPercent: number; remainingPercent: number; resetAfterSeconds: number | null; resetAt: number | null }; secondary?: { windowSeconds: number; usedPercent: number; remainingPercent: number; resetAfterSeconds: number | null; resetAt: number | null }; checkedAt: string; error?: string; } function formatRemainingTime(seconds: number | null | undefined): string | null { if (typeof seconds !== "number" || seconds < 0) return null; const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`; } function formatResetAt(unixSeconds: number | null | undefined, lang: string): string | null { if (typeof unixSeconds !== "number" || unixSeconds <= 0) return null; return new Intl.DateTimeFormat(lang, { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false, }).format(new Date(unixSeconds * 1000)); } interface UsageRangeData { totalTokens: number; totalInput: number; totalOutput: number; totalCacheRead: number; totalCacheWrite: number; totalCost: number; totalRequests: number; cacheHitRate: number; dailyBreakdown: { date: string; input: number; output: number; cacheRead: number; cacheWrite: number; cost: number; requests: number; }[]; hourlyBreakdown: { hour: string; input: number; output: number; cacheRead: number; cacheWrite: number; cost: number; requests: number; }[]; requestLog: { timestamp: string; providerId: string; modelId: string; input: number; output: number; cost: number; requests: number; }[]; providerStats: { providerId: string; totalTokens: number; totalInput: number; totalOutput: number; totalCost: number; totalRequests: number; modelCount: number; }[]; modelStats: { modelId: string; providerId: string; totalTokens: number; totalInput: number; totalOutput: number; totalCost: number; totalRequests: number; }[]; notice?: "no-config" | "api-error"; } type SourceKey = "all" | "pi" | "chatgpt"; type RangeKey = "all" | "today" | "7d" | "30d" | "custom"; type TabKey = "log" | "provider" | "model"; type SortDir = "asc" | "desc"; const RANGE_KEYS: RangeKey[] = ["all", "today", "7d", "30d", "custom"]; const COLORS = ["#00d8ff", "#9ef01a", "#ffb84d", "#9f8cff", "#ff5c7a"]; const CHART_LINE_COLORS: Record = { input: "#00d8ff", output: "#9ef01a", cacheRead: "#9f8cff", cacheWrite: "#ffb84d", cost: "#ff5c7a", }; const LOG_PAGE_SIZE = 20; // ─── Helpers ──────────────────────────────────────────── function formatTokensShort(n: number, lang: string = "en"): string { // 中文:亿 / 万 if (lang.startsWith("zh")) { if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(2)}亿`; if (n >= 10_000) return `${(n / 10_000).toFixed(1)}万`; return n.toLocaleString("zh-CN"); } // 日文:億 / 万 if (lang.startsWith("ja")) { if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(2)}億`; if (n >= 10_000) return `${(n / 10_000).toFixed(1)}万`; return n.toLocaleString("ja-JP"); } // 英文:M / K if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return n.toString(); } function formatCostShort(n: number): string { if (n >= 1) return `$${n.toFixed(2)}`; if (n >= 0.01) return `¢${(n * 100).toFixed(1)}`; return `$${n.toFixed(4)}`; } type TrendGranularity = "hour" | "day" | "month"; function formatDateShort(dateStr: string, lang = "en", granularity: TrendGranularity = "day"): string { // Parse as a China-time (UTC+8) calendar date and format in the active locale. const [y, m, dNum = 1] = dateStr.split("-").map(Number); if (!y || !m) return dateStr; const d = new Date(Date.UTC(y, m - 1, dNum)); return d.toLocaleDateString(lang, granularity === "month" ? { year: "numeric", month: "short", timeZone: "Asia/Shanghai" } : { month: "short", day: "numeric", timeZone: "Asia/Shanghai" }); } type DailyUsageRow = UsageRangeData["dailyBreakdown"][number]; function getDataSpanDays(rows: DailyUsageRow[]): number { if (rows.length < 2) return 0; const first = Date.parse(`${rows[0]!.date}T00:00:00Z`); const last = Date.parse(`${rows[rows.length - 1]!.date}T00:00:00Z`); return Number.isFinite(first) && Number.isFinite(last) ? Math.max(0, Math.round((last - first) / 86_400_000)) : 0; } function aggregateByMonth(rows: DailyUsageRow[]): DailyUsageRow[] { const buckets = new Map(); for (const row of rows) { const month = row.date.slice(0, 7); const existing = buckets.get(month); if (existing) { existing.input += row.input; existing.output += row.output; existing.cacheRead += row.cacheRead; existing.cacheWrite += row.cacheWrite; existing.cost += row.cost; existing.requests += row.requests; } else { buckets.set(month, { ...row, date: `${month}-01` }); } } return Array.from(buckets.values()).sort((a, b) => a.date.localeCompare(b.date)); } function cnTodayStr(): string { // "YYYY-MM-DD" in China time (UTC+8), independent of system timezone. return new Intl.DateTimeFormat("en-CA", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", }).format(new Date()); } function sortRows>(rows: T[], key: string, dir: SortDir): T[] { return [...rows].sort((a, b) => { const av = Number(a[key] ?? 0); const bv = Number(b[key] ?? 0); return dir === "desc" ? bv - av : av - bv; }); } function downloadCsv(filename: string, headers: string[], rows: (string | number)[][]) { const esc = (v: string | number) => { const s = String(v); return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; }; const csv = [headers, ...rows].map((r) => r.map(esc).join(",")).join("\n"); const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" }); const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = filename; a.click(); URL.revokeObjectURL(a.href); } // ─── Stat Card ────────────────────────────────────────── function StatCard({ title, value, subtitle, icon, progress, children, className, }: { title: string; value: string; subtitle?: string; icon: React.ReactNode; progress?: number; children?: React.ReactNode; className?: string; }) { return (

{title}

{icon}

{value}

{subtitle &&

{subtitle}

} {progress !== undefined && (
90 ? "#10b981" : "#3b82f6" }} />
)} {children}
); } // ─── Breakdown Row ────────────────────────────────────── function BreakdownRow({ label, value, total, color, lang = "en" }: { label: string; value: number; total: number; color: string; lang?: string }) { const pct = total > 0 ? (value / total) * 100 : 0; return (
{label}
{formatTokensShort(value, lang)} ({pct.toFixed(1)}%)
); } // ─── Sortable Table Header ────────────────────────────── function SortableTh({ label, sortKey, sort, onSort, }: { label: string; sortKey: string; sort: { key: string; dir: SortDir }; onSort: (key: string) => void; }) { const active = sort.key === sortKey; return ( onSort(sortKey)} > {label} {active && (sort.dir === "desc" ? : )} ); } // ─── Analytics Components ─────────────────────────────── function AnalyticsMetric({ label, value, detail, icon, accent = "var(--signal-cyan)", }: { label: string; value: string; detail: string; icon: React.ReactNode; accent?: string; }) { return (
{icon}

{label}

{value}

{detail}

); } function DistributionRow({ name, meta, value, percentage, color, valueLabel, }: { name: string; meta: string; value: number; percentage: number; color: string; valueLabel: string; }) { return (
{name} {meta}
{valueLabel}
0 ? 1.5 : 0)}%`, backgroundColor: color }} />
{percentage.toFixed(1)}%
); } // ─── Main Component ───────────────────────────────────── export function DashboardPage() { const { t, lang } = useTranslation(); const { currency, toggle: toggleCurrency } = useCurrency(); const { initialized } = useConfigStore(); const [source, setSource] = useState("all"); const [range, setRange] = useState("today"); const [customFrom, setCustomFrom] = useState(""); const [customTo, setCustomTo] = useState(""); const [tab, setTab] = useState("log"); const [data, setData] = useState(null); const [loadedQuery, setLoadedQuery] = useState<{ source: SourceKey; range: RangeKey }>({ source: "all", range: "today" }); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [autoRefresh, setAutoRefresh] = useState(true); const [refreshInterval, setRefreshInterval] = useState(30); const [showIntervalPicker, setShowIntervalPicker] = useState(false); const [lastUpdated, setLastUpdated] = useState(null); const [analyticsOpen, setAnalyticsOpen] = useState(false); const [consoleOpen, setConsoleOpen] = useState(false); const [logPage, setLogPage] = useState(1); const [providerSort, setProviderSort] = useState<{ key: string; dir: SortDir }>({ key: "totalCost", dir: "desc" }); const [modelSort, setModelSort] = useState<{ key: string; dir: SortDir }>({ key: "totalCost", dir: "desc" }); const [codexUsage, setCodexUsage] = useState(null); const customInvalid = range === "custom" && !!customFrom && !!customTo && customFrom > customTo; const fetchData = useCallback((force = false) => { if (!initialized || customInvalid) return; const baseUrl = source === "chatgpt" ? "/api/pi/chatgpt-usage-range" : source === "all" ? "/api/pi/all-usage-range" : "/api/pi/usage-range"; // force=true adds refresh=1 so the API rescan bypasses its 30s session cache let url = `${baseUrl}?range=${range}${force ? "&refresh=1" : ""}`; if (range === "custom" && customFrom) { url += `&from=${customFrom}&to=${customTo || customFrom}`; } setRefreshing(true); setError(null); fetch(url) .then((r) => { if (!r.ok) throw new Error(`usage request failed: ${r.status}`); return r.json(); }) .then((d) => { setData(d); setLoadedQuery({ source, range }); setLoading(false); setRefreshing(false); setLastUpdated(new Date().toLocaleTimeString()); }) .catch(() => { setLoading(false); setRefreshing(false); setError("dashboard.load_error"); }); }, [initialized, source, range, customFrom, customTo, customInvalid]); useEffect(() => { fetchData(); }, [fetchData]); // Official Codex quotas use the locally logged-in OAuth session. The API // returns only a sanitized summary; OAuth credentials never reach the UI. useEffect(() => { let cancelled = false; const load = () => { fetch("/api/pi/codex-usage-status") .then((r) => r.json()) .then((status: CodexUsageStatus) => { if (!cancelled) setCodexUsage(status); }) .catch(() => { if (!cancelled) setCodexUsage(null); }); }; load(); const id = window.setInterval(load, 30_000); return () => { cancelled = true; window.clearInterval(id); }; }, [source]); // Reset request-log pagination when the queried range or source changes useEffect(() => { setLogPage(1); }, [range, customFrom, customTo, source]); // Keep the previous snapshot visible while a new source/range is loading. useEffect(() => { setLoading(true); }, [source, range, customFrom, customTo]); // Auto-refresh with configurable interval (seconds) useEffect(() => { if (!autoRefresh || refreshInterval <= 0) return; const id = setInterval(fetchData, refreshInterval * 1000); return () => clearInterval(id); }, [autoRefresh, refreshInterval, fetchData]); const today = cnTodayStr(); const displaySource = data ? loadedQuery.source : source; const displayRange = data ? loadedQuery.range : range; // Chart data: hourly for today, daily for short ranges, and monthly for long histories. const dailyRows = data?.dailyBreakdown ?? []; const dataSpanDays = getDataSpanDays(dailyRows); const chartGranularity: TrendGranularity = displayRange === "today" ? "hour" : dataSpanDays > 90 ? "month" : "day"; const trendRows = chartGranularity === "month" ? aggregateByMonth(dailyRows) : dailyRows; const rawBreakdown = displayRange === "today" ? data?.hourlyBreakdown : trendRows; const chartData = (rawBreakdown ?? []).map((d: any) => ({ date: displayRange === "today" ? d.hour?.slice(-5) : formatDateShort(d.date || d.hour, lang, chartGranularity), rawDate: d.date || d.hour, input: Math.round(d.input / 1000), output: Math.round(d.output / 1000), cacheRead: Math.round(d.cacheRead / 1000), cacheWrite: Math.round(d.cacheWrite / 1000), cost: parseFloat((currency === "CNY" ? d.cost * USD_TO_CNY : d.cost).toFixed(4)), requests: d.requests, })); const dataRangeLabel = dailyRows.length > 0 ? `${formatDateShort(dailyRows[0]!.date, lang)} - ${formatDateShort(dailyRows[dailyRows.length - 1]!.date, lang)}` : t("dashboard.no_data"); // Sorted stats + request-log pagination const sortedProviders = sortRows(data?.providerStats ?? [], providerSort.key, providerSort.dir); const sortedModels = sortRows(data?.modelStats ?? [], modelSort.key, modelSort.dir); const providerTotalCost = (data?.providerStats ?? []).reduce((s, p) => s + p.totalCost, 0); const totalLogPages = Math.max(1, Math.ceil((data?.requestLog.length ?? 0) / LOG_PAGE_SIZE)); const currentLogPage = Math.min(logPage, totalLogPages); const pagedLog = (data?.requestLog ?? []).slice((currentLogPage - 1) * LOG_PAGE_SIZE, currentLogPage * LOG_PAGE_SIZE); const analytics = useMemo(() => { const current = data ?? { totalTokens: 0, totalInput: 0, totalOutput: 0, totalCacheRead: 0, totalCacheWrite: 0, totalCost: 0, totalRequests: 0, cacheHitRate: 0, dailyBreakdown: [], hourlyBreakdown: [], requestLog: [], providerStats: [], modelStats: [], }; const totalTokens = Math.max(current.totalTokens, 0); const totalRequests = Math.max(current.totalRequests, 0); const cacheTokens = current.totalCacheRead + current.totalCacheWrite; const activitySource = displayRange === "today" ? current.hourlyBreakdown : trendRows; const peak = activitySource.reduce<{ label: string; requests: number; tokens: number } | null>((best, row: any) => { const requests = row.requests ?? 0; const tokens = (row.input ?? 0) + (row.output ?? 0) + (row.cacheRead ?? 0) + (row.cacheWrite ?? 0); const label = displayRange === "today" ? String(row.hour ?? "").slice(-5) : formatDateShort(row.date || row.hour || "", lang, chartGranularity); if (!best || requests > best.requests || (requests === best.requests && tokens > best.tokens)) { return { label, requests, tokens }; } return best; }, null); const providers = current.providerStats.filter((provider) => provider.totalRequests > 0).sort((a, b) => b.totalTokens - a.totalTokens); const models = current.modelStats.filter((model) => model.totalRequests > 0).sort((a, b) => b.totalTokens - a.totalTokens); const providerTokenTotal = providers.reduce((sum, provider) => sum + provider.totalTokens, 0); const composition = [ { key: "input", value: current.totalInput, color: CHART_LINE_COLORS.input }, { key: "output", value: current.totalOutput, color: CHART_LINE_COLORS.output }, { key: "cacheRead", value: current.totalCacheRead, color: CHART_LINE_COLORS.cacheRead }, { key: "cacheWrite", value: current.totalCacheWrite, color: CHART_LINE_COLORS.cacheWrite }, ]; return { totalTokens, avgTokens: totalRequests > 0 ? totalTokens / totalRequests : 0, avgCost: totalRequests > 0 ? current.totalCost / totalRequests : 0, cacheTokens, cacheShare: totalTokens > 0 ? (cacheTokens / totalTokens) * 100 : 0, activeProviders: providers.length, activeModels: models.length, peak, providers, models, providerTokenTotal, composition, activity: activitySource.map((row: any) => ({ label: displayRange === "today" ? String(row.hour ?? "").slice(-5) : formatDateShort(row.date || row.hour || "", lang, chartGranularity), requests: row.requests ?? 0, tokens: Math.round(((row.input ?? 0) + (row.output ?? 0) + (row.cacheRead ?? 0) + (row.cacheWrite ?? 0)) / 1000), })), }; }, [data, displayRange, trendRows, lang, chartGranularity]); const isSubscriptionSource = displaySource === "chatgpt"; const fmtCostCell = (v: number) => isSubscriptionSource ? "—" : (currency === "CNY" ? `¥${(v * USD_TO_CNY).toFixed(4)}` : formatCostShort(v)); const totalCostLabel = isSubscriptionSource ? t("dashboard.subscription_usage") : formatCost(data?.totalCost ?? 0, currency); const totalCostSubtitle = isSubscriptionSource ? t("dashboard.not_billed") : `${currency === "CNY" ? `¥${((data?.totalCost ?? 0) * USD_TO_CNY).toFixed(4)}` : `$${(data?.totalCost ?? 0).toFixed(4)}`} ${currency}`; const costAxisLabel = t(currency === "CNY" ? "dashboard.cost_label_cny" : "dashboard.cost_label_usd"); const toggleSort = (setter: typeof setProviderSort) => (key: string) => setter((s) => (s.key === key ? { key, dir: s.dir === "desc" ? "asc" : "desc" } : { key, dir: "desc" })); const handleExport = () => { if (!data) return; if (tab === "log") { downloadCsv(`pi-usage-log-${displayRange}.csv`, ["time", "provider", "model", "input", "output", "cost_usd", "requests"], data.requestLog.map((r) => [r.timestamp, r.providerId, r.modelId, r.input, r.output, r.cost, r.requests])); } else if (tab === "provider") { downloadCsv(`pi-usage-provider-${displayRange}.csv`, ["provider", "tokens", "input", "output", "cost_usd", "requests", "models"], sortedProviders.map((p) => [p.providerId, p.totalTokens, p.totalInput, p.totalOutput, p.totalCost, p.totalRequests, p.modelCount])); } else { downloadCsv(`pi-usage-model-${displayRange}.csv`, ["model", "provider", "tokens", "input", "output", "cost_usd", "requests"], sortedModels.map((m) => [m.modelId, m.providerId, m.totalTokens, m.totalInput, m.totalOutput, m.totalCost, m.totalRequests])); } }; return (
{/* Title + Time Range Selector + Currency Toggle */}
TELEMETRY // LIVE OPERATIONS

{t("dashboard.title")}

{data ? t("dashboard.requests_count", formatNumber(data.totalRequests), isSubscriptionSource ? t("dashboard.not_billed") : formatCost(data.totalCost, currency)) : ""} {lastUpdated && · {t("dashboard.last_updated", lastUpdated)}}

{data && (

{t(displaySource === "all" ? "dashboard.source_all" : displaySource === "chatgpt" ? "dashboard.source_chatgpt" : "dashboard.source_pi")} · {t(`dashboard.range.${displayRange}`)} · {dataRangeLabel} {refreshing && · {t("dashboard.refreshing")}}

)} {data?.notice && (

{data.notice === "no-config" ? t("dashboard.copilot_not_configured") : t("dashboard.copilot_api_error")}

)} {error && (

{t(error)}

)}
{showIntervalPicker && (
{t("dashboard.refresh_interval")}
{[5, 10, 30, 60].map((s) => ( ))}
)}
{RANGE_KEYS.map((key) => ( ))}
{t("dashboard.data_source")}
{([ ["all", "dashboard.source_all"], ["pi", "dashboard.source_pi"], ["chatgpt", "dashboard.source_chatgpt"], ] as const).map(([key, label]) => ( ))}
{codexUsage && (
{codexUsage.loggedIn ? t("dashboard.codex_logged_in") : t("dashboard.codex_not_logged_in")} {codexUsage.loggedIn && codexUsage.primary && codexUsage.secondary && ( <> {t("dashboard.codex_5h")}: {t("dashboard.codex_remaining", `${codexUsage.primary.remainingPercent}%`)} {formatRemainingTime(codexUsage.primary.resetAfterSeconds) && ` · ${formatRemainingTime(codexUsage.primary.resetAfterSeconds)}`} {formatResetAt(codexUsage.primary.resetAt, lang) && ` · ${t("dashboard.codex_resets", formatResetAt(codexUsage.primary.resetAt, lang)!)}`} {t("dashboard.codex_7d")}: {t("dashboard.codex_remaining", `${codexUsage.secondary.remainingPercent}%`)} {formatRemainingTime(codexUsage.secondary.resetAfterSeconds) && ` · ${formatRemainingTime(codexUsage.secondary.resetAfterSeconds)}`} {formatResetAt(codexUsage.secondary.resetAt, lang) && ` · ${t("dashboard.codex_resets", formatResetAt(codexUsage.secondary.resetAt, lang)!)}`} )} {codexUsage.error && {t("dashboard.codex_quota_unavailable")}}
)}
{/* Custom Date Picker */} {range === "custom" && (
setCustomFrom(e.target.value)} className="rounded-lg border px-3 py-1.5 text-xs" style={{ backgroundColor: "var(--input-bg)", borderColor: customInvalid ? "#ef4444" : "var(--input-border)", color: "var(--input-text)" }} /> {t("dashboard.to")} setCustomTo(e.target.value)} className="rounded-lg border px-3 py-1.5 text-xs" style={{ backgroundColor: "var(--input-bg)", borderColor: customInvalid ? "#ef4444" : "var(--input-border)", color: "var(--input-text)" }} /> {customInvalid && {t("dashboard.invalid_range")}}
)} {/* Loading State */} {loading && !data && (
)} {!loading && !data && error && (
{t("dashboard.load_error")} {t("dashboard.load_error_hint")}
)} {data && (
{/* Overview Cards */}
} subtitle={`≈ ${formatTokensShort(data.totalTokens, lang)}`} >
} subtitle={t("dashboard.api_calls")} /> } subtitle={totalCostSubtitle} /> } progress={data.cacheHitRate} />
{/* Rich analytics overview */}
setAnalyticsOpen(event.currentTarget.open)} > {t("dashboard.detailed_analysis")} {t("dashboard.detailed_analysis_hint")} {analytics.activeModels} {t("dashboard.active_models")}
SYSTEM READOUT // 01

{t("dashboard.operational_overview")}

} /> } accent="var(--signal-amber)" /> } accent="var(--signal-lime)" /> } accent="var(--signal-violet)" />
TOKEN FLOW // 02

{t("dashboard.token_composition")}

{analytics.composition.map((entry) => )} formatTokensShort(Number(Array.isArray(value) ? value[0] : value ?? 0), lang)} contentStyle={{ backgroundColor: "var(--card-bg-solid)", border: "1px solid var(--card-border)", borderRadius: "8px", fontSize: "11px" }} />
{formatTokensShort(analytics.cacheTokens, lang)} {t("dashboard.cache_total")}
{analytics.composition.map((entry) => { const pct = analytics.totalTokens > 0 ? (entry.value / analytics.totalTokens) * 100 : 0; const labels: Record = { input: t("dashboard.input"), output: t("dashboard.output"), cacheRead: t("dashboard.cache_hit"), cacheWrite: t("dashboard.cache_create"), }; return (
{labels[entry.key]} {pct.toFixed(1)}%
); })}
{t("dashboard.cache_hit_rate")} {analytics.cacheShare.toFixed(1)}%
ROUTING MATRIX // 03

{t(displaySource === "chatgpt" ? "dashboard.model_mix" : "dashboard.provider_mix")}

{(displaySource === "chatgpt" ? analytics.models : analytics.providers).length === 0 ? (

{t("dashboard.no_data")}

) : displaySource === "chatgpt" ? analytics.models.slice(0, 5).map((model, index) => ( 0 ? (model.totalTokens / analytics.totalTokens) * 100 : 0} valueLabel={formatTokensShort(model.totalTokens, lang)} color={COLORS[index % COLORS.length] ?? COLORS[0]!} /> )) : analytics.providers.slice(0, 5).map((provider, index) => ( 0 ? (provider.totalTokens / analytics.providerTokenTotal) * 100 : 0} valueLabel={formatTokensShort(provider.totalTokens, lang)} color={COLORS[index % COLORS.length] ?? COLORS[0]!} /> ))}
REQUEST PULSE // 04

{t("dashboard.request_activity")}

[Number(Array.isArray(value) ? value[0] : value ?? 0), t("dashboard.requests")]} contentStyle={{ backgroundColor: "var(--card-bg-solid)", border: "1px solid var(--card-border)", borderRadius: "8px", fontSize: "11px" }} />
{t("dashboard.total_requests")} {formatNumber(data.totalRequests)}
MODEL LOAD // 05

{t("dashboard.model_leaderboard")}

{analytics.models.length === 0 ? (

{t("dashboard.no_data")}

) : analytics.models.slice(0, 5).map((model, index) => (
0{index + 1}
{model.modelId}
{model.providerId} · {formatNumber(model.totalRequests)} {t("dashboard.requests")}
{formatTokensShort(model.totalTokens, lang)}
))}
{/* Usage Trend Chart */}

{t("dashboard.usage_trend")}

{displayRange === "today" ? t("dashboard.range.today") : `${dataRangeLabel}${chartGranularity === "month" ? ` · ${t("dashboard.monthly_view")}` : ""}`}

{/* Tabs: Request Log / Provider / Model */}
setConsoleOpen(event.currentTarget.open)} > {t("dashboard.raw_data")} {t("dashboard.raw_data_hint")} {t("dashboard.total_items", String(data.requestLog.length))}
{/* Tab Header */}
{([ { key: "log" as TabKey, label: "dashboard.request_log" }, { key: "provider" as TabKey, label: "dashboard.provider_stats" }, { key: "model" as TabKey, label: "dashboard.model_stats" }, ]).map((tabItem) => ( ))}
{/* Tab Content */}
{tab === "log" && ( <> {pagedLog.length === 0 ? ( ) : ( pagedLog.map((r, i) => ( )) )}
{t("dashboard.time")} {t("dashboard.provider")} {t("dashboard.model")} {t("dashboard.input")} {t("dashboard.output")} {t("dashboard.cost")} {t("dashboard.requests")}
{t("dashboard.no_data")}
{r.timestamp} {r.providerId} {r.modelId} {formatTokensShort(r.input, lang)} {formatTokensShort(r.output, lang)} {fmtCostCell(r.cost)} {r.requests}
{data.requestLog.length > LOG_PAGE_SIZE && (
{t("dashboard.total_items", String(data.requestLog.length))}
{currentLogPage} / {totalLogPages}
)} )} {tab === "provider" && ( {sortedProviders.length === 0 ? ( ) : ( sortedProviders.map((p, i) => { const pct = providerTotalCost > 0 ? (p.totalCost / providerTotalCost) * 100 : 0; return ( ); }) )}
{t("dashboard.provider")} {t("dashboard.cost_share")}
{t("dashboard.no_data")}
{p.providerId} {formatTokensShort(p.totalTokens, lang)} {formatTokensShort(p.totalInput, lang)} {formatTokensShort(p.totalOutput, lang)} {fmtCostCell(p.totalCost)} {p.totalRequests} {p.modelCount}
{pct.toFixed(1)}%
)} {tab === "model" && ( {sortedModels.length === 0 ? ( ) : ( sortedModels.map((m) => ( )) )}
{t("dashboard.model")} {t("dashboard.provider")}
{t("dashboard.no_data")}
{m.modelId} {m.providerId} {formatTokensShort(m.totalTokens, lang)} {formatTokensShort(m.totalInput, lang)} {formatTokensShort(m.totalOutput, lang)} {fmtCostCell(m.totalCost)} {m.totalRequests}
)}
)}
); }