import { useState } from "react";
import { ShieldCheck, Check, X } from "lucide-react";
import { pendingApproval, type RunState } from "../lib/run";
import { approveTask } from "../lib/api";
import { Badge } from "../ui";
// ApprovalBar:HITL 人工审批中断条 —— 全局常驻(任务停在审批节点时,不管在哪个页面都要能看见+操作)。
// 触发以「后端状态 waiting」为准(落 PG,可靠);exec 的 await 事件仅用于丰富摘要(可能抢跑丢失)。
// 决定经 approveTask 发回;dispatcher 续跑后状态轮询/SSE 更新,本条随 waiting 解除自动消失。
export function ApprovalBar({ run }: { run: RunState }) {
const approval =
run.taskId && run.lifecycle === "waiting"
? pendingApproval(run.exec) ?? { node: "", title: run.detail || "人工审批", summary: "" }
: null;
if (!approval || !run.taskId) return null;
// key=taskId:换任务时重新挂载,重置 submitted 等本地状态,避免上一个审批的「已提交」残留。
return ;
}
function Bar({ taskId, node, title, summary }: { taskId: string; node: string; title: string; summary: string }) {
const [note, setNote] = useState("");
const [busy, setBusy] = useState<"approve" | "reject" | null>(null);
const [submitted, setSubmitted] = useState(false); // 决定已发出;正常情况下任务续跑后本条随 waiting 解除自动消失
const [err, setErr] = useState("");
const decide = async (approved: boolean) => {
setBusy(approved ? "approve" : "reject");
setErr("");
try {
await approveTask(taskId, approved, { node, note });
setSubmitted(true); // 关键:成功后置「已提交」而非停在 spinner —— 否则任务若恢复不了会永远卡「提交中…」
} catch (e) {
setErr((e as Error).message);
} finally {
setBusy(null);
}
};
return (
人工审批
{title}
{submitted ? "已提交决定" : "等待决定"}
{summary &&
{summary}}
{submitted ? (
决定已发出,等待任务续跑…(若长时间无响应,该任务可能已失效)
) : (
setNote(e.target.value)}
placeholder="备注(可选,拒绝原因等)"
className="min-w-0 flex-1 rounded border border-line bg-ink-950/60 px-2 py-1 text-[11px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500/50 focus:outline-none"
/>
)}
{err &&
提交失败:{err}
}
);
}