import { Check, Circle, FileDown, FileSpreadsheet, LoaderCircle, MessageSquareText, Sparkles, TriangleAlert } from "lucide-react"; import type { HarnessEvent } from "@agent-studio/shared"; import { currentRunEvents } from "./analysisProgressState.js"; type StageStatus = "pending" | "running" | "complete" | "error"; const eventLabel: Partial> = { "attachment.uploaded": "报表上传完成", "skill.requested": "正在读取日报分析规则", "skill.loaded": "日报分析规则已加载", "tool.requested": "开始解析广告数据", "tool.completed": "数据解析完成", "tool.failed": "数据解析失败", "assistant.delta": "正在撰写日报结论", "assistant.completed": "日报内容生成完成", "artifact.ready": "Word 报告已生成", "artifact.failed": "Word 报告生成失败", "run.completed": "本轮任务已完成", "run.failed": "本轮任务失败" }; function getEventLabel(event: HarnessEvent) { if (event.type === "tool.requested" && event.payload.toolName === "build_daily_brief") return "正在套用 Skill 日报模板"; if (event.type === "tool.completed" && event.payload.toolName === "build_daily_brief") return "Skill 模板 Word 生成完成"; if (event.type === "tool.failed" && event.payload.toolName === "build_daily_brief") return "Skill 模板 Word 生成失败"; return eventLabel[event.type]; } function has(events: HarnessEvent[], type: HarnessEvent["type"], predicate?: (event: HarnessEvent) => boolean) { return events.some((event) => event.type === type && (!predicate || predicate(event))); } function stageStatus(events: HarnessEvent[], stage: "upload" | "skill" | "analysis" | "report" | "word"): StageStatus { const runEvents = currentRunEvents(events); const failed = has(runEvents, "run.failed"); if (stage === "upload") return has(events, "attachment.uploaded") ? "complete" : runEvents.length ? "running" : "pending"; if (stage === "skill") return has(events, "skill.loaded") ? "complete" : has(runEvents, "skill.requested") ? "running" : failed ? "error" : "pending"; if (stage === "analysis") { if (has(runEvents, "tool.failed", (event) => event.type === "tool.failed" && event.payload.toolName === "analyze_uploaded_report")) return "error"; if (has(runEvents, "tool.completed", (event) => event.type === "tool.completed" && event.payload.toolName === "analyze_uploaded_report")) return "complete"; return has(runEvents, "tool.requested", (event) => event.type === "tool.requested" && event.payload.toolName === "analyze_uploaded_report") ? "running" : "pending"; } if (stage === "report") return has(runEvents, "tool.requested", (event) => event.type === "tool.requested" && event.payload.toolName === "build_daily_brief") ? "complete" : has(runEvents, "assistant.delta") || has(runEvents, "tool.completed", (event) => event.type === "tool.completed" && event.payload.toolName === "analyze_uploaded_report") ? "running" : "pending"; if (has(runEvents, "artifact.failed")) return "error"; if (has(runEvents, "artifact.ready")) return "complete"; return has(runEvents, "tool.requested", (event) => event.type === "tool.requested" && event.payload.toolName === "build_daily_brief") ? "running" : "pending"; } const stages = [ { id: "upload" as const, label: "接收广告报表", detail: "Excel / CSV 数据", Icon: FileSpreadsheet }, { id: "skill" as const, label: "加载分析规则", detail: "daily-campaign-triage", Icon: Sparkles }, { id: "analysis" as const, label: "解析与计算", detail: "指标、异常与趋势", Icon: LoaderCircle }, { id: "report" as const, label: "生成日报", detail: "结论与行动建议", Icon: MessageSquareText }, { id: "word" as const, label: "输出 Word", detail: "可下载报告文件", Icon: FileDown } ]; export function AnalysisProgress({ events, running, reconnecting }: { events: HarnessEvent[]; running: boolean; reconnecting: boolean }) { const recent = events.filter((event) => getEventLabel(event)).slice(-6).reverse(); return ; }