summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/workflow/PhaseColumn.tsx
blob: 277b04cbe252f7c255baafe8794be46fafb26c9c (plain) (blame)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { useState } from "react";
import type { ContractSummary, ContractPhase } from "../../lib/api";
import { WorkflowContractCard } from "./WorkflowContractCard";

interface PhaseColumnProps {
  phase: ContractPhase;
  contracts: ContractSummary[];
  onContractClick: (contractId: string) => void;
  onDrop: (contractId: string, phase: ContractPhase) => void;
  onContextMenu?: (e: React.MouseEvent, contract: ContractSummary) => void;
}

const phaseConfig: Record<
  ContractPhase,
  { label: string; color: string; bgColor: string; borderColor: string }
> = {
  research: {
    label: "Research",
    color: "text-purple-400",
    bgColor: "bg-purple-400/10",
    borderColor: "border-purple-400/30",
  },
  specify: {
    label: "Specify",
    color: "text-blue-400",
    bgColor: "bg-blue-400/10",
    borderColor: "border-blue-400/30",
  },
  plan: {
    label: "Plan",
    color: "text-cyan-400",
    bgColor: "bg-cyan-400/10",
    borderColor: "border-cyan-400/30",
  },
  execute: {
    label: "Execute",
    color: "text-yellow-400",
    bgColor: "bg-yellow-400/10",
    borderColor: "border-yellow-400/30",
  },
  review: {
    label: "Review",
    color: "text-green-400",
    bgColor: "bg-green-400/10",
    borderColor: "border-green-400/30",
  },
};

export function PhaseColumn({
  phase,
  contracts,
  onContractClick,
  onDrop,
  onContextMenu,
}: PhaseColumnProps) {
  const [isDragOver, setIsDragOver] = useState(false);
  const config = phaseConfig[phase];

  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragOver(true);
  };

  const handleDragLeave = () => {
    setIsDragOver(false);
  };

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragOver(false);
    const contractId = e.dataTransfer.getData("contractId");
    if (contractId) {
      onDrop(contractId, phase);
    }
  };

  return (
    <div
      className={`
        flex flex-col min-w-[220px] flex-1 border border-[rgba(117,170,252,0.15)]
        ${isDragOver ? "bg-[rgba(117,170,252,0.05)]" : "bg-transparent"}
        transition-colors
      `}
      onDragOver={handleDragOver}
      onDragLeave={handleDragLeave}
      onDrop={handleDrop}
    >
      {/* Column header */}
      <div
        className={`
          p-3 border-b ${config.borderColor} ${config.bgColor}
          flex items-center justify-between
        `}
      >
        <span className={`font-mono text-xs uppercase tracking-wider ${config.color}`}>
          {config.label}
        </span>
        <span className="font-mono text-[10px] text-[#555]">
          ({contracts.length})
        </span>
      </div>

      {/* Cards container */}
      <div className="flex-1 overflow-y-auto p-2 space-y-2">
        {contracts.length === 0 ? (
          <div className="p-4 text-center font-mono text-[10px] text-[#555]">
            No contracts
          </div>
        ) : (
          contracts.map((contract) => (
            <WorkflowContractCard
              key={contract.id}
              contract={contract}
              onClick={() => onContractClick(contract.id)}
              onDragStart={(e) => {
                e.dataTransfer.setData("contractId", contract.id);
                e.dataTransfer.effectAllowed = "move";
              }}
              onContextMenu={onContextMenu ? (e) => onContextMenu(e, contract) : undefined}
            />
          ))
        )}
      </div>
    </div>
  );
}