From 97e21c8296ec5f91912d56980ebf3b18a1ca3507 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sat, 7 Feb 2026 18:27:54 +0000
Subject: Add directive monitor contracts

---
 .../directives/DirectiveContractsTab.tsx           | 131 +++++++++++++++++++++
 1 file changed, 131 insertions(+)
 create mode 100644 makima/frontend/src/components/directives/DirectiveContractsTab.tsx

(limited to 'makima/frontend/src/components/directives/DirectiveContractsTab.tsx')

diff --git a/makima/frontend/src/components/directives/DirectiveContractsTab.tsx b/makima/frontend/src/components/directives/DirectiveContractsTab.tsx
new file mode 100644
index 0000000..59ebfc8
--- /dev/null
+++ b/makima/frontend/src/components/directives/DirectiveContractsTab.tsx
@@ -0,0 +1,131 @@
+import { useNavigate } from "react-router";
+import type {
+  DirectiveWithChains,
+  StepContractSummary,
+  ContractPhase,
+} from "../../lib/api";
+import { PhaseProgressBarCompact } from "../contracts/PhaseProgressBar";
+
+interface DirectiveContractsTabProps {
+  directive: DirectiveWithChains;
+}
+
+const statusColors: Record<string, string> = {
+  active: "text-green-400",
+  completed: "text-blue-400",
+  archived: "text-[#555]",
+};
+
+function ContractCard({
+  summary,
+  label,
+}: {
+  summary: StepContractSummary;
+  label: string;
+}) {
+  const navigate = useNavigate();
+
+  const progressPct =
+    summary.taskCount > 0
+      ? Math.round((summary.tasksDone / summary.taskCount) * 100)
+      : 0;
+
+  return (
+    <div
+      className="border border-dashed border-[rgba(117,170,252,0.25)] bg-[rgba(117,170,252,0.03)] p-3 cursor-pointer hover:bg-[rgba(117,170,252,0.06)] transition-colors"
+      onClick={() => navigate(`/contracts/${summary.id}`)}
+    >
+      <div className="flex items-center justify-between mb-1.5">
+        <div className="flex items-center gap-2 min-w-0">
+          <span className="font-mono text-[11px] text-[#dbe7ff] truncate">
+            {summary.name}
+          </span>
+          <span className="font-mono text-[9px] text-[#7788aa] uppercase shrink-0">
+            {summary.contractType}
+          </span>
+        </div>
+        <div className="flex items-center gap-2 shrink-0">
+          <span
+            className={`font-mono text-[9px] uppercase ${statusColors[summary.status] || "text-[#888]"}`}
+          >
+            {summary.status}
+          </span>
+          <span className="font-mono text-[9px] text-[#75aafc]">&rarr;</span>
+        </div>
+      </div>
+
+      <div className="flex items-center gap-2 mb-1.5">
+        <span className="font-mono text-[9px] text-[#7788aa] uppercase shrink-0">
+          {label}
+        </span>
+        <PhaseProgressBarCompact
+          currentPhase={summary.phase as ContractPhase}
+        />
+      </div>
+
+      {/* Task progress bar */}
+      <div className="flex items-center gap-2">
+        <div className="flex-1 h-1 bg-[rgba(117,170,252,0.1)] rounded-full overflow-hidden">
+          <div
+            className="h-full bg-[#3f6fb3] rounded-full transition-all"
+            style={{ width: `${progressPct}%` }}
+          />
+        </div>
+        <span className="font-mono text-[9px] text-[#7788aa] shrink-0">
+          {summary.tasksDone}/{summary.taskCount} tasks
+        </span>
+      </div>
+    </div>
+  );
+}
+
+export function DirectiveContractsTab({
+  directive,
+}: DirectiveContractsTabProps) {
+  // Collect all contract summaries
+  const contracts: { summary: StepContractSummary; label: string }[] = [];
+
+  if (directive.orchestratorContractSummary) {
+    contracts.push({
+      summary: directive.orchestratorContractSummary,
+      label: "Planning",
+    });
+  }
+
+  for (const chain of directive.chains) {
+    for (const step of chain.steps) {
+      if (step.contractSummary) {
+        contracts.push({
+          summary: step.contractSummary,
+          label: step.name,
+        });
+      }
+    }
+  }
+
+  if (contracts.length === 0) {
+    return (
+      <div className="text-center py-8">
+        <p className="font-mono text-xs text-[#7788aa]">
+          {directive.status === "draft"
+            ? "No contracts yet. Start the directive to begin planning."
+            : directive.status === "planning"
+              ? "Planning in progress... contracts will appear when steps are created."
+              : "No contracts associated with this directive."}
+        </p>
+      </div>
+    );
+  }
+
+  return (
+    <div className="space-y-2">
+      {contracts.map((c) => (
+        <ContractCard
+          key={c.summary.id}
+          summary={c.summary}
+          label={c.label}
+        />
+      ))}
+    </div>
+  );
+}
-- 
cgit v1.2.3

