diff options
| author | soryu <soryu@soryu.co> | 2026-01-15 00:23:44 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-15 00:23:47 +0000 |
| commit | eff0d844ca6e35bfbc2d5fdaa2d2f92177611f2e (patch) | |
| tree | 90d87d6daf9dd78c31e4b816bb1d282db73821dd /makima/frontend/src/routes/contracts.tsx | |
| parent | 87044a747b47bd83249d61a45842c7f7b2eae56d (diff) | |
| download | soryu-eff0d844ca6e35bfbc2d5fdaa2d2f92177611f2e.tar.gz soryu-eff0d844ca6e35bfbc2d5fdaa2d2f92177611f2e.zip | |
Contract type system
Diffstat (limited to 'makima/frontend/src/routes/contracts.tsx')
| -rw-r--r-- | makima/frontend/src/routes/contracts.tsx | 72 |
1 files changed, 62 insertions, 10 deletions
diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx index 8c90804..f09ec5b 100644 --- a/makima/frontend/src/routes/contracts.tsx +++ b/makima/frontend/src/routes/contracts.tsx @@ -11,10 +11,12 @@ import type { ContractWithRelations, ContractPhase, ContractStatus, + ContractType, CreateContractRequest, RepositorySourceType, DaemonDirectory, } from "../lib/api"; +import { getValidPhases, getDefaultPhase } from "../lib/api"; export default function ContractsPage() { const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth(); @@ -71,7 +73,8 @@ function ContractsPageContent() { const [isCreating, setIsCreating] = useState(false); const [newContractName, setNewContractName] = useState(""); const [newContractDescription, setNewContractDescription] = useState(""); - const [initialPhase, setInitialPhase] = useState<ContractPhase>("research"); + const [contractType, setContractType] = useState<ContractType>("simple"); + const [initialPhase, setInitialPhase] = useState<ContractPhase>("plan"); const [repoType, setRepoType] = useState<RepositorySourceType>("remote"); const [repoName, setRepoName] = useState(""); const [repoUrl, setRepoUrl] = useState(""); @@ -136,7 +139,8 @@ function ContractsPageContent() { const data: CreateContractRequest = { name: newContractName.trim(), description: newContractDescription.trim() || undefined, - initialPhase: initialPhase !== "research" ? initialPhase : undefined, + contractType: contractType, + initialPhase: initialPhase !== getDefaultPhase(contractType) ? initialPhase : undefined, }; try { @@ -171,7 +175,8 @@ function ContractsPageContent() { setIsCreating(false); setNewContractName(""); setNewContractDescription(""); - setInitialPhase("research"); + setContractType("simple"); + setInitialPhase("plan"); setRepoType("remote"); setRepoName(""); setRepoUrl(""); @@ -184,6 +189,8 @@ function ContractsPageContent() { }, [ newContractName, newContractDescription, + contractType, + initialPhase, repoType, repoName, repoUrl, @@ -200,7 +207,8 @@ function ContractsPageContent() { setIsCreating(false); setNewContractName(""); setNewContractDescription(""); - setInitialPhase("research"); + setContractType("simple"); + setInitialPhase("plan"); setRepoType("remote"); setRepoName(""); setRepoUrl(""); @@ -424,6 +432,48 @@ function ContractsPageContent() { /> </div> + {/* Contract Type */} + <div> + <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> + </div> + {/* Starting Phase */} <div> <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1"> @@ -434,14 +484,16 @@ 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]" > - <option value="research">Research</option> - <option value="specify">Specify</option> - <option value="plan">Plan</option> - <option value="execute">Execute</option> - <option value="review">Review</option> + {getValidPhases(contractType).map((phase) => ( + <option key={phase} value={phase}> + {phase.charAt(0).toUpperCase() + phase.slice(1)} + </option> + ))} </select> <p className="mt-1 font-mono text-xs text-[#8b949e]"> - Skip earlier phases if you already have requirements defined + {contractType === "simple" + ? "Start in Plan to define what to build, or Execute if already planned" + : "Skip earlier phases if you already have requirements defined"} </p> </div> |
