* improve performance * split task details panel into components * remove useTaskDetails hook * create task details context * move context provider
111 lines
2.9 KiB
TypeScript
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>
|
|
);
|
|
}
|