Files
CC-Switch/src/components/usage/ModelStatsTable.tsx
T
Jason 687ffc237d feat: add per-app usage filtering (Claude/Codex/Gemini)
Add dashboard-level app type filter to usage statistics, replacing the
DataSourceBar with a more useful segmented control. All components
(summary cards, trend chart, provider stats, model stats, request logs)
now respond to the selected app filter.

Backend: add optional app_type parameter to get_usage_summary,
get_daily_trends, get_provider_stats, and get_model_stats queries.
Frontend: new AppTypeFilter type, updated query keys with appType
dimension for proper cache separation, and RequestLogTable local
filter auto-locks when dashboard filter is active.
2026-04-09 16:49:13 +08:00

87 lines
2.6 KiB
TypeScript

import { useTranslation } from "react-i18next";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useModelStats } from "@/lib/query/usage";
import { fmtUsd } from "./format";
interface ModelStatsTableProps {
appType?: string;
refreshIntervalMs: number;
}
export function ModelStatsTable({
appType,
refreshIntervalMs,
}: ModelStatsTableProps) {
const { t } = useTranslation();
const { data: stats, isLoading } = useModelStats(appType, {
refetchInterval: refreshIntervalMs > 0 ? refreshIntervalMs : false,
});
if (isLoading) {
return <div className="h-[400px] animate-pulse rounded bg-gray-100" />;
}
return (
<div className="rounded-lg border border-border/50 bg-card/40 backdrop-blur-sm overflow-hidden">
<Table>
<TableHeader>
<TableRow>
<TableHead>{t("usage.model", "模型")}</TableHead>
<TableHead className="text-right">
{t("usage.requests", "请求数")}
</TableHead>
<TableHead className="text-right">
{t("usage.tokens", "Tokens")}
</TableHead>
<TableHead className="text-right">
{t("usage.totalCost", "总成本")}
</TableHead>
<TableHead className="text-right">
{t("usage.avgCost", "平均成本")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{stats?.length === 0 ? (
<TableRow>
<TableCell
colSpan={5}
className="text-center text-muted-foreground"
>
{t("usage.noData", "暂无数据")}
</TableCell>
</TableRow>
) : (
stats?.map((stat) => (
<TableRow key={stat.model}>
<TableCell className="font-mono text-sm">
{stat.model}
</TableCell>
<TableCell className="text-right">
{stat.requestCount.toLocaleString()}
</TableCell>
<TableCell className="text-right">
{stat.totalTokens.toLocaleString()}
</TableCell>
<TableCell className="text-right">
{fmtUsd(stat.totalCost, 4)}
</TableCell>
<TableCell className="text-right">
{fmtUsd(stat.avgCostPerRequest, 6)}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
);
}