Files
cc-switch/src/components/usage/ProviderStatsTable.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

93 lines
2.8 KiB
TypeScript

import { useTranslation } from "react-i18next";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useProviderStats } from "@/lib/query/usage";
import { fmtUsd } from "./format";
interface ProviderStatsTableProps {
appType?: string;
refreshIntervalMs: number;
}
export function ProviderStatsTable({
appType,
refreshIntervalMs,
}: ProviderStatsTableProps) {
const { t } = useTranslation();
const { data: stats, isLoading } = useProviderStats(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.provider", "Provider")}</TableHead>
<TableHead className="text-right">
{t("usage.requests", "请求数")}
</TableHead>
<TableHead className="text-right">
{t("usage.tokens", "Tokens")}
</TableHead>
<TableHead className="text-right">
{t("usage.cost", "成本")}
</TableHead>
<TableHead className="text-right">
{t("usage.successRate", "成功率")}
</TableHead>
<TableHead className="text-right">
{t("usage.avgLatency", "平均延迟")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{stats?.length === 0 ? (
<TableRow>
<TableCell
colSpan={6}
className="text-center text-muted-foreground"
>
{t("usage.noData", "暂无数据")}
</TableCell>
</TableRow>
) : (
stats?.map((stat) => (
<TableRow key={stat.providerId}>
<TableCell className="font-medium">
{stat.providerName}
</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">
{stat.successRate.toFixed(1)}%
</TableCell>
<TableCell className="text-right">
{stat.avgLatencyMs}ms
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
);
}