From 6364363d1418728351f252b799d397b756e1f985 Mon Sep 17 00:00:00 2001 From: soryu Date: Sat, 24 Jan 2026 20:06:28 +0000 Subject: feat: Simplify contract deliverables and add Templates UI ## Backend Changes ### Phase Deliverables Simplified - **Simple contract type**: - Plan phase: Only 'Plan' deliverable (required) - Execute phase: Only 'PR' deliverable (required) - **Specification contract type**: - Research phase: Only 'Research Notes' deliverable (required) - Specify phase: Only 'Requirements Document' deliverable (required) - Plan phase: Only 'Plan' deliverable (required) - Execute phase: Only 'PR' deliverable (required) - Review phase: Only 'Release Notes' deliverable (required) ### New 'execute' Contract Type - Only has 'execute' phase (no plan or review phases) - NO deliverables at all - executes tasks directly - Added to ContractType enum with proper Display/FromStr implementations - Added helper methods: `initial_phase()`, `terminal_phase()` ### API Updates - Added `get_phase_deliverables_for_type()` for contract-type-aware deliverables - Added `get_phase_checklist_for_type()` for contract-type-aware checklists - Added `check_phase_completion_for_type()` for contract-type-aware completion checks - Added `check_deliverables_met()` function for deliverable validation - Added `should_auto_progress()` for autonomous contract progression - Added new ContractToolRequest::CheckDeliverablesMet tool ## Frontend Changes (makima/frontend) ### Templates Page - Add TemplateEditor component for editing phase deliverables - Create Templates page with template card grid layout - Add navigation link in NavStrip - Implement three built-in templates: Simple, Specification, Execute - Support for creating custom templates with configurable phases/deliverables - Templates are persisted to localStorage Co-Authored-By: Claude Opus 4.5 --- makima/frontend/src/routes/templates.tsx | 268 +++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 makima/frontend/src/routes/templates.tsx (limited to 'makima/frontend/src/routes/templates.tsx') diff --git a/makima/frontend/src/routes/templates.tsx b/makima/frontend/src/routes/templates.tsx new file mode 100644 index 0000000..ce944a8 --- /dev/null +++ b/makima/frontend/src/routes/templates.tsx @@ -0,0 +1,268 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router"; +import { Masthead } from "../components/Masthead"; +import { TemplateEditor } from "../components/templates/TemplateEditor"; +import { useAuth } from "../contexts/AuthContext"; +import type { ContractTemplate } from "../types/templates"; +import { DEFAULT_TEMPLATES } from "../types/templates"; + +const STORAGE_KEY = "makima_contract_templates"; + +export default function TemplatesPage() { + const navigate = useNavigate(); + const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth(); + + const [templates, setTemplates] = useState(() => { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + try { + return JSON.parse(saved); + } catch { + return DEFAULT_TEMPLATES; + } + } + return DEFAULT_TEMPLATES; + }); + + const [editingTemplate, setEditingTemplate] = useState( + null + ); + const [showNewTemplateForm, setShowNewTemplateForm] = useState(false); + const [newTemplateName, setNewTemplateName] = useState(""); + const [newTemplateDescription, setNewTemplateDescription] = useState(""); + + // Redirect to login if not authenticated + useEffect(() => { + if (!authLoading && isAuthConfigured && !isAuthenticated) { + navigate("/login"); + } + }, [authLoading, isAuthConfigured, isAuthenticated, navigate]); + + const saveTemplates = (newTemplates: ContractTemplate[]) => { + setTemplates(newTemplates); + localStorage.setItem(STORAGE_KEY, JSON.stringify(newTemplates)); + }; + + const handleSaveTemplate = (updatedTemplate: ContractTemplate) => { + const newTemplates = templates.map((t) => + t.id === updatedTemplate.id ? updatedTemplate : t + ); + saveTemplates(newTemplates); + setEditingTemplate(null); + }; + + const handleCreateTemplate = () => { + if (!newTemplateName.trim()) return; + + const newTemplate: ContractTemplate = { + id: `custom-${Date.now()}`, + name: newTemplateName.trim(), + description: newTemplateDescription.trim() || "Custom contract template", + isBuiltIn: false, + phases: [ + { + id: `phase-${Date.now()}`, + name: "Execute", + deliverables: [], + }, + ], + }; + + saveTemplates([...templates, newTemplate]); + setNewTemplateName(""); + setNewTemplateDescription(""); + setShowNewTemplateForm(false); + }; + + const handleDeleteTemplate = (templateId: string) => { + const template = templates.find((t) => t.id === templateId); + if (template?.isBuiltIn) return; + + if (window.confirm(`Are you sure you want to delete "${template?.name}"?`)) { + saveTemplates(templates.filter((t) => t.id !== templateId)); + } + }; + + const handleResetToDefaults = () => { + if ( + window.confirm( + "Reset all templates to defaults? This will remove any custom templates." + ) + ) { + saveTemplates(DEFAULT_TEMPLATES); + } + }; + + // Show loading state + if (authLoading) { + return ( +
+
+ Loading... +
+
+ ); + } + + // Editor view + if (editingTemplate) { + return ( +
+ +
+ setEditingTemplate(null)} + /> +
+
+ ); + } + + return ( +
+ +
+ {/* Header */} +
+
+

+ Contract Templates +

+

+ Manage contract types and their phase deliverables +

+
+
+ + +
+
+ + {/* New Template Form */} + {showNewTemplateForm && ( +
+
+ setNewTemplateName(e.target.value)} + /> + setNewTemplateDescription(e.target.value)} + /> + + +
+
+ )} + + {/* Templates Grid */} +
+ {templates.map((template) => ( +
+ {/* Card Header */} +
+

{template.name}

+ {template.isBuiltIn ? ( + + Built-in + + ) : ( + + Custom + + )} +
+ + {/* Card Body */} +
+

+ {template.description} +

+ + {/* Phases */} +
+ {template.phases.map((phase, index) => ( +
+
+ + {index < template.phases.length - 1 && ( + + )} +
+
+ + {phase.name} + + + {phase.deliverables.length === 0 + ? "(no deliverables)" + : phase.deliverables.map((d) => d.name).join(", ")} + +
+
+ ))} +
+
+ + {/* Card Footer */} +
+ + {!template.isBuiltIn && ( + + )} +
+
+ ))} +
+
+
+ ); +} -- cgit v1.2.3