'use client'; import { AnimatePresence } from 'framer-motion'; import type { DragEvent } from 'react'; import { KANBAN_STATUSES, type KanbanStatus } from '../../lib/kanban'; import type { BeadIssue } from '../../lib/types'; import { KanbanCard } from './kanban-card'; interface KanbanBoardProps { columns: Record<(typeof KANBAN_STATUSES)[number], BeadIssue[]>; selectedIssueId: string | null; pendingIssueIds: Set; activeStatus: KanbanStatus | null; onActivateStatus: (status: KanbanStatus | null) => void; onMoveIssue: (issue: BeadIssue, targetStatus: KanbanStatus) => void; onSelect: (issue: BeadIssue) => void; } const STATUS_META: Record<(typeof KANBAN_STATUSES)[number], { label: string; dot: string }> = { open: { label: 'Open', dot: 'bg-zinc-300' }, in_progress: { label: 'In Progress', dot: 'bg-amber-300' }, blocked: { label: 'Blocked', dot: 'bg-rose-300' }, deferred: { label: 'Deferred', dot: 'bg-stone-400' }, closed: { label: 'Done', dot: 'bg-emerald-300' }, }; const STATUS_COLUMN_CLASS: Record<(typeof KANBAN_STATUSES)[number], string> = { open: 'bg-zinc-500/10', in_progress: 'bg-amber-500/10', blocked: 'bg-rose-500/10', deferred: 'bg-stone-500/10', closed: 'bg-emerald-500/10', }; export function KanbanBoard({ columns, selectedIssueId, pendingIssueIds, activeStatus, onActivateStatus, onMoveIssue, onSelect }: KanbanBoardProps) { const allIssues = KANBAN_STATUSES.flatMap((status) => columns[status]); const issueLookup = new Map(allIssues.map((issue) => [issue.id, issue])); const handleExpandAndSelect = (status: KanbanStatus, issue: BeadIssue) => { onActivateStatus(status); onSelect(issue); }; const onDragStart = (issue: BeadIssue, event: DragEvent) => { event.dataTransfer.setData('application/x-bead-id', issue.id); event.dataTransfer.setData('application/x-bead-status', issue.status); event.dataTransfer.effectAllowed = 'move'; }; const onDropLane = (targetStatus: KanbanStatus, event: DragEvent) => { event.preventDefault(); const issueId = event.dataTransfer.getData('application/x-bead-id'); const sourceStatus = event.dataTransfer.getData('application/x-bead-status') as KanbanStatus; if (!issueId || !sourceStatus || sourceStatus === targetStatus) { return; } const issue = issueLookup.get(issueId); if (!issue) { return; } onMoveIssue(issue, targetStatus); }; return (
{KANBAN_STATUSES.map((status) => (
event.preventDefault()} onDrop={(event) => onDropLane(status, event)} className={`rounded-2xl border border-border-soft ${STATUS_COLUMN_CLASS[status]} p-2.5 transition ${ activeStatus === status ? 'shadow-card' : 'opacity-90' }`} >
{activeStatus === status ? ( ) : null}
{activeStatus === status ? (
{columns[status].map((issue) => ( ))} {columns[status].length === 0 ? (
No beads
) : null}
) : (
{columns[status].slice(0, 6).map((issue) => ( ))} {columns[status].length > 6 ? ( ) : null} {columns[status].length === 0 ? ( No beads ) : null}
)}
))}
); }