import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router"; import type { DirectiveWithChains, DirectiveStatus, ChainWithSteps, ChainStep, ContractPhase, } from "../../lib/api"; import { getDirective } from "../../lib/api"; import { PhaseProgressBarCompact } from "../contracts/PhaseProgressBar"; import { StepDiagram } from "./StepDiagram"; import { DirectiveContractsTab } from "./DirectiveContractsTab"; interface DirectiveDetailProps { directive: DirectiveWithChains; onBack: () => void; onDelete?: (id: string) => void; onStart?: (id: string) => void; onRefresh?: (updated: DirectiveWithChains) => void; } type Tab = "overview" | "chain" | "contracts"; 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"; const summary = step.contractSummary; return (
{icon}
{step.name} {step.status}
{summary && (
{summary.tasksDone}/{summary.taskCount} tasks {step.contractId && ( )}
)} {!summary && step.contractId && ( )} {step.description && (

{step.description}

)}
); } 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(); const [activeTab, setActiveTab] = useState("overview"); // 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]); // Count total steps and completed steps across all chains const totalSteps = directive.chains.reduce( (sum, c) => sum + c.totalSteps, 0 ); const completedSteps = directive.chains.reduce( (sum, c) => sum + c.completedSteps, 0 ); // Count contracts const contractCount = (directive.orchestratorContractSummary ? 1 : 0) + directive.chains.reduce( (sum, c) => sum + c.steps.filter((s) => s.contractSummary != null).length, 0 ); const tabs: { key: Tab; label: string; count?: number }[] = [ { key: "overview", label: "Overview" }, { key: "chain", label: "Chain", count: totalSteps }, { key: "contracts", label: "Contracts", count: contractCount }, ]; return (
{/* Header */}
{onStart && directive.status === "draft" && ( )} {onDelete && ( )}

{directive.title}

{directive.status} {isLive && ( polling )} v{directive.version}
{/* Tabs */}
{tabs.map((tab) => ( ))}
{/* Tab content */}
{activeTab === "overview" && (
{/* Orchestrator contract link */} {directive.orchestratorContractId && (
Planning Contract {directive.orchestratorContractSummary && ( )} {directive.status === "planning" && ( planning in progress )}
)} {/* Goal */}

Goal

{directive.goal}

{/* Config grid */}
Autonomy
{directive.autonomyLevel}
Chains
{directive.chainGenerationCount} generated
Cost
${directive.totalCostUsd.toFixed(2)}
{directive.repositoryUrl && (
Repository
{directive.repositoryUrl}
)}
{/* Stat cards */}
{totalSteps}
Total Steps
{completedSteps}
Completed
${directive.totalCostUsd.toFixed(2)}
Cost
{/* Structured sections */} {/* Metadata */}

Metadata

Created {new Date(directive.createdAt).toLocaleString()} Updated {new Date(directive.updatedAt).toLocaleString()} {directive.startedAt && ( <> Started {new Date(directive.startedAt).toLocaleString()} )} {directive.completedAt && ( <> Completed {new Date(directive.completedAt).toLocaleString()} )} Version {directive.version}
)} {activeTab === "chain" && (
{/* Step diagram */} {directive.chains.length > 0 && (

Step Dependencies

c.steps)} />
)} {/* Chain cards */}

Chains ({directive.chains.length})

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

{directive.status === "planning" ? "Planning in progress... chains will appear when the planner completes." : directive.status === "draft" ? "No chains yet. Start the directive to begin planning." : "No chains created for this directive."}

) : (
{directive.chains.map((cws) => ( ))}
)}
)} {activeTab === "contracts" && ( )}
); }