79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
import type { AttentionSummary, Decision } from "@umt/contracts";
|
|
import { ChevronRight } from "lucide-react";
|
|
import { approvalHref, HEALTH_HREF } from "~/components/console/inbox-bell.tsx";
|
|
import { consolePath } from "~/components/console/nav.ts";
|
|
import { Link } from "~/lib/router-compat.tsx";
|
|
import { ORGANIZE_ATTENTION_PATH } from "~/routes/console/redirects.ts";
|
|
|
|
// 首页与运行状态共用的「需要关注」汇总计数(2026-10-06 去掉待处理中心后):
|
|
// 「3 条整理需要处理」「1 个站点不可达」…,点进去到对应页面;只列非零项。
|
|
|
|
/** 系统健康事项按问题归类成一句(量词 + 说法) */
|
|
function healthKind(d: Pick<Decision, "kind" | "dedupeKey">): { key: string; unit: string } {
|
|
if (d.dedupeKey.startsWith("library_root_offline:")) return { key: "library_root", unit: "个媒体库目录离线" };
|
|
switch (d.kind) {
|
|
case "source_unreachable":
|
|
return { key: d.kind, unit: "个站点不可达" };
|
|
case "downloader_offline":
|
|
return { key: d.kind, unit: "个下载器离线" };
|
|
case "cache_volume_unknown":
|
|
return { key: d.kind, unit: "个下载目录读不到容量" };
|
|
case "space_insufficient":
|
|
return { key: d.kind, unit: "个下载盘空间不足" };
|
|
default:
|
|
return { key: "other", unit: "个系统问题" };
|
|
}
|
|
}
|
|
|
|
export interface AttentionRow {
|
|
key: string;
|
|
text: string;
|
|
to: string;
|
|
}
|
|
|
|
/** 汇总 → 一行一类(非零才列) */
|
|
export function attentionRows(s: AttentionSummary | undefined): AttentionRow[] {
|
|
if (!s) return [];
|
|
const rows: AttentionRow[] = [];
|
|
if (s.organize) rows.push({ key: "organize", text: `${s.organize} 条整理需要处理`, to: ORGANIZE_ATTENTION_PATH });
|
|
if (s.approvals.total) {
|
|
const first = s.approvals.items[0];
|
|
rows.push({
|
|
key: "approvals",
|
|
text: `${s.approvals.total} 个操作等你批准`,
|
|
to: s.approvals.total === 1 && first ? approvalHref(first) : consolePath("agent"),
|
|
});
|
|
}
|
|
const counts = new Map<string, { unit: string; n: number }>();
|
|
for (const d of s.health.items) {
|
|
const k = healthKind(d);
|
|
const cur = counts.get(k.key);
|
|
counts.set(k.key, { unit: k.unit, n: (cur?.n ?? 0) + 1 });
|
|
}
|
|
const listed = [...counts.values()].reduce((a, x) => a + x.n, 0);
|
|
if (s.health.total > listed) {
|
|
const cur = counts.get("other");
|
|
counts.set("other", { unit: "个系统问题", n: (cur?.n ?? 0) + s.health.total - listed });
|
|
}
|
|
for (const [key, v] of counts) rows.push({ key, text: `${v.n} ${v.unit}`, to: HEALTH_HREF });
|
|
return rows;
|
|
}
|
|
|
|
export function AttentionList({ rows }: { rows: AttentionRow[] }) {
|
|
return (
|
|
<ul className="divide-y divide-border/60">
|
|
{rows.map((r) => (
|
|
<li key={r.key}>
|
|
<Link
|
|
to={r.to}
|
|
className="flex min-w-0 items-center justify-between gap-3 py-1.5 text-sm text-foreground hover:underline"
|
|
>
|
|
<span className="truncate">{r.text}</span>
|
|
<ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden />
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|