import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router"; import type { DirectiveWithChains, DirectiveStatus, 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", }; 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" && (
{directive.chains.length === 0 ? (
{directive.status === "planning" ? "\u2699" : "\u25CB"}

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

) : ( <> {/* Chain metadata header */} {directive.chains.map((chain) => (
{chain.name} gen {chain.generation} {chain.status} {chain.completedSteps}/{chain.totalSteps} steps {chain.failedSteps > 0 && ( ({chain.failedSteps} failed) )}
))} )}
)} {activeTab === "contracts" && ( )}
); }