diff --git a/components/kanban/SwimlanesBoard.tsx b/components/kanban/SwimlanesBoard.tsx index b150ca8..0045700 100644 --- a/components/kanban/SwimlanesBoard.tsx +++ b/components/kanban/SwimlanesBoard.tsx @@ -2,8 +2,69 @@ import { Task, TaskStatus } from '@/lib/types'; import { TaskCard } from './TaskCard'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useTasksContext } from '@/contexts/TasksContext'; +import { + DndContext, + DragEndEvent, + DragOverlay, + DragStartEvent, + closestCenter, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + SortableContext, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; +import { useDroppable } from '@dnd-kit/core'; +// import { SortableTaskCard } from './SortableTaskCard'; + +// Composant pour les colonnes droppables +function DroppableColumn({ + status, + tasks, + onDeleteTask, + onEditTask, + onUpdateTitle, + compactView +}: { + status: TaskStatus; + tasks: Task[]; + onDeleteTask?: (taskId: string) => Promise; + onEditTask?: (task: Task) => void; + onUpdateTitle?: (taskId: string, newTitle: string) => Promise; + compactView: boolean; +}) { + const { setNodeRef, isOver } = useDroppable({ + id: status, + }); + + return ( +
+ t.id)} strategy={verticalListSortingStrategy}> +
+ {tasks.map(task => ( + + ))} +
+
+
+ ); +} interface SwimlanesboardProps { tasks: Task[]; @@ -23,6 +84,17 @@ export function SwimlanesBoard({ compactView = false }: SwimlanesboardProps) { const { tags: availableTags } = useTasksContext(); + const [activeTask, setActiveTask] = useState(null); + const [collapsedSwimlanes, setCollapsedSwimlanes] = useState>(new Set()); + + // Configuration des capteurs pour le drag & drop + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + distance: 8, + }, + }) + ); const statuses: { id: TaskStatus; title: string; color: string }[] = [ { id: 'todo', title: 'À faire', color: 'gray' }, @@ -31,6 +103,40 @@ export function SwimlanesBoard({ { id: 'cancelled', title: 'Annulé', color: 'red' } ]; + // Handlers pour le drag & drop + const handleDragStart = (event: DragStartEvent) => { + const task = tasks.find(t => t.id === event.active.id); + setActiveTask(task || null); + }; + + const handleDragEnd = async (event: DragEndEvent) => { + const { active, over } = event; + setActiveTask(null); + + if (!over || !onUpdateStatus) return; + + const taskId = active.id as string; + const newStatus = over.id as TaskStatus; + + // Vérifier si le statut a changé + const task = tasks.find(t => t.id === taskId); + if (task && task.status !== newStatus) { + await onUpdateStatus(taskId, newStatus); + } + }; + + const toggleSwimlane = (tagName: string) => { + setCollapsedSwimlanes(prev => { + const newSet = new Set(prev); + if (newSet.has(tagName)) { + newSet.delete(tagName); + } else { + newSet.add(tagName); + } + return newSet; + }); + }; + // Grouper les tâches par tags const tasksByTag = useMemo(() => { const grouped: { [tagName: string]: Task[] } = {}; @@ -62,7 +168,14 @@ export function SwimlanesBoard({ }; return ( -
+ +
{/* Header */}
@@ -72,10 +185,7 @@ export function SwimlanesBoard({
{/* Headers des colonnes */} -
-
- Tags -
+
{statuses.map(status => (
@@ -98,8 +208,11 @@ export function SwimlanesBoard({ .map(([tagName, tagTasks]) => (
{/* Header de la swimlane */} -
-
+
+
{/* Contenu de la swimlane */} -
-
{/* Colonne vide pour le nom du tag */} - - {statuses.map(status => { - const statusTasks = tagTasks.filter(task => task.status === status.id); - return ( -
- {statusTasks.map(task => ( - - ))} -
- ); - })} -
+ {!collapsedSwimlanes.has(tagName) && ( +
+ {statuses.map(status => { + const statusTasks = tagTasks.filter(task => task.status === status.id); + return ( + + ); + })} +
+ )}
))}
-
+
+ + {/* DragOverlay pour l'aperçu pendant le drag */} + + {activeTask && ( + + )} + + ); }