diff options
| author | soryu <soryu@soryu.co> | 2026-01-25 02:55:45 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-25 02:55:45 +0000 |
| commit | b58a7a92f86784620bd5eb214d7f191c6f68f4d3 (patch) | |
| tree | c927ff7a2796de8f92cd7b8ebcf2a79c63c75901 /makima/frontend/src/routes/contracts.tsx | |
| parent | 579c983d3efb8f1414ffb45b9e031f741cce5f76 (diff) | |
| download | soryu-makima/task-task-418149db-418149db.tar.gz soryu-makima/task-task-418149db-418149db.zip | |
[WIP] Heartbeat checkpoint - 2026-01-25 02:55:45 UTCmakima/task-task-418149db-418149db
Diffstat (limited to 'makima/frontend/src/routes/contracts.tsx')
| -rw-r--r-- | makima/frontend/src/routes/contracts.tsx | 121 |
1 files changed, 82 insertions, 39 deletions
diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx index 6acda29..aa5bf3d 100644 --- a/makima/frontend/src/routes/contracts.tsx +++ b/makima/frontend/src/routes/contracts.tsx @@ -6,7 +6,12 @@ import { ContractDetail } from "../components/contracts/ContractDetail"; import { DirectoryInput } from "../components/mesh/DirectoryInput"; import { useContracts } from "../hooks/useContracts"; import { useAuth } from "../contexts/AuthContext"; -import { createTask, getDaemonDirectories, getRepositorySuggestions } from "../lib/api"; +import { + createTask, + getDaemonDirectories, + getRepositorySuggestions, + listContractTypes, +} from "../lib/api"; import type { ContractWithRelations, ContractSummary, @@ -17,8 +22,8 @@ import type { RepositorySourceType, DaemonDirectory, RepositoryHistoryEntry, + ContractTypeTemplate, } from "../lib/api"; -import { getValidPhases, getDefaultPhase } from "../lib/api"; export default function ContractsPage() { const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth(); @@ -85,6 +90,43 @@ function ContractsPageContent() { const [suggestedDirectories, setSuggestedDirectories] = useState<DaemonDirectory[]>([]); const [repoSuggestions, setRepoSuggestions] = useState<RepositoryHistoryEntry[]>([]); const [showRepoSuggestions, setShowRepoSuggestions] = useState(false); + const [contractTypes, setContractTypes] = useState<ContractTypeTemplate[]>([]); + const [contractTypesLoading, setContractTypesLoading] = useState(false); + + // Fetch contract types when modal opens + useEffect(() => { + if (isCreating) { + setContractTypesLoading(true); + listContractTypes() + .then((res) => { + setContractTypes(res.types); + setContractTypesLoading(false); + }) + .catch((err) => { + console.error("Failed to fetch contract types:", err); + // Fall back to built-in types + setContractTypes([ + { + id: "simple", + name: "Simple", + description: "Plan \u2192 Execute: Simple workflow with a plan document", + phases: ["plan", "execute"], + defaultPhase: "plan", + isBuiltin: true, + }, + { + id: "specification", + name: "Specification", + description: "Research \u2192 Specify \u2192 Plan \u2192 Execute \u2192 Review: Full specification-driven development with TDD", + phases: ["research", "specify", "plan", "execute", "review"], + defaultPhase: "research", + isBuiltin: true, + }, + ]); + setContractTypesLoading(false); + }); + } + }, [isCreating]); // Fetch repository suggestions when modal opens and repo type changes useEffect(() => { @@ -170,11 +212,15 @@ function ContractsPageContent() { setCreateError(null); + // Get default phase from contract types or fall back to static function + const selectedType = contractTypes.find((t) => t.id === contractType); + const defaultPhaseForType = selectedType?.defaultPhase || (contractType === "simple" ? "plan" : "research"); + const data: CreateContractRequest = { name: newContractName.trim(), description: newContractDescription.trim() || undefined, contractType: contractType, - initialPhase: initialPhase !== getDefaultPhase(contractType) ? initialPhase : undefined, + initialPhase: initialPhase !== defaultPhaseForType ? initialPhase : undefined, }; try { @@ -224,6 +270,7 @@ function ContractsPageContent() { newContractName, newContractDescription, contractType, + contractTypes, initialPhase, repoType, repoName, @@ -514,41 +561,37 @@ function ContractsPageContent() { <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1"> Contract Type </label> - <div className="flex gap-2"> - <button - type="button" - onClick={() => { - setContractType("simple"); - setInitialPhase("plan"); - }} - className={`flex-1 px-3 py-2 font-mono text-xs uppercase transition-colors ${ - contractType === "simple" - ? "bg-[#0f3c78] text-[#dbe7ff] border border-[#75aafc]" - : "bg-[#0d1b2d] text-[#8b949e] border border-[#3f6fb3] hover:border-[#75aafc]" - }`} - > - Simple - </button> - <button - type="button" - onClick={() => { - setContractType("specification"); - setInitialPhase("research"); - }} - className={`flex-1 px-3 py-2 font-mono text-xs uppercase transition-colors ${ - contractType === "specification" - ? "bg-[#0f3c78] text-[#dbe7ff] border border-[#75aafc]" - : "bg-[#0d1b2d] text-[#8b949e] border border-[#3f6fb3] hover:border-[#75aafc]" - }`} - > - Specification - </button> - </div> - <p className="mt-1 font-mono text-xs text-[#8b949e]"> - {contractType === "simple" - ? "Plan → Execute: Simple workflow with a plan document" - : "Research → Specify → Plan → Execute → Review: Full specification-driven development with TDD"} - </p> + {contractTypesLoading ? ( + <div className="flex items-center justify-center py-4"> + <span className="font-mono text-xs text-[#8b949e]">Loading contract types...</span> + </div> + ) : ( + <> + <div className="flex gap-2"> + {contractTypes.map((type) => ( + <button + key={type.id} + type="button" + onClick={() => { + setContractType(type.id as ContractType); + setInitialPhase(type.defaultPhase as ContractPhase); + }} + className={`flex-1 px-3 py-2 font-mono text-xs uppercase transition-colors ${ + contractType === type.id + ? "bg-[#0f3c78] text-[#dbe7ff] border border-[#75aafc]" + : "bg-[#0d1b2d] text-[#8b949e] border border-[#3f6fb3] hover:border-[#75aafc]" + }`} + > + {type.name} + </button> + ))} + </div> + <p className="mt-1 font-mono text-xs text-[#8b949e]"> + {contractTypes.find((t) => t.id === contractType)?.description || + "Select a contract type"} + </p> + </> + )} </div> {/* Starting Phase */} @@ -561,7 +604,7 @@ function ContractsPageContent() { onChange={(e) => setInitialPhase(e.target.value as ContractPhase)} className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]" > - {getValidPhases(contractType).map((phase) => ( + {(contractTypes.find((t) => t.id === contractType)?.phases || []).map((phase) => ( <option key={phase} value={phase}> {phase.charAt(0).toUpperCase() + phase.slice(1)} </option> |
