145 lines
5.9 KiB
TypeScript
145 lines
5.9 KiB
TypeScript
"use client";
|
||
|
||
import { AlertCircle, CheckCircle2 } from "lucide-react";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type DetailUiValidationSnapshot = {
|
||
passed?: boolean;
|
||
issues?: string[];
|
||
};
|
||
|
||
type DetailUiReviewSnapshot = {
|
||
screenIndex: number;
|
||
action?: "pass" | "protocol_patch" | "regenerate_image";
|
||
passed?: boolean;
|
||
issues?: string[];
|
||
};
|
||
|
||
export function DetailUiValidationPanel(params: {
|
||
generationMode: "classic" | "agent_layout";
|
||
planValidation?: DetailUiValidationSnapshot | null;
|
||
latestScreenValidation?: DetailUiReviewSnapshot | null;
|
||
surfaceId?: string | null;
|
||
}) {
|
||
const planIssues = Array.isArray(params.planValidation?.issues)
|
||
? params.planValidation?.issues.filter(Boolean)
|
||
: [];
|
||
const latestIssues = Array.isArray(params.latestScreenValidation?.issues)
|
||
? params.latestScreenValidation.issues.filter(Boolean)
|
||
: [];
|
||
|
||
if (!params.planValidation && !params.latestScreenValidation) {
|
||
return null;
|
||
}
|
||
|
||
const title =
|
||
params.generationMode === "agent_layout" ? "协议排版质检" : "排版与复审状态";
|
||
const hasPlanValidation = Boolean(params.planValidation);
|
||
const latestActionLabel =
|
||
params.latestScreenValidation?.action === "protocol_patch"
|
||
? "已走协议修正"
|
||
: params.latestScreenValidation?.action === "regenerate_image"
|
||
? "已要求重生图"
|
||
: "通过";
|
||
|
||
return (
|
||
<div className="mt-3 rounded-xl border border-border/50 bg-zinc-50/40 p-3 dark:bg-zinc-900/40">
|
||
<div className="mb-2 flex items-center justify-between gap-3">
|
||
<div className="flex items-center gap-2">
|
||
{params.planValidation?.passed === false || latestIssues.length > 0 ? (
|
||
<AlertCircle className="h-4 w-4 text-amber-500" />
|
||
) : (
|
||
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
|
||
)}
|
||
<p className="text-[11px] font-medium text-foreground">{title}</p>
|
||
</div>
|
||
<span className="text-[10px] text-muted-foreground">
|
||
{params.surfaceId
|
||
? params.generationMode === "agent_layout"
|
||
? "结构化 surface 已建立"
|
||
: "当前结果可继续复审"
|
||
: params.generationMode === "agent_layout"
|
||
? "文字层可直接修正"
|
||
: "问题会触发二次修图"}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<div className="flex items-start justify-between gap-3 rounded-lg border border-border/40 bg-background/80 px-3 py-2 dark:bg-zinc-950/40">
|
||
<div>
|
||
<div className="text-[11px] font-medium text-foreground">规划阶段</div>
|
||
<div className="mt-0.5 text-[10px] text-muted-foreground">
|
||
{params.planValidation?.passed === false
|
||
? `发现 ${planIssues.length} 条协议/排版提示`
|
||
: hasPlanValidation
|
||
? "协议结构已通过基础校验"
|
||
: "等待本轮规划结果"}
|
||
</div>
|
||
</div>
|
||
<span
|
||
className={cn(
|
||
"rounded-full px-2 py-0.5 text-[10px] font-medium",
|
||
!hasPlanValidation
|
||
? "bg-zinc-100 text-zinc-600 ring-1 ring-inset ring-zinc-500/10 dark:bg-zinc-800 dark:text-zinc-300"
|
||
: params.planValidation?.passed === false
|
||
? "bg-amber-50 text-amber-700 ring-1 ring-inset ring-amber-500/20 dark:bg-amber-500/10 dark:text-amber-300"
|
||
: "bg-emerald-50 text-emerald-700 ring-1 ring-inset ring-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300",
|
||
)}
|
||
>
|
||
{!hasPlanValidation
|
||
? "待开始"
|
||
: params.planValidation?.passed === false
|
||
? "待优化"
|
||
: "通过"}
|
||
</span>
|
||
</div>
|
||
|
||
{params.latestScreenValidation ? (
|
||
<div className="flex items-start justify-between gap-3 rounded-lg border border-border/40 bg-background/80 px-3 py-2 dark:bg-zinc-950/40">
|
||
<div>
|
||
<div className="text-[11px] font-medium text-foreground">
|
||
最近一屏:第 {params.latestScreenValidation.screenIndex} 屏
|
||
</div>
|
||
<div className="mt-0.5 text-[10px] text-muted-foreground">
|
||
{params.latestScreenValidation.passed === false
|
||
? latestIssues[0] || "存在可优化项"
|
||
: "最近一屏已通过当前质检"}
|
||
</div>
|
||
</div>
|
||
<span
|
||
className={cn(
|
||
"rounded-full px-2 py-0.5 text-[10px] font-medium",
|
||
params.latestScreenValidation.action === "regenerate_image"
|
||
? "bg-red-50 text-red-700 ring-1 ring-inset ring-red-500/20 dark:bg-red-500/10 dark:text-red-300"
|
||
: params.latestScreenValidation.action === "protocol_patch"
|
||
? "bg-blue-50 text-blue-700 ring-1 ring-inset ring-blue-500/20 dark:bg-blue-500/10 dark:text-blue-300"
|
||
: "bg-emerald-50 text-emerald-700 ring-1 ring-inset ring-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300",
|
||
)}
|
||
>
|
||
{latestActionLabel}
|
||
</span>
|
||
</div>
|
||
) : null}
|
||
|
||
{(planIssues.length > 0 || latestIssues.length > 0) && (
|
||
<div className="rounded-lg border border-dashed border-border/40 bg-background/60 px-3 py-2 dark:bg-zinc-950/30">
|
||
<div className="text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
||
当前关注点
|
||
</div>
|
||
<div className="mt-1 space-y-1">
|
||
{[...planIssues, ...latestIssues]
|
||
.filter((item, index, array) => array.indexOf(item) === index)
|
||
.slice(0, 3)
|
||
.map((issue) => (
|
||
<div key={issue} className="text-[11px] leading-5 text-foreground/85">
|
||
· {issue}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|