PMFlow/frontend/src/pages/ContractSyncBoardPage.tsx

94 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useQuery } from "@tanstack/react-query";
import { ShieldCheck } from "lucide-react";
import { taskBriefApi } from "@/api/taskBriefs";
import { ConfigSubpageHeader } from "@/components/ai-config/ConfigSubpageHeader";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
export default function ContractSyncBoardPage() {
const syncQuery = useQuery({
queryKey: ["task-brief-contract-sync"],
queryFn: () => taskBriefApi.getContractSyncStatus(),
});
const data = syncQuery.data;
const files = data?.files || [];
const errorText = syncQuery.error instanceof Error ? syncQuery.error.message : "请检查后端服务。";
return (
<div className="space-y-6">
<ConfigSubpageHeader
title="契约同步看板"
description="持续检查关键契约文档是否与当前功能实现同步。"
icon={<ShieldCheck className="h-5 w-5" />}
tone="amber"
meta={
<span>
状态:
{syncQuery.isLoading ? "检查中" : syncQuery.isError ? "检查失败" : data?.all_synced ? "已同步" : "存在缺失"}
</span>
}
/>
<Card className="border-slate-200/80 bg-white/90 shadow-sm backdrop-blur-sm dark:border-slate-800 dark:bg-slate-900/85">
<CardHeader>
<CardTitle className="text-base">契约文件明细</CardTitle>
<CardDescription>缺失项会直接影响跨端协作与回归可靠性。</CardDescription>
</CardHeader>
<CardContent>
{syncQuery.isLoading ? (
<p className="text-sm text-slate-500 dark:text-slate-400">加载中...</p>
) : syncQuery.isError ? (
<div className="rounded-xl border border-rose-200/70 bg-rose-50/80 p-4 dark:border-rose-900/40 dark:bg-rose-950/30">
<p className="text-sm font-medium text-rose-700 dark:text-rose-200">同步状态读取失败:{errorText}</p>
<Button
className="mt-3"
variant="outline"
size="sm"
onClick={() => void syncQuery.refetch()}
disabled={syncQuery.isFetching}
>
{syncQuery.isFetching ? "重试中..." : "重试"}
</Button>
</div>
) : files.length === 0 ? (
<p className="text-sm text-slate-500 dark:text-slate-400">暂无契约文件记录。</p>
) : (
<div className="overflow-x-auto rounded-xl border border-slate-200/90 dark:border-slate-700">
<Table>
<TableHeader>
<TableRow>
<TableHead>文件</TableHead>
<TableHead>存在性</TableHead>
<TableHead>更新时间</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{files.map((item) => (
<TableRow key={item.file} className="hover:bg-slate-50/70 dark:hover:bg-slate-800/60">
<TableCell className="font-medium">{item.file}</TableCell>
<TableCell>
<span
className={
item.exists
? "inline-flex rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/35 dark:text-emerald-200"
: "inline-flex rounded-full bg-rose-100 px-2 py-0.5 text-xs font-medium text-rose-700 dark:bg-rose-900/35 dark:text-rose-200"
}
>
{item.exists ? "已存在" : "缺失"}
</span>
</TableCell>
<TableCell>{item.updated_at || "-"}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</CardContent>
</Card>
</div>
);
}