1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
import { useMemo, useState } from "react";
import type { ContractSummary, ContractPhase } from "../../lib/api";
import { PhaseColumn } from "./PhaseColumn";
import { ContractContextMenu } from "../contracts/ContractContextMenu";
interface WorkflowBoardProps {
contracts: ContractSummary[];
onContractClick: (contractId: string) => void;
onPhaseChange: (contractId: string, newPhase: ContractPhase) => void;
onMarkComplete?: (contract: ContractSummary) => void;
onMarkActive?: (contract: ContractSummary) => void;
onArchive?: (contract: ContractSummary) => void;
onDelete?: (contract: ContractSummary) => void;
onGoToSupervisor?: (contract: ContractSummary) => void;
}
const phases: ContractPhase[] = ["research", "specify", "plan", "execute", "review"];
export function WorkflowBoard({
contracts,
onContractClick,
onPhaseChange,
onMarkComplete,
onMarkActive,
onArchive,
onDelete,
onGoToSupervisor,
}: WorkflowBoardProps) {
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null);
const [contextMenuContract, setContextMenuContract] = useState<ContractSummary | null>(null);
const handleContextMenu = (e: React.MouseEvent, contract: ContractSummary) => {
e.preventDefault();
e.stopPropagation(); // Prevent interference with drag-and-drop
setContextMenuPosition({ x: e.clientX, y: e.clientY });
setContextMenuContract(contract);
};
const closeContextMenu = () => {
setContextMenuPosition(null);
setContextMenuContract(null);
};
// Group contracts by phase
const contractsByPhase = useMemo(() => {
const grouped: Record<ContractPhase, ContractSummary[]> = {
research: [],
specify: [],
plan: [],
execute: [],
review: [],
};
for (const contract of contracts) {
const phase = contract.phase as ContractPhase;
if (grouped[phase]) {
grouped[phase].push(contract);
} else {
// Default to research if unknown phase
grouped.research.push(contract);
}
}
return grouped;
}, [contracts]);
return (
<>
<div className="flex gap-2 h-full overflow-x-auto">
{phases.map((phase) => (
<PhaseColumn
key={phase}
phase={phase}
contracts={contractsByPhase[phase]}
onContractClick={onContractClick}
onDrop={onPhaseChange}
onContextMenu={handleContextMenu}
/>
))}
</div>
{/* Context Menu */}
{contextMenuPosition && contextMenuContract && (
<ContractContextMenu
x={contextMenuPosition.x}
y={contextMenuPosition.y}
contract={contextMenuContract}
onClose={closeContextMenu}
onMarkComplete={() => onMarkComplete?.(contextMenuContract)}
onMarkActive={() => onMarkActive?.(contextMenuContract)}
onArchive={() => onArchive?.(contextMenuContract)}
onDelete={() => onDelete?.(contextMenuContract)}
onGoToSupervisor={() => onGoToSupervisor?.(contextMenuContract)}
/>
)}
</>
);
}
|