import { DndContext, PointerSensor, useSensor, useSensors, closestCorners, type DragEndEvent, useDroppable, useDraggable, } from "@dnd-kit/core"; import type { Lane, ProjectTask } from "@/types"; import { LANE_LABELS, formatDate, classNames } from "@/lib/format"; const LANES: Lane[] = ["todo", "doing", "review", "done"]; const LANE_DOT: Record = { todo: "#98A2B3", doing: "#2E90FA", review: "#7A5AF8", done: "#12B76A", }; export function Kanban({ tasks, onMove, onCardClick, readOnly, }: { tasks: ProjectTask[]; onMove: (taskId: string, lane: Lane) => void; onCardClick?: (t: ProjectTask) => void; readOnly?: boolean }) { const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } })); function onDragEnd(e: DragEndEvent) { const lane = e.over?.id as Lane | undefined; const taskId = e.active.id as string; if (lane && LANES.includes(lane)) { const t = tasks.find((x) => x.id === taskId); if (t && t.lane !== lane) onMove(taskId, lane); } } return (
{LANES.map((lane) => ( t.lane === lane)} onCardClick={onCardClick} readOnly={readOnly} /> ))}
); } function Column({ lane, tasks, onCardClick, readOnly }: { lane: Lane; tasks: ProjectTask[]; onCardClick?: (t: ProjectTask) => void; readOnly?: boolean }) { const { setNodeRef, isOver } = useDroppable({ id: lane }); return (
{LANE_LABELS[lane]} {tasks.length}
{tasks.map((t) => )} {tasks.length === 0 &&
λΉ„μ–΄ 있음
}
); } function KanbanCard({ task, onCardClick, readOnly }: { task: ProjectTask; onCardClick?: (t: ProjectTask) => void; readOnly?: boolean }) { const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id: task.id, disabled: readOnly }); const style = transform ? { transform: `translate(${transform.x}px, ${transform.y}px)` } : undefined; return (
{ if (!isDragging) onCardClick?.(task); }} className={classNames( "bg-surface border border-border rounded-control p-3 shadow-card", readOnly ? "" : "cursor-pointer active:cursor-grabbing", isDragging && "opacity-60" )} >
{task.title}
{formatDate(task.start)} {task.assignee ? task.assignee.split("@")[0] : ""}
{task.progress > 0 && (
)}
); }