diff options
Diffstat (limited to 'makima/frontend/src/components/directives/DirectiveDetail.tsx')
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveDetail.tsx | 425 |
1 files changed, 0 insertions, 425 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx deleted file mode 100644 index 6bdf5aa..0000000 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ /dev/null @@ -1,425 +0,0 @@ -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<DirectiveStatus, string> = { - 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 ( - <div> - <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-1"> - {label} - </h4> - <div className="font-mono text-xs text-[#9bb8d8] bg-[rgba(0,0,0,0.2)] p-2 max-h-32 overflow-y-auto"> - {items.map((item, i) => ( - <div key={i} className="mb-0.5"> - {typeof item === "string" ? item : JSON.stringify(item)} - </div> - ))} - </div> - </div> - ); -} - -export function DirectiveDetail({ - directive, - onBack, - onDelete, - onStart, - onRefresh, -}: DirectiveDetailProps) { - const navigate = useNavigate(); - const [activeTab, setActiveTab] = useState<Tab>("overview"); - - // Auto-poll when directive is in an active state - const isLive = - directive.status === "planning" || directive.status === "active"; - const intervalRef = useRef<ReturnType<typeof setInterval> | 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 ( - <div className="panel h-full flex flex-col"> - {/* Header */} - <div className="p-4 border-b border-dashed border-[rgba(117,170,252,0.35)]"> - <div className="flex items-center justify-between mb-3"> - <button - onClick={onBack} - className="font-mono text-xs text-[#75aafc] hover:text-[#9bc3ff] transition-colors" - > - ← Back to list - </button> - <div className="flex items-center gap-2"> - {onStart && directive.status === "draft" && ( - <button - onClick={() => onStart(directive.id)} - className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase" - > - Start - </button> - )} - {onDelete && ( - <button - onClick={() => onDelete(directive.id)} - className="px-3 py-1.5 font-mono text-xs text-red-400 border border-red-400/30 hover:border-red-400/50 transition-colors uppercase" - > - Delete - </button> - )} - </div> - </div> - <div className="flex items-center gap-3 mb-2"> - <h2 className="font-mono text-lg text-[#dbe7ff]"> - {directive.title} - </h2> - <span - className={`font-mono text-xs uppercase ${ - statusColors[directive.status as DirectiveStatus] || "text-[#888]" - }`} - > - {directive.status} - </span> - {isLive && ( - <span className="font-mono text-[9px] text-yellow-400/60 animate-pulse"> - polling - </span> - )} - <span className="font-mono text-[10px] text-[#7788aa]"> - v{directive.version} - </span> - </div> - </div> - - {/* Tabs */} - <div className="flex border-b border-[rgba(117,170,252,0.2)]"> - {tabs.map((tab) => ( - <button - key={tab.key} - onClick={() => setActiveTab(tab.key)} - className={` - px-4 py-2 font-mono text-xs uppercase tracking-wider transition-colors - ${ - activeTab === tab.key - ? "text-[#dbe7ff] border-b-2 border-[#75aafc]" - : "text-[#555] hover:text-[#9bc3ff]" - } - `} - > - {tab.label} - {tab.count != null && tab.count > 0 && ( - <span className="ml-1 text-[10px] text-[#7788aa]"> - ({tab.count}) - </span> - )} - </button> - ))} - </div> - - {/* Tab content */} - <div className="flex-1 overflow-y-auto p-4"> - {activeTab === "overview" && ( - <div className="space-y-4"> - {/* Orchestrator contract link */} - {directive.orchestratorContractId && ( - <div className="flex items-center gap-2 p-2 border border-dashed border-[rgba(117,170,252,0.2)] bg-[rgba(117,170,252,0.03)]"> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - Planning Contract - </span> - {directive.orchestratorContractSummary && ( - <PhaseProgressBarCompact - currentPhase={ - directive.orchestratorContractSummary - .phase as ContractPhase - } - /> - )} - <button - onClick={() => - navigate( - `/contracts/${directive.orchestratorContractId}` - ) - } - className="font-mono text-[11px] text-[#75aafc] hover:text-white transition-colors" - > - {directive.orchestratorContractSummary?.name || - directive.orchestratorContractId.slice(0, 8) + "..."}{" "} - → - </button> - {directive.status === "planning" && ( - <span className="font-mono text-[9px] text-yellow-400 animate-pulse"> - planning in progress - </span> - )} - </div> - )} - - {/* Goal */} - <div> - <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-1"> - Goal - </h4> - <p className="font-mono text-xs text-[#9bb8d8] whitespace-pre-wrap"> - {directive.goal} - </p> - </div> - - {/* Config grid */} - <div className="grid grid-cols-3 gap-2"> - <div> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - Autonomy - </span> - <div className="font-mono text-xs text-[#dbe7ff]"> - {directive.autonomyLevel} - </div> - </div> - <div> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - Chains - </span> - <div className="font-mono text-xs text-[#dbe7ff]"> - {directive.chainGenerationCount} generated - </div> - </div> - <div> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - Cost - </span> - <div className="font-mono text-xs text-[#dbe7ff]"> - ${directive.totalCostUsd.toFixed(2)} - </div> - </div> - {directive.repositoryUrl && ( - <div className="col-span-3"> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - Repository - </span> - <div className="font-mono text-xs text-[#dbe7ff] truncate"> - {directive.repositoryUrl} - </div> - </div> - )} - </div> - - {/* Stat cards */} - <div className="grid grid-cols-3 gap-2"> - <div className="border border-dashed border-[rgba(117,170,252,0.2)] p-2 text-center"> - <div className="font-mono text-lg text-[#dbe7ff]"> - {totalSteps} - </div> - <div className="font-mono text-[9px] text-[#7788aa] uppercase"> - Total Steps - </div> - </div> - <div className="border border-dashed border-[rgba(117,170,252,0.2)] p-2 text-center"> - <div className="font-mono text-lg text-green-400"> - {completedSteps} - </div> - <div className="font-mono text-[9px] text-[#7788aa] uppercase"> - Completed - </div> - </div> - <div className="border border-dashed border-[rgba(117,170,252,0.2)] p-2 text-center"> - <div className="font-mono text-lg text-[#dbe7ff]"> - ${directive.totalCostUsd.toFixed(2)} - </div> - <div className="font-mono text-[9px] text-[#7788aa] uppercase"> - Cost - </div> - </div> - </div> - - {/* Structured sections */} - <JsonSection label="Requirements" data={directive.requirements} /> - <JsonSection - label="Acceptance Criteria" - data={directive.acceptanceCriteria} - /> - <JsonSection label="Constraints" data={directive.constraints} /> - <JsonSection - label="External Dependencies" - data={directive.externalDependencies} - /> - - {/* Metadata */} - <div> - <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-1"> - Metadata - </h4> - <div className="grid grid-cols-2 gap-1 font-mono text-[10px]"> - <span className="text-[#7788aa]">Created</span> - <span className="text-[#9bb8d8]"> - {new Date(directive.createdAt).toLocaleString()} - </span> - <span className="text-[#7788aa]">Updated</span> - <span className="text-[#9bb8d8]"> - {new Date(directive.updatedAt).toLocaleString()} - </span> - {directive.startedAt && ( - <> - <span className="text-[#7788aa]">Started</span> - <span className="text-[#9bb8d8]"> - {new Date(directive.startedAt).toLocaleString()} - </span> - </> - )} - {directive.completedAt && ( - <> - <span className="text-[#7788aa]">Completed</span> - <span className="text-[#9bb8d8]"> - {new Date(directive.completedAt).toLocaleString()} - </span> - </> - )} - <span className="text-[#7788aa]">Version</span> - <span className="text-[#9bb8d8]">{directive.version}</span> - </div> - </div> - </div> - )} - - {activeTab === "chain" && ( - <div className="space-y-4"> - {directive.chains.length === 0 ? ( - <div className="flex flex-col items-center justify-center py-12"> - <div className="font-mono text-[40px] text-[#333] mb-3"> - {directive.status === "planning" ? "\u2699" : "\u25CB"} - </div> - <p className="font-mono text-xs text-[#7788aa] text-center max-w-[300px]"> - {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."} - </p> - </div> - ) : ( - <> - {/* Chain metadata header */} - {directive.chains.map((chain) => ( - <div key={chain.id} className="space-y-3"> - <div className="flex items-center gap-3 pb-2 border-b border-dashed border-[rgba(117,170,252,0.15)]"> - <span className="font-mono text-xs text-[#dbe7ff]"> - {chain.name} - </span> - <span className="font-mono text-[10px] text-[#7788aa] uppercase"> - gen {chain.generation} - </span> - <span className={`font-mono text-[10px] uppercase ${ - chain.status === "completed" ? "text-green-400" : - chain.status === "failed" ? "text-red-400" : - chain.status === "running" ? "text-yellow-400" : - "text-[#7788aa]" - }`}> - {chain.status} - </span> - <span className="font-mono text-[10px] text-[#7788aa] ml-auto"> - {chain.completedSteps}/{chain.totalSteps} steps - {chain.failedSteps > 0 && ( - <span className="text-red-400 ml-1"> - ({chain.failedSteps} failed) - </span> - )} - </span> - </div> - <StepDiagram steps={chain.steps} /> - </div> - ))} - </> - )} - </div> - )} - - {activeTab === "contracts" && ( - <DirectiveContractsTab directive={directive} /> - )} - </div> - </div> - ); -} |
