All files / src/components/board StageColumn.tsx

0% Statements 0/73
0% Branches 0/1
0% Functions 0/1
0% Lines 0/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                                                                                                                                                                                                                 
import { useDroppable } from "@dnd-kit/core";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import type { BoardStage, TaskRecord } from "../../types";
import { EmptyState } from "../shared/EmptyState";
import { TaskCard } from "./TaskCard";
 
const TERMINAL_STAGE_ID = "done";
 
export function StageColumn({
  stage,
  tasks,
  onEdit,
  onDelete,
  onRun,
}: {
  stage: BoardStage;
  tasks: TaskRecord[];
  onEdit: (task: TaskRecord) => void;
  onDelete: (taskId: string) => void;
  onRun: (taskId: string) => void;
}) {
  const {
    attributes,
    listeners,
    setNodeRef: setSortableRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: `sortable-stage:${stage.id}` });
 
  const { isOver, setNodeRef: setDropRef } = useDroppable({
    id: `stage:${stage.id}`,
    data: { stage: stage.id },
  });
 
  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };
 
  return (
    <section
      ref={setSortableRef}
      style={style}
      className={`board-column${isOver ? " board-column--drop-over" : ""}${isDragging ? " board-column--dragging" : ""}`}
    >
      <header className="board-column__header">
        <div
          className="board-column__drag-handle"
          {...listeners}
          {...attributes}
        >
          <svg
            className="board-column__grip-icon"
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="currentColor"
            aria-hidden="true"
          >
            <circle cx="4" cy="2" r="1.2" />
            <circle cx="8" cy="2" r="1.2" />
            <circle cx="4" cy="6" r="1.2" />
            <circle cx="8" cy="6" r="1.2" />
            <circle cx="4" cy="10" r="1.2" />
            <circle cx="8" cy="10" r="1.2" />
          </svg>
        </div>
        <div className="board-column__heading">
          <span className="board-column__name">{stage.name}</span>
          <div className="board-column__meta">
            {stage.auto_start ? (
              <span className="board-column__label">auto-start</span>
            ) : null}
            {stage.command_id ? (
              <span className="board-column__label">command:{stage.command_id}</span>
            ) : null}
            {stage.profile_id ? (
              <span className="board-column__label">profile:{stage.profile_id}</span>
            ) : null}
          </div>
        </div>
        <span className="board-column__count">{tasks.length}</span>
      </header>
      <div ref={setDropRef} className="board-column__body">
        {tasks.length === 0 ? (
          <EmptyState title="No tasks" />
        ) : (
          tasks.map((task) => (
            <TaskCard
              key={task.task_id}
              task={task}
              onEdit={() => onEdit(task)}
              onDelete={() => onDelete(task.task_id)}
              onRun={() => onRun(task.task_id)}
              canRun={stage.id !== TERMINAL_STAGE_ID}
            />
          ))
        )}
      </div>
    </section>
  );
}