import type { AcquisitionPlan, PlanStatus } from "@umt/contracts"; import { ClipboardList } from "lucide-react"; import { useState } from "react"; import { Link } from "react-router"; import { PLAN_STATUS_LABEL, useDownloadTasks, usePlans } from "~/api/downloads.ts"; import { type DataColumn, DataTable } from "~/components/console/data-table.tsx"; import { DetailSection, DetailSheet } from "~/components/console/detail-sheet.tsx"; import { FilterBar, FilterSelect } from "~/components/console/filter-bar.tsx"; import { JsonView } from "~/components/console/json-view.tsx"; import { KvList } from "~/components/console/kv-list.tsx"; import { consolePath } from "~/components/console/nav.ts"; import { PageHeader } from "~/components/console/page-header.tsx"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { useQueryState } from "~/components/console/use-query-state.ts"; import { DownloadsTabs } from "~/components/downloads/downloads-tabs.tsx"; import { PRIORITY } from "~/components/library/labels.ts"; import { Button } from "~/components/ui/button.tsx"; import { relTime } from "~/components/workbench/release-fields.ts"; import { planReasonText } from "~/lib/labels.ts"; import { formatBytes, formatDateTime } from "~/lib/utils.ts"; export const meta = () => [{ title: "下载计划 · 下载面板 · UHD 媒体自动化控制台" }]; const PURPOSE = { fill: "补缺", upgrade: "升级", replace_source: "换源" } as const; const planTone = (s: PlanStatus) => s === "failed" ? "destructive" : s === "completed" ? "success" : s === "expired" ? "warning" : "neutral"; export default function DownloadPlansPage() { const f = useQueryState(["status"] as const); const [openPlan, setOpenPlan] = useState(null); const plans = usePlans((f.values.status || undefined) as PlanStatus | undefined); // 计划 → 下载任务:同一列表按 acquisitionPlanId 对上 const downloads = useDownloadTasks({}); const rows = plans.data?.pages.flatMap((p) => p.items); const linkedDownload = openPlan ? downloads.data?.pages.flatMap((p) => p.items).find((t) => t.acquisitionPlanId === openPlan.id) : undefined; const columns: DataColumn[] = [ { key: "purpose", header: "计划", cell: (p) => (

{PURPOSE[p.purpose]} · 优先级 {PRIORITY[p.priority as keyof typeof PRIORITY] ?? p.priority}

{p.id.slice(0, 8)}

), }, { key: "status", header: "状态", cell: (p) => {PLAN_STATUS_LABEL[p.status]}, }, { key: "reserved", header: "预留空间", hideBelow: "md", align: "right", cell: (p) => {formatBytes(p.reservedBytes, 1)}, }, { key: "reason", header: "说明", hideBelow: "lg", cell: (p) => {planReasonText(p.statusReason) ?? "—"}, }, { key: "at", header: "创建", hideBelow: "sm", cell: (p) => {relTime(p.createdAt)}, }, ]; return ( <> f.set("status", v)} options={Object.entries(PLAN_STATUS_LABEL).map(([value, label]) => ({ value, label }))} /> p.id} loading={plans.isPending} error={plans.error} onRetry={() => void plans.refetch()} onRowClick={setOpenPlan} selectedKey={openPlan?.id} hasMore={plans.hasNextPage} onLoadMore={() => void plans.fetchNextPage()} loadingMore={plans.isFetchingNextPage} empty={{ icon: ClipboardList, title: f.active ? "没有符合筛选的下载计划" : "还没有下载计划", description: f.active ? undefined : "候选被选中后出现在这里。", }} /> !o && setOpenPlan(null)} title={openPlan ? `${PURPOSE[openPlan.purpose]}计划` : ""} description={planReasonText(openPlan?.statusReason) ?? undefined} footer={ linkedDownload ? ( ) : null } > {openPlan ? ( <> {PLAN_STATUS_LABEL[openPlan.status]} ), }, { label: "优先级", value: openPlan.priority }, { label: "预留空间", value: formatBytes(openPlan.reservedBytes, 1) }, { label: "过期于", value: formatDateTime(openPlan.expiresAt) }, { label: "创建", value: formatDateTime(openPlan.createdAt) }, { label: "版本目标", value: openPlan.slotId, mono: true }, { label: "资源", value: openPlan.sourceReleaseId, mono: true, wide: true }, ]} /> ) : null} ); }