'use client' import { useState } from 'react' import useSWR from 'swr' import { Zap, Monitor, Smartphone } from 'lucide-react' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar, Legend, } from 'recharts' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { ErrorBanner, EmptyState } from '@/components/ui/state' import { TableSkeleton, ChartSkeleton } from '@/components/ui/skeleton' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { api } from '@/lib/api-client' import { formatNumber } from '@/lib/utils' import type { UsageRecord, UsageByModel, ModelUsageStat, DailyUsageStat } from '@/lib/types' export default function UsagePage() { const [days, setDays] = useState(7) const [activeTab, setActiveTab] = useState('relay') const [error, setError] = useState('') // 4 parallel SWR calls — each loads independently const { data: dailyData = [], isLoading: dailyLoading } = useSWR( ['usage.daily', days], () => api.usage.daily({ days }) ) const { data: modelData = [], isLoading: modelLoading } = useSWR( ['usage.byModel', days], () => api.usage.byModel({ days }) ) const { data: telemetryModels = [] } = useSWR( ['telemetry.modelStats'], () => api.telemetry.modelStats() ) const { data: telemetryDaily = [] } = useSWR( ['telemetry.dailyStats', days], () => api.telemetry.dailyStats({ days }) ) const relayLoading = dailyLoading || modelLoading const telemetryLoading = !telemetryModels.length && !telemetryDaily.length && (dailyLoading || modelLoading) // === Relay 用量图表数据 === const relayLineData = dailyData.map((r) => ({ day: r.day.slice(5), Input: r.input_tokens, Output: r.output_tokens, })) const relayBarData = modelData.map((r) => ({ model: r.model_id, 请求量: r.count, Input: r.input_tokens, Output: r.output_tokens, })) const relayTotalInput = dailyData.reduce((s, r) => s + r.input_tokens, 0) const relayTotalOutput = dailyData.reduce((s, r) => s + r.output_tokens, 0) const relayTotalRequests = dailyData.reduce((s, r) => s + r.count, 0) // === 遥测图表数据 === const telemetryLineData = telemetryDaily.map((r) => ({ day: r.day.slice(5), Input: r.input_tokens, Output: r.output_tokens, 设备数: r.unique_devices, })) const telemetryTotalInput = telemetryDaily.reduce((s, r) => s + r.input_tokens, 0) const telemetryTotalOutput = telemetryDaily.reduce((s, r) => s + r.output_tokens, 0) const telemetryTotalRequests = telemetryDaily.reduce((s, r) => s + r.request_count, 0) // === 合计 === const totalInput = relayTotalInput + telemetryTotalInput const totalOutput = relayTotalOutput + telemetryTotalOutput const totalRequests = relayTotalRequests + telemetryTotalRequests return (
总请求数
{formatNumber(totalRequests)}
总 Input Tokens
{formatNumber(totalInput)}
总 Output Tokens
{formatNumber(totalOutput)}
中转请求
{formatNumber(relayTotalRequests)}
桌面端调用
{formatNumber(telemetryTotalRequests)}