import { useEffect, useRef } from "react"; import { useNavigate } from "react-router"; import type { DirectiveWithChains, DirectiveStatus, ChainWithSteps, ChainStep, } from "../../lib/api"; import { getDirective } from "../../lib/api"; interface DirectiveDetailProps { directive: DirectiveWithChains; onBack: () => void; onDelete?: (id: string) => void; onStart?: (id: string) => void; onRefresh?: (updated: DirectiveWithChains) => void; } const statusColors: Record = { draft: "text-[#888]", planning: "text-yellow-400", active: "text-green-400", paused: "text-orange-400", completed: "text-blue-400", archived: "text-[#555]", failed: "text-red-400", }; const stepStatusColors: Record = { pending: "text-[#888]", running: "text-yellow-400", passed: "text-green-400", failed: "text-red-400", }; const stepStatusIcons: Record = { pending: "\u25CB", // ○ running: "\u25D4", // ◔ passed: "\u25CF", // ● failed: "\u2715", // ✕ }; function StepRow({ step }: { step: ChainStep }) { const navigate = useNavigate(); const color = stepStatusColors[step.status] || "text-[#888]"; const icon = stepStatusIcons[step.status] || "\u25CB"; return (
{icon}
{step.name} {step.status}
{step.description && (

{step.description}

)}
{step.contractId && ( )}
); } function ChainCard({ chainWithSteps }: { chainWithSteps: ChainWithSteps }) { const chain = chainWithSteps; const steps = chainWithSteps.steps || []; return (
{chain.name} gen {chain.generation} · {chain.status}
{chain.description && (

{chain.description}

)}
{chain.completedSteps}/{chain.totalSteps} steps {chain.failedSteps > 0 && ( {chain.failedSteps} failed )} {chain.currentConfidence != null && ( confidence: {(chain.currentConfidence * 100).toFixed(0)}% )}
{steps.length > 0 && (
{steps.map((step) => ( ))}
)}
); } function JsonSection({ label, data, }: { label: string; data: unknown[] | unknown; }) { const items = Array.isArray(data) ? data : []; if (items.length === 0) return null; return (

{label}

{items.map((item, i) => (
{typeof item === "string" ? item : JSON.stringify(item)}
))}
); } export function DirectiveDetail({ directive, onBack, onDelete, onStart, onRefresh, }: DirectiveDetailProps) { const navigate = useNavigate(); // Auto-poll when directive is in an active state const isLive = directive.status === "planning" || directive.status === "active"; const intervalRef = useRef | null>(null); useEffect(() => { if (!isLive) { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } return; } intervalRef.current = setInterval(async () => { try { const updated = await getDirective(directive.id); if (updated && onRefresh) { onRefresh(updated); } } catch { // Ignore poll errors } }, 5000); return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }; }, [isLive, directive.id, onRefresh]); return (
{/* Header */}
{directive.status} {isLive && ( polling )} v{directive.version}
{onStart && directive.status === "draft" && ( )} {onDelete && ( )}

{directive.title}

{/* Content */}
{/* Orchestrator contract link */} {directive.orchestratorContractId && (
Planning Contract {directive.status === "planning" && ( planning in progress )}
)} {/* Goal */}

Goal

{directive.goal}

{/* Config */}
Autonomy
{directive.autonomyLevel}
Chains
{directive.chainGenerationCount} generated
Cost
${directive.totalCostUsd.toFixed(2)}
{directive.repositoryUrl && (
Repository
{directive.repositoryUrl}
)}
{/* Structured sections */} {/* Chains */}

Chains ({directive.chains.length})

{directive.chains.length === 0 ? (

{directive.status === "planning" ? "Planning in progress... chains will appear when the planner completes." : "No chains yet. Chains are created during planning."}

) : (
{directive.chains.map((cws) => ( ))}
)}
); }