mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-25 23:41:27 +08:00
687ffc237d
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.
93 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|