import { useEffect, useState } from "react";
import { Eye, Stethoscope, Wrench, FileText, Share2, CheckCircle2, AlertTriangle, XCircle, WifiOff, Twitter, Linkedin } from "lucide-react";
const ICONS = {
inspector: Eye,
diagnostician: Stethoscope,
action: Wrench,
reporter: FileText,
social: Share2,
};
const VERDICT_CONFIG = {
pass: { label: "PASS", color: "#10B981", Icon: CheckCircle2 },
warn: { label: "WARN", color: "#F59E0B", Icon: AlertTriangle },
fail: { label: "FAIL", color: "#ED1C24", Icon: XCircle },
};
// ── Renderers — one per agent role ─────────────────────────────────────────
function InspectorOutput({ parsed, isMock }) {
const vc = VERDICT_CONFIG[parsed?.verdict] || VERDICT_CONFIG.warn;
const defects = parsed?.defects || [];
return (
{isMock && (
AMD server offline — showing demo data. Start the vLLM server for live inference.
)}
{/* Verdict banner */}
CONFIDENCE
{Math.round((parsed?.confidence || 0) * 100)}%
{/* Observation */}
{parsed?.observation && (
Observation
{parsed.observation.replace("[LOCAL MOCK — AMD server offline]", "").trim()}
)}
{/* Defects */}
{defects.length > 0 && (
Detected Defects ({defects.length})
{defects.map((d, i) => (
{d.type}
{d.severity?.toUpperCase()}
{d.location && @ {d.location}}
{d.description}
))}
)}
);
}
function DiagnosticianOutput({ parsed }) {
const factors = parsed?.contributing_factors || [];
return (
{parsed?.probable_cause && (
Root Cause
{parsed.probable_cause.replace("[LOCAL MOCK]", "").trim()}
)}
{parsed?.affected_process_step && (
Affected Process Step
{parsed.affected_process_step}
)}
{factors.length > 0 && (
Contributing Factors
{factors.map((f, i) => (
-
→
{f}
))}
)}
);
}
function ActionOutput({ parsed }) {
const steps = parsed?.steps || [];
const tools = parsed?.parts_or_tools || [];
const priorityColor = { P0: "#ED1C24", P1: "#F97316", P2: "#F59E0B", P3: "#71717A" }[parsed?.priority] || "#71717A";
return (
{parsed?.priority && (
Priority
{parsed.priority}
)}
{parsed?.assignee_role && (
Assign To
{parsed.assignee_role}
)}
{parsed?.estimated_minutes && (
Est. Time
{parsed.estimated_minutes} min
)}
{steps.length > 0 && (
Work Order Steps
{steps.map((s, i) => (
-
{String(i + 1).padStart(2, "0")}.
{s}
))}
)}
{tools.length > 0 && (
Parts / Tools Required
{tools.map((t, i) => {t})}
)}
);
}
function ReporterOutput({ parsed }) {
const tags = parsed?.tags || [];
return (
{parsed?.headline && (
{parsed.headline.replace("[Mock]", "").trim()}
)}
{parsed?.summary && (
{parsed.summary}
)}
{tags.length > 0 && (
{tags.map((t, i) => (
#{t}
))}
)}
);
}
function SocialOutput({ parsed }) {
const xText = parsed?.x_post || "";
const linkedInText = parsed?.linkedin_post || "";
return (
X / Twitter
{xText}
LinkedIn
{linkedInText}
);
}
function AgentContent({ agent, isMock }) {
const { role, output } = agent;
const parsed = output?.parsed || {};
if (role === "inspector") return ;
if (role === "diagnostician") return ;
if (role === "action") return ;
if (role === "reporter") return ;
if (role === "social") return ;
return {JSON.stringify(parsed, null, 2)};
}
// ── Main component ──────────────────────────────────────────────────────────
export default function AgentTranscript({ transcript }) {
const [revealed, setRevealed] = useState(0);
useEffect(() => {
if (!transcript) return;
setRevealed(0);
const agents = transcript.agents || [];
agents.forEach((_, i) => {
setTimeout(() => setRevealed((r) => Math.max(r, i + 1)), i * 400);
});
}, [transcript]);
if (!transcript) return null;
const agents = transcript.agents || [];
// Detect mock mode from first agent's source
const isMock = agents[0]?.output?.source?.includes("mock");
return (
{agents.map((a, idx) => {
const Icon = ICONS[a.role] || Eye;
const isVisible = idx < revealed;
const isActive = idx === revealed - 1;
return (
{/* Agent header */}
{/* Agent body */}
{isVisible && (
)}
);
})}
);
}
function StatusPill({ visible, active }) {
if (!visible) return queued;
if (active) return complete;
return complete;
}