import { useMemo, useState } from 'react'; import { Button } from '@renderer/components/ui/button'; import { cn } from '@renderer/lib/utils'; import { Columns3, LayoutGrid } from 'lucide-react'; import { KanbanColumn } from './KanbanColumn'; import { KanbanFilterPopover } from './KanbanFilterPopover'; import { KanbanTaskCard } from './KanbanTaskCard'; import type { KanbanFilterState } from './KanbanFilterPopover'; import type { Session } from '@renderer/types/data'; import type { KanbanColumnId, KanbanState, ResolvedTeamMember, TeamTask } from '@shared/types'; interface KanbanBoardProps { tasks: TeamTask[]; kanbanState: KanbanState; filter: KanbanFilterState; sessions: Session[]; leadSessionId?: string; members: ResolvedTeamMember[]; onFilterChange: (filter: KanbanFilterState) => void; onRequestReview: (taskId: string) => void; onApprove: (taskId: string) => void; onRequestChanges: (taskId: string) => void; onMoveBackToDone: (taskId: string) => void; onStartTask: (taskId: string) => void; onCompleteTask: (taskId: string) => void; onScrollToTask?: (taskId: string) => void; onTaskClick?: (task: TeamTask) => void; } type KanbanViewMode = 'grid' | 'columns'; const COLUMNS: { id: KanbanColumnId; title: string }[] = [ { id: 'todo', title: 'TODO' }, { id: 'in_progress', title: 'IN PROGRESS' }, { id: 'done', title: 'DONE' }, { id: 'review', title: 'REVIEW' }, { id: 'approved', title: 'APPROVED' }, ]; function getTaskColumn(task: TeamTask, kanbanState: KanbanState): KanbanColumnId | null { const explicit = kanbanState.tasks[task.id]; if (explicit?.column) { return explicit.column; } if (task.status === 'pending') { return 'todo'; } if (task.status === 'in_progress') { return 'in_progress'; } if (task.status === 'completed') { return 'done'; } return null; } export const KanbanBoard = ({ tasks, kanbanState, filter, sessions, leadSessionId, members, onFilterChange, onRequestReview, onApprove, onRequestChanges, onMoveBackToDone, onStartTask, onCompleteTask, onScrollToTask, onTaskClick, }: KanbanBoardProps): React.JSX.Element => { const [viewMode, setViewMode] = useState('grid'); const taskMap = useMemo(() => new Map(tasks.map((t) => [t.id, t])), [tasks]); const grouped = useMemo(() => { const result = new Map( COLUMNS.map(({ id }) => [id, [] as TeamTask[]]) ); for (const task of tasks) { const column = getTaskColumn(task, kanbanState); if (!column) { continue; } result.get(column)?.push(task); } return result; }, [tasks, kanbanState]); const renderCards = (columnId: KanbanColumnId, columnTasks: TeamTask[]): React.JSX.Element => { if (columnTasks.length === 0) { return (
No tasks
); } return ( <> {columnTasks.map((task) => ( 0} taskMap={taskMap} onRequestReview={onRequestReview} onApprove={onApprove} onRequestChanges={onRequestChanges} onMoveBackToDone={onMoveBackToDone} onStartTask={onStartTask} onCompleteTask={onCompleteTask} onScrollToTask={onScrollToTask} onTaskClick={onTaskClick} /> ))} ); }; return (
{viewMode === 'grid' ? (
{COLUMNS.map((column) => { const columnTasks = grouped.get(column.id) ?? []; return ( {renderCards(column.id, columnTasks)} ); })}
) : (
{COLUMNS.map((column) => { const columnTasks = grouped.get(column.id) ?? []; return (
{renderCards(column.id, columnTasks)}
); })}
)}
); };