Files
UHD_Media_Tool/apps/web/app/routes/console/home.tsx
T
rootandClaude Fable 5.1 304feea141 chore(deno): migrate toolchain from Node 24 + pnpm to Deno 2.9.7 + TypeScript 7.0.2 (W1.5)
- root deno.json workspace (nodeModulesDir auto), npm versions registered once in imports, deno.lock;
  remove pnpm-lock/pnpm-workspace/root package.json; members keep minimal package.json (name/type/exports)
- tasks: dev, typecheck (tsc 7.0.2 per member + deno check), lint/fmt (biome, useImportExtensions),
  test (vitest via deno), check:tokens, build (web + deno compile), ci, db:migrate/reset, gen:types
- tsconfig base moved to config/ (no root tsconfig.json); deno types -> types/deno.d.ts; web drops baseUrl
- server: Deno.serve entry with graceful shutdown, --role migrate|preflight|probe, WEB_ROOT static SPA hosting;
  drop @hono/node-server, tsx, esbuild build.mjs
- logger: redact child() bindings and serialize err by message/stack; sync stdout destination; tests
- relative imports get explicit extensions (sites/mp-sync paths left for follow-up)
- crash-recovery tests spawn `deno run -A`; tasks tests use unique per-file schemas
- single-container Dockerfile + entrypoint (migrate -> preflight -> api+worker watchdog), docker-compose.yml
- docs: README, runbook, ADR-001 superseded, plan §1 validation row

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-27 18:53:06 +08:00

59 lines
2.7 KiB
TypeScript

import { Activity, AlertTriangle, CircleCheck, HardDrive, ListTodo, Radio, Sparkles } from "lucide-react";
import { useHealth } from "~/api/health.ts";
import { EmptyState } from "~/components/console/empty-state.tsx";
import { PageHeader } from "~/components/console/page-header.tsx";
import { StatusBadge } from "~/components/console/status-badge.tsx";
import { formatDateTime } from "~/lib/utils.ts";
// 首页六区块(REQ §13.4.3),W3 接入正式状态投影
const SECTIONS = [
{ title: "自动化概览", icon: Sparkles, hint: "今日新增、升级与已核验入库" },
{ title: "当前工作", icon: Activity, hint: "正在搜索/下载/整理/核验的目标" },
{ title: "需要我处理", icon: AlertTriangle, hint: "根因级事项与剩余决策" },
{ title: "缓存与保留", icon: HardDrive, hint: "使用率、在途预留与可清理量" },
{ title: "来源健康", icon: Radio, hint: "站点、qB、Emby、模型上游" },
{ title: "最近产出", icon: ListTodo, hint: "新增的集与版本、整理前后路径" },
];
function ServiceStatus() {
const { data, isPending, isError, error } = useHealth();
if (isPending) return <StatusBadge>检查中</StatusBadge>;
if (isError) return <StatusBadge tone="destructive">服务不可达:{error.message}</StatusBadge>;
return (
<div className="flex flex-wrap items-center gap-2">
<StatusBadge tone={data.status === "ok" ? "success" : "warning"}>
服务{data.status === "ok" ? "正常" : "降级"}
</StatusBadge>
<StatusBadge tone={data.db === "ok" ? "success" : data.db === "down" ? "destructive" : "neutral"}>
数据库 {data.db === "ok" ? "正常" : data.db === "down" ? "不可用" : "未知"}
</StatusBadge>
<span className="text-meta text-muted-foreground">
v{data.version} · {data.role} · {formatDateTime(data.time)}
</span>
</div>
);
}
export default function Home() {
return (
<>
<PageHeader
title="首页"
description="系统是否在推进目标、完成了什么、还有什么缺口、是否需要你。"
actions={<ServiceStatus />}
/>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{SECTIONS.map((s) => (
<section key={s.title} className="pg-slab rounded-xl p-5">
<div className="mb-3 flex items-center gap-2">
<s.icon className="h-4 w-4 text-muted-foreground" />
<h2 className="text-body font-semibold text-foreground">{s.title}</h2>
</div>
<EmptyState icon={CircleCheck} title="暂无数据" description={s.hint} plain className="py-8" />
</section>
))}
</div>
</>
);
}