From 52d121269195f0e799d0ab4241e4facc3c7c0596 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Mon, 19 Jan 2026 17:55:22 +0000
Subject: Add right-click context menu for contracts on contracts and board
 pages (#8)

Implement a reusable ContractContextMenu component that provides:
- Mark as Complete/Active/Archive status actions (conditionally shown)
- Go to Supervisor Task link (when supervisor exists)
- Delete action with confirmation

Integrate context menu into:
- ContractList.tsx on the contracts page
- WorkflowBoard on the workflow/board page via PhaseColumn and WorkflowContractCard

Features match ElementContextMenu patterns:
- Fixed positioning with z-50
- Click outside and Escape key close handlers
- Viewport overflow prevention
- Dark theme colors (#0a1628, #0d1b2d, #75aafc, #9bc3ff)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 makima/frontend/src/routes/workflow.tsx | 49 +++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

(limited to 'makima/frontend/src/routes/workflow.tsx')

diff --git a/makima/frontend/src/routes/workflow.tsx b/makima/frontend/src/routes/workflow.tsx
index cb72e9e..e122092 100644
--- a/makima/frontend/src/routes/workflow.tsx
+++ b/makima/frontend/src/routes/workflow.tsx
@@ -4,7 +4,7 @@ import { Masthead } from "../components/Masthead";
 import { WorkflowBoard } from "../components/workflow/WorkflowBoard";
 import { useContracts } from "../hooks/useContracts";
 import { useAuth } from "../contexts/AuthContext";
-import type { ContractPhase, ContractStatus } from "../lib/api";
+import type { ContractPhase, ContractStatus, ContractSummary } from "../lib/api";
 
 type StatusFilter = "all" | ContractStatus;
 
@@ -41,7 +41,7 @@ export default function WorkflowPage() {
 
 function WorkflowPageContent() {
   const navigate = useNavigate();
-  const { contracts, loading, error, changePhase, saveContract } = useContracts();
+  const { contracts, loading, error, changePhase, saveContract, editContract, removeContract } = useContracts();
   const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
   const [isCreating, setIsCreating] = useState(false);
   const [newContractName, setNewContractName] = useState("");
@@ -68,6 +68,46 @@ function WorkflowPageContent() {
     [changePhase]
   );
 
+  // Context menu handlers
+  const handleContextMarkComplete = useCallback(
+    async (contract: ContractSummary) => {
+      await editContract(contract.id, { status: "completed", version: contract.version });
+    },
+    [editContract]
+  );
+
+  const handleContextMarkActive = useCallback(
+    async (contract: ContractSummary) => {
+      await editContract(contract.id, { status: "active", version: contract.version });
+    },
+    [editContract]
+  );
+
+  const handleContextArchive = useCallback(
+    async (contract: ContractSummary) => {
+      await editContract(contract.id, { status: "archived", version: contract.version });
+    },
+    [editContract]
+  );
+
+  const handleContextDelete = useCallback(
+    async (contract: ContractSummary) => {
+      if (confirm(`Are you sure you want to delete "${contract.name}"?`)) {
+        await removeContract(contract.id);
+      }
+    },
+    [removeContract]
+  );
+
+  const handleContextGoToSupervisor = useCallback(
+    (contract: ContractSummary) => {
+      if (contract.supervisorTaskId) {
+        navigate(`/mesh/${contract.supervisorTaskId}`);
+      }
+    },
+    [navigate]
+  );
+
   const handleCreateContract = useCallback(async () => {
     if (!newContractName.trim()) return;
     const contract = await saveContract({
@@ -196,6 +236,11 @@ function WorkflowPageContent() {
               contracts={filteredContracts}
               onContractClick={handleContractClick}
               onPhaseChange={handlePhaseChange}
+              onMarkComplete={handleContextMarkComplete}
+              onMarkActive={handleContextMarkActive}
+              onArchive={handleContextArchive}
+              onDelete={handleContextDelete}
+              onGoToSupervisor={handleContextGoToSupervisor}
             />
           )}
         </div>
-- 
cgit v1.2.3

