import { useState, useEffect } from "react"; interface StepData { id: string; status: "pending" | "active" | "completed" | "error"; detail?: string; timestamp?: string; } interface StatusResponse { current_step: string | null; steps: StepData[]; started_at: string | null; completed_at: string | null; } interface WorkflowStep { id: string; label: string; icon: JSX.Element; status: "pending" | "active" | "completed" | "error"; detail?: string; } interface WorkflowStatusProps { sessionId?: string; } const STEP_CONFIG: { id: string; label: string; icon: JSX.Element; defaultDetail: string }[] = [ { id: "fetch", label: "Fetch Responses", icon: ( ), defaultDetail: "Getting data from Google Sheets", }, { id: "normalize", label: "Parse Data", icon: ( ), defaultDetail: "Organizing questions and answers", }, { id: "grade", label: "Grade Answers", icon: ( ), defaultDetail: "Comparing with correct answers", }, { id: "explain", label: "Generate Explanations", icon: ( ), defaultDetail: "Explaining wrong answers", }, { id: "group", label: "Create Peer Groups", icon: ( ), defaultDetail: "Matching helpers with learners", }, { id: "report", label: "Generate Report", icon: ( ), defaultDetail: "Creating comprehensive report", }, { id: "email", label: "Send Email", icon: ( ), defaultDetail: "Delivering report to inbox", }, ]; export function WorkflowStatus({ sessionId = "default" }: WorkflowStatusProps) { const [steps, setSteps] = useState( STEP_CONFIG.map(s => ({ ...s, status: "pending" as const, detail: s.defaultDetail })) ); const [isConnected, setIsConnected] = useState(false); const [isAnalyzing, setIsAnalyzing] = useState(false); useEffect(() => { let eventSource: EventSource | null = null; let retryCount = 0; const maxRetries = 3; const connect = () => { eventSource = new EventSource(`/api/status/${sessionId}/stream`); eventSource.onopen = () => { setIsConnected(true); retryCount = 0; }; eventSource.onmessage = (event) => { try { const data: StatusResponse = JSON.parse(event.data); // Check if there's any activity const hasActivity = data.steps && data.steps.length > 0; setIsAnalyzing(hasActivity && !data.completed_at); // Update steps based on server data setSteps(prevSteps => { return prevSteps.map(step => { const serverStep = data.steps?.find(s => s.id === step.id); if (serverStep) { return { ...step, status: serverStep.status as WorkflowStep["status"], detail: serverStep.detail || step.detail, }; } return step; }); }); } catch (e) { console.error("Error parsing status:", e); } }; eventSource.onerror = () => { setIsConnected(false); eventSource?.close(); // Retry connection if (retryCount < maxRetries) { retryCount++; setTimeout(connect, 2000 * retryCount); } }; }; connect(); return () => { eventSource?.close(); }; }, [sessionId]); const getStatusColor = (status: WorkflowStep["status"]) => { switch (status) { case "completed": return "bg-[var(--color-success)] text-white"; case "active": return "bg-[var(--color-accent)] text-white animate-pulse"; case "error": return "bg-red-500 text-white"; default: return "bg-[var(--color-border)] text-[var(--color-text-muted)]"; } }; const getLineColor = (status: WorkflowStep["status"]) => { switch (status) { case "completed": return "bg-[var(--color-success)]"; case "active": return "bg-[var(--color-accent)]"; default: return "bg-[var(--color-border)]"; } }; const activeStep = steps.find(s => s.status === "active"); const completedCount = steps.filter(s => s.status === "completed").length; return (
{/* Header */}

{isAnalyzing ? "AI Agent Working..." : "Agent Status"}

{completedCount > 0 && ( {completedCount}/{steps.length} )}
{/* Active step highlight */} {activeStep && (
{activeStep.label}
{activeStep.detail}
)} {/* Steps list */}
{steps.map((step, index) => (
{/* Connector line */} {index < steps.length - 1 && (
)}
{/* Icon circle */}
{step.status === "completed" ? ( ) : step.status === "active" ? (
) : step.status === "error" ? ( ) : ( {index + 1} )}
{/* Label */}
{step.label}
))}
{/* Connection status */}
{isConnected ? "Live updates" : "Connecting..."}
); }