summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/directives/StepNode.tsx
blob: e54f5eb87fa86a58b990fbfd8d500c6231ab3389 (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
import { Handle, Position } from "@xyflow/react";
import type { StepStatus, ConfidenceLevel, DirectiveGraphNode } from "../../lib/api";

// Step status colors for both list and DAG views
export const stepStatusStyles: Record<StepStatus, { border: string; bg: string; text: string }> = {
  pending: { border: "#556677", bg: "#556677", text: "#556677" },
  ready: { border: "#3b82f6", bg: "#3b82f6", text: "#3b82f6" },
  running: { border: "#22c55e", bg: "#22c55e", text: "#22c55e" },
  evaluating: { border: "#eab308", bg: "#eab308", text: "#eab308" },
  passed: { border: "#75aafc", bg: "#75aafc", text: "#75aafc" },
  failed: { border: "#ef4444", bg: "#ef4444", text: "#ef4444" },
  rework: { border: "#f97316", bg: "#f97316", text: "#f97316" },
  skipped: { border: "#556677", bg: "#556677", text: "#556677" },
  blocked: { border: "#ef4444", bg: "#ef4444", text: "#ef4444" },
};

// Confidence level colors
export const confidenceColors: Record<ConfidenceLevel, string> = {
  green: "#22c55e",
  yellow: "#eab308",
  red: "#ef4444",
};

// Node dimensions
export const NODE_WIDTH = 180;
export const NODE_HEIGHT = 70;

// Custom node component for steps
export function StepNodeComponent({ data }: { data: DirectiveGraphNode & { selected?: boolean } }) {
  const styles = stepStatusStyles[data.status] || stepStatusStyles.pending;
  const isRunning = data.status === "running" || data.status === "evaluating";

  return (
    <div
      className={`rounded-lg border-2 bg-[#0a1628] overflow-hidden ${
        isRunning ? "animate-pulse" : ""
      }`}
      style={{
        width: NODE_WIDTH,
        height: NODE_HEIGHT,
        borderColor: styles.border,
        borderStyle: data.status === "pending" ? "dashed" : "solid",
      }}
    >
      <Handle
        type="target"
        position={Position.Top}
        className="!bg-[#75aafc] !w-3 !h-3 !border-2 !border-[#0a1628]"
      />

      {/* Status indicator bar */}
      <div className="h-1.5" style={{ backgroundColor: styles.bg }} />

      {/* Content */}
      <div className="p-2">
        <div className="flex items-center justify-between mb-1">
          <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">{data.name}</span>
          {data.confidenceScore !== null && data.confidenceLevel && (
            <div
              className="w-2 h-2 rounded-full flex-shrink-0 ml-1"
              style={{ backgroundColor: confidenceColors[data.confidenceLevel] }}
              title={`Confidence: ${Math.round(data.confidenceScore * 100)}%`}
            />
          )}
        </div>
        <div className="flex items-center justify-between">
          <span
            className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
            style={{
              color: styles.text,
              backgroundColor: `${styles.bg}20`,
            }}
          >
            {data.status}
          </span>
          <span className="font-mono text-[10px] text-[#8b949e]">{data.stepType}</span>
        </div>
      </div>

      <Handle
        type="source"
        position={Position.Bottom}
        className="!bg-[#f59e0b] !w-3 !h-3 !border-2 !border-[#0a1628]"
      />
    </div>
  );
}