www_site/components/admin/overview.tsx

52 lines
2.8 KiB
TypeScript
Raw Normal View History

"use client";
import { useEffect, useState } from "react";
import { getUsageSummary } from "@/lib/admin";
import { StatusCard } from "@/components/ui";
type Summary = {
quota: { used: number; reserved: number; adjustment: number };
ledger_total: number;
tasks: { total: number; by_status: Record<string, number>; failed: number };
};
export function OverviewPanel() {
const [data, setData] = useState<Summary | null>(null);
const [error, setError] = useState("");
useEffect(() => {
getUsageSummary()
.then(setData)
.catch((e) => setError(e instanceof Error ? e.message : "无法加载概览"));
}, []);
if (error) return <div className="rounded-xl border border-danger/30 bg-danger/10 p-4 text-sm text-danger">{error}</div>;
if (!data) return <StatusCard title="正在加载概览" description="正在汇总额度与任务。" tone="info" />;
const statusLabels: Record<string, string> = { queued: "排队", running: "进行中", succeeded: "成功", failed: "失败" };
return (
<div className="space-y-6">
<div className="grid gap-5 md:grid-cols-3">
<StatusCard title="累计使用" description={`${data.quota.used.toLocaleString()} 字符`} tone="success" />
<StatusCard title="冻结中" description={`${data.quota.reserved.toLocaleString()} 字符`} tone="warning" />
<StatusCard title="手动调整" description={`${data.quota.adjustment.toLocaleString()} 字符`} tone="info" />
</div>
<div className="grid gap-5 md:grid-cols-3">
<StatusCard title="任务总数" description={`${data.tasks.total}`} />
<StatusCard title={statusLabels.succeeded} description={`${data.tasks.by_status.succeeded ?? 0}`} tone="success" />
<StatusCard title="失败任务" description={`${data.tasks.failed}`} tone="warning" />
</div>
<div className="rounded-2xl border border-line bg-panel/50 p-5">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-subtle"></p>
<dl className="mt-3 grid gap-3 text-sm sm:grid-cols-3">
<div className="flex justify-between gap-4 border-b border-line pb-3 sm:border-0 sm:pb-0"><dt className="text-muted"></dt><dd className="text-copy">{data.ledger_total.toLocaleString()}</dd></div>
<div className="flex justify-between gap-4 sm:justify-start"><dt className="text-muted">quota_accounts.used</dt><dd className="text-copy">{data.quota.used.toLocaleString()}</dd></div>
<div className="flex justify-between gap-4 sm:justify-start"><dt className="text-muted"></dt><dd className="text-copy">{data.quota.adjustment.toLocaleString()}</dd></div>
</dl>
<p className="mt-3 text-xs leading-5 text-subtle"></p>
</div>
</div>
);
}