From 97e21c8296ec5f91912d56980ebf3b18a1ca3507 Mon Sep 17 00:00:00 2001 From: soryu Date: Sat, 7 Feb 2026 18:27:54 +0000 Subject: Add directive monitor contracts --- .../src/components/directives/DirectiveDetail.tsx | 393 +++++++++++++++------ 1 file changed, 288 insertions(+), 105 deletions(-) (limited to 'makima/frontend/src/components/directives/DirectiveDetail.tsx') diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index 094cdf2..95dc7cc 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -1,12 +1,16 @@ -import { useEffect, useRef } from "react"; +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; @@ -16,6 +20,8 @@ interface DirectiveDetailProps { onRefresh?: (updated: DirectiveWithChains) => void; } +type Tab = "overview" | "chain" | "contracts"; + const statusColors: Record = { draft: "text-[#888]", planning: "text-yellow-400", @@ -36,17 +42,18 @@ const stepStatusColors: Record = { const stepStatusIcons: Record = { pending: "\u25CB", // ○ running: "\u25D4", // ◔ - passed: "\u25CF", // ● - failed: "\u2715", // ✕ + 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}
@@ -57,21 +64,41 @@ function StepRow({ step }: { step: ChainStep }) { {step.status}
+ {summary && ( +
+ + + {summary.tasksDone}/{summary.taskCount} tasks + + {step.contractId && ( + + )} +
+ )} + {!summary && step.contractId && ( + + )} {step.description && ( -

+

{step.description}

)}
- {step.contractId && ( - - )}
); } @@ -84,7 +111,9 @@ function ChainCard({ chainWithSteps }: { chainWithSteps: ChainWithSteps }) {
- {chain.name} + + {chain.name} + gen {chain.generation} · {chain.status} @@ -102,7 +131,9 @@ function ChainCard({ chainWithSteps }: { chainWithSteps: ChainWithSteps }) { {chain.failedSteps} failed )} {chain.currentConfidence != null && ( - confidence: {(chain.currentConfidence * 100).toFixed(0)}% + + confidence: {(chain.currentConfidence * 100).toFixed(0)}% + )}
@@ -151,6 +182,7 @@ export function DirectiveDetail({ onRefresh, }: DirectiveDetailProps) { const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState("overview"); // Auto-poll when directive is in an active state const isLive = @@ -185,6 +217,31 @@ export function DirectiveDetail({ }; }, [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 */} @@ -235,109 +292,235 @@ export function DirectiveDetail({
- {/* Content */} -
- {/* Orchestrator contract link */} - {directive.orchestratorContractId && ( -
- - Planning Contract - - - {directive.status === "planning" && ( - - planning in progress + `} + > + {tab.label} + {tab.count != null && tab.count > 0 && ( + + ({tab.count}) )} -
- )} + + ))} +
- {/* Goal */} -
-

- Goal -

-

- {directive.goal} -

-
+ {/* Tab content */} +
+ {activeTab === "overview" && ( +
+ {/* Orchestrator contract link */} + {directive.orchestratorContractId && ( +
+ + Planning Contract + + {directive.orchestratorContractSummary && ( + + )} + + {directive.status === "planning" && ( + + planning in progress + + )} +
+ )} - {/* Config */} -
-
- - Autonomy - -
- {directive.autonomyLevel} + {/* Goal */} +
+

+ Goal +

+

+ {directive.goal} +

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

+ 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}
- )} -
+
+ )} - {/* Structured sections */} - - - - + {activeTab === "chain" && ( +
+ {/* Step diagram */} + {directive.chains.length > 0 && ( +
+

+ Step Dependencies +

+ c.steps)} + /> +
+ )} - {/* 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) => ( - - ))} + {/* 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" && ( + + )}
); -- cgit v1.2.3