From 87044a747b47bd83249d61a45842c7f7b2eae56d Mon Sep 17 00:00:00 2001 From: soryu Date: Sun, 11 Jan 2026 05:52:14 +0000 Subject: Contract system --- .../src/components/contracts/PhaseHint.tsx | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 makima/frontend/src/components/contracts/PhaseHint.tsx (limited to 'makima/frontend/src/components/contracts/PhaseHint.tsx') diff --git a/makima/frontend/src/components/contracts/PhaseHint.tsx b/makima/frontend/src/components/contracts/PhaseHint.tsx new file mode 100644 index 0000000..95573ed --- /dev/null +++ b/makima/frontend/src/components/contracts/PhaseHint.tsx @@ -0,0 +1,90 @@ +import type { ContractPhase, ContractWithRelations } from "../../lib/api"; + +interface PhaseHintProps { + contract: ContractWithRelations; + onAdvancePhase: (phase: ContractPhase) => void; +} + +const phaseOrder: ContractPhase[] = ["research", "specify", "plan", "execute", "review"]; + +interface HintConfig { + condition: (contract: ContractWithRelations) => boolean; + message: (contract: ContractWithRelations) => string; + nextPhase: ContractPhase; +} + +const phaseHints: Record = { + research: { + condition: (c) => c.files.length >= 1, + message: (c) => + `You have ${c.files.length} file${c.files.length === 1 ? "" : "s"}. Ready to specify requirements?`, + nextPhase: "specify", + }, + specify: { + condition: (c) => c.files.length >= 2, + message: () => "Spec files ready. Create implementation plan?", + nextPhase: "plan", + }, + plan: { + condition: (c) => c.files.length >= 1 && c.repositories.length >= 1, + message: () => "Plan documented. Ready to create tasks?", + nextPhase: "execute", + }, + execute: { + condition: (c) => { + // Show hint only when all tasks are complete + const doneTasks = c.tasks.filter( + (t) => t.status === "done" || t.status === "merged" + ); + return c.tasks.length > 0 && doneTasks.length === c.tasks.length; + }, + message: (c) => { + const doneTasks = c.tasks.filter( + (t) => t.status === "done" || t.status === "merged" + ); + return `All ${doneTasks.length} task${doneTasks.length === 1 ? "" : "s"} complete. Ready for review?`; + }, + nextPhase: "review", + }, + review: null, // No hint for review phase - it's the final phase +}; + +export function PhaseHint({ contract, onAdvancePhase }: PhaseHintProps) { + const hintConfig = phaseHints[contract.phase]; + + // No hint for this phase + if (!hintConfig) return null; + + // Condition not met + if (!hintConfig.condition(contract)) return null; + + return ( +
+ + {hintConfig.message(contract)} + + +
+ ); +} + +export function getNextPhase(currentPhase: ContractPhase): ContractPhase | null { + const currentIndex = phaseOrder.indexOf(currentPhase); + if (currentIndex === -1 || currentIndex === phaseOrder.length - 1) { + return null; + } + return phaseOrder[currentIndex + 1]; +} + +export function getPreviousPhase(currentPhase: ContractPhase): ContractPhase | null { + const currentIndex = phaseOrder.indexOf(currentPhase); + if (currentIndex <= 0) { + return null; + } + return phaseOrder[currentIndex - 1]; +} -- cgit v1.2.3