Files
vibe-kanban/frontend/src/components/tasks/TaskKanbanBoard.tsx
Anastasiia Solop aae0984271 Refactor TaskDetailsPanel (#126)
* improve performance

* split task details panel into components

* remove useTaskDetails hook

* create task details context

* move context provider
2025-07-11 11:31:28 +02:00

111 lines
2.9 KiB
TypeScript

import { useMemo } from 'react';
import {
type DragEndEvent,
KanbanBoard,
KanbanCards,
KanbanHeader,
KanbanProvider,
} from '@/components/ui/shadcn-io/kanban';
import { TaskCard } from './TaskCard';
import type { TaskStatus, TaskWithAttemptStatus } from 'shared/types';
type Task = TaskWithAttemptStatus;
interface TaskKanbanBoardProps {
tasks: Task[];
searchQuery?: string;
onDragEnd: (event: DragEndEvent) => void;
onEditTask: (task: Task) => void;
onDeleteTask: (taskId: string) => void;
onViewTaskDetails: (task: Task) => void;
}
const allTaskStatuses: TaskStatus[] = [
'todo',
'inprogress',
'inreview',
'done',
'cancelled',
];
const statusLabels: Record<TaskStatus, string> = {
todo: 'To Do',
inprogress: 'In Progress',
inreview: 'In Review',
done: 'Done',
cancelled: 'Cancelled',
};
const statusBoardColors: Record<TaskStatus, string> = {
todo: 'hsl(var(--neutral))',
inprogress: 'hsl(var(--info))',
inreview: 'hsl(var(--warning))',
done: 'hsl(var(--success))',
cancelled: 'hsl(var(--destructive))',
};
export function TaskKanbanBoard({
tasks,
searchQuery = '',
onDragEnd,
onEditTask,
onDeleteTask,
onViewTaskDetails,
}: TaskKanbanBoardProps) {
// Memoize filtered tasks
const filteredTasks = useMemo(() => {
if (!searchQuery.trim()) {
return tasks;
}
const query = searchQuery.toLowerCase();
return tasks.filter(
(task) =>
task.title.toLowerCase().includes(query) ||
(task.description && task.description.toLowerCase().includes(query))
);
}, [tasks, searchQuery]);
// Memoize grouped tasks
const groupedTasks = useMemo(() => {
const groups: Record<TaskStatus, Task[]> = {} as Record<TaskStatus, Task[]>;
allTaskStatuses.forEach((status) => {
groups[status] = [];
});
filteredTasks.forEach((task) => {
const normalizedStatus = task.status.toLowerCase() as TaskStatus;
if (groups[normalizedStatus]) {
groups[normalizedStatus].push(task);
} else {
groups['todo'].push(task);
}
});
return groups;
}, [filteredTasks]);
return (
<KanbanProvider onDragEnd={onDragEnd}>
{Object.entries(groupedTasks).map(([status, statusTasks]) => (
<KanbanBoard key={status} id={status as TaskStatus}>
<KanbanHeader
name={statusLabels[status as TaskStatus]}
color={statusBoardColors[status as TaskStatus]}
/>
<KanbanCards>
{statusTasks.map((task, index) => (
<TaskCard
key={task.id}
task={task}
index={index}
status={status}
onEdit={onEditTask}
onDelete={onDeleteTask}
onViewDetails={onViewTaskDetails}
/>
))}
</KanbanCards>
</KanbanBoard>
))}
</KanbanProvider>
);
}