From 6328477bc459eca0243b685553dbd75b925fdc8a Mon Sep 17 00:00:00 2001 From: soryu Date: Mon, 26 Jan 2026 17:03:45 +0000 Subject: Add dynamic contract type templates with user customization (#33) - Add 'execute' contract type as a built-in template in backend - Fix API response field name mismatch (types -> contractTypes) - Remove duplicate ContractTypeTemplate definition in api.ts - Merge built-in types from API with user templates from localStorage - User templates created in the Templates page now appear in contract creation Co-authored-by: Claude Opus 4.5 --- makima/frontend/src/routes/contracts.tsx | 52 +++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) (limited to 'makima/frontend/src/routes') diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx index 4f74692..36eb980 100644 --- a/makima/frontend/src/routes/contracts.tsx +++ b/makima/frontend/src/routes/contracts.tsx @@ -93,19 +93,49 @@ function ContractsPageContent() { const [contractTypes, setContractTypes] = useState([]); const [contractTypesLoading, setContractTypesLoading] = useState(false); - // Fetch contract types when modal opens + // Fetch contract types when modal opens - merges built-in types with user templates useEffect(() => { if (isCreating) { setContractTypesLoading(true); + + // Load user templates from localStorage + const loadUserTemplates = (): ContractTypeTemplate[] => { + try { + const saved = localStorage.getItem("makima_contract_templates"); + if (saved) { + const templates = JSON.parse(saved); + // Convert user templates to ContractTypeTemplate format, excluding built-ins + return templates + .filter((t: { isBuiltIn?: boolean }) => !t.isBuiltIn) + .map((t: { id: string; name: string; description: string; phases: { id: string }[] }) => ({ + id: t.id, + name: t.name, + description: t.description, + phases: t.phases.map((p: { id: string }) => p.id) as ContractPhase[], + defaultPhase: (t.phases[0]?.id || "execute") as ContractPhase, + isBuiltin: false, + })); + } + } catch { + // Ignore localStorage errors + } + return []; + }; + listContractTypes() .then((res) => { - setContractTypes(res.types); + // Merge built-in types from API with user templates from localStorage + const userTemplates = loadUserTemplates(); + // Filter out any user templates that have the same ID as built-in types + const builtinIds = new Set(res.contractTypes.map(t => t.id)); + const uniqueUserTemplates = userTemplates.filter(t => !builtinIds.has(t.id)); + setContractTypes([...res.contractTypes, ...uniqueUserTemplates]); setContractTypesLoading(false); }) .catch((err) => { console.error("Failed to fetch contract types:", err); - // Fall back to built-in types - setContractTypes([ + // Fall back to built-in types + user templates + const builtinTypes: ContractTypeTemplate[] = [ { id: "simple", name: "Simple", @@ -122,7 +152,19 @@ function ContractsPageContent() { defaultPhase: "research", isBuiltin: true, }, - ]); + { + id: "execute", + name: "Execute", + description: "Execute only: Minimal workflow for immediate task execution", + phases: ["execute"], + defaultPhase: "execute", + isBuiltin: true, + }, + ]; + const userTemplates = loadUserTemplates(); + const builtinIds = new Set(builtinTypes.map(t => t.id)); + const uniqueUserTemplates = userTemplates.filter(t => !builtinIds.has(t.id)); + setContractTypes([...builtinTypes, ...uniqueUserTemplates]); setContractTypesLoading(false); }); } -- cgit v1.2.3