summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/directives/DirectiveDetail.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/components/directives/DirectiveDetail.tsx')
-rw-r--r--makima/frontend/src/components/directives/DirectiveDetail.tsx425
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"
- >
- &larr; 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) + "..."}{" "}
- &rarr;
- </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>
- );
-}