diff --git a/components/kanban/BoardContainer.tsx b/components/kanban/BoardContainer.tsx index be0eb4f..c92943e 100644 --- a/components/kanban/BoardContainer.tsx +++ b/components/kanban/BoardContainer.tsx @@ -91,6 +91,7 @@ export function KanbanBoardContainer({ onDeleteTask={deleteTask} onEditTask={handleEditTask} onUpdateTitle={handleUpdateTitle} + onUpdateStatus={handleUpdateStatus} compactView={kanbanFilters.compactView} pinnedTagName={pinnedTagName} /> diff --git a/components/kanban/ObjectivesBoard.tsx b/components/kanban/ObjectivesBoard.tsx index d7862f1..1372d5d 100644 --- a/components/kanban/ObjectivesBoard.tsx +++ b/components/kanban/ObjectivesBoard.tsx @@ -1,78 +1,175 @@ 'use client'; +import { useState } from 'react'; import { useObjectivesCollapse } from '@/hooks/useObjectivesCollapse'; -import { Task } from '@/lib/types'; +import { Task, TaskStatus } from '@/lib/types'; import { TaskCard } from './TaskCard'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; +import { + DndContext, + DragEndEvent, + DragOverlay, + DragStartEvent, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + SortableContext, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; +import { useDroppable } from '@dnd-kit/core'; interface ObjectivesBoardProps { tasks: Task[]; onDeleteTask?: (taskId: string) => Promise; onEditTask?: (task: Task) => void; onUpdateTitle?: (taskId: string, newTitle: string) => Promise; + onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise; compactView?: boolean; pinnedTagName?: string; } +// Composant pour les colonnes droppables +function DroppableColumn({ + status, + tasks, + title, + color, + icon, + onDeleteTask, + onEditTask, + onUpdateTitle, + compactView +}: { + status: TaskStatus; + tasks: Task[]; + title: string; + color: string; + icon: string; + onDeleteTask?: (taskId: string) => Promise; + onEditTask?: (task: Task) => void; + onUpdateTitle?: (taskId: string, newTitle: string) => Promise; + compactView: boolean; +}) { + const { setNodeRef } = useDroppable({ + id: status, + }); + + return ( +
+
+
+

+ {title} +

+
+ + {tasks.length} + +
+ + {tasks.length === 0 ? ( +
+
{icon}
+ Aucun objectif {title.toLowerCase()} +
+ ) : ( + t.id)} strategy={verticalListSortingStrategy}> +
+ {tasks.map(task => ( +
+ +
+ ))} +
+
+ )} +
+ ); +} + export function ObjectivesBoard({ tasks, onDeleteTask, onEditTask, onUpdateTitle, + onUpdateStatus, compactView = false, pinnedTagName = "Objectifs" }: ObjectivesBoardProps) { const { isCollapsed, toggleCollapse } = useObjectivesCollapse(); + const [activeTask, setActiveTask] = useState(null); + + // Configuration des sensors pour le drag & drop + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + distance: 8, // Évite les clics accidentels + }, + }) + ); + + // 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); + } + }; if (tasks.length === 0) { return null; // Ne rien afficher s'il n'y a pas d'objectifs } return ( -
-
- - -
- - -
- - {String(tasks.length).padStart(2, '0')} - - - {/* Bouton collapse séparé pour mobile */} + +
+
+ + +
-
-
- - - {!isCollapsed && ( - - {(() => { - // Séparer les tâches par statut - const activeTasks = tasks.filter(task => task.status === 'todo' || task.status === 'in_progress'); - const completedTasks = tasks.filter(task => task.status === 'done'); - - return ( -
- {/* Colonne En cours / À faire */} -
-
-
-

- En cours / À faire -

-
- - {activeTasks.length} - -
- - {activeTasks.length === 0 ? ( -
-
🎯
- Aucun objectif actif -
- ) : ( -
- {activeTasks.map(task => ( -
- -
- ))} -
- )} -
- - {/* Colonne Terminé */} -
-
-
-

- Terminé -

-
- - {completedTasks.length} - -
- - {completedTasks.length === 0 ? ( -
-
- Aucun objectif terminé -
- ) : ( -
- {completedTasks.map(task => ( -
- -
- ))} -
- )} -
+ +
+ + {String(tasks.length).padStart(2, '0')} + + + {/* Bouton collapse séparé pour mobile */} +
- ); - })()} - - )} - +
+ + + {!isCollapsed && ( + + {(() => { + // Séparer les tâches par statut + const inProgressTasks = tasks.filter(task => task.status === 'in_progress'); + const todoTasks = tasks.filter(task => task.status === 'todo' || task.status === 'backlog'); + const completedTasks = tasks.filter(task => task.status === 'done'); + + return ( +
+ + + + + +
+ ); + })()} +
+ )} + +
-
+ + {/* Overlay pour le drag & drop */} + + {activeTask ? ( +
+ +
+ ) : null} +
+ ); }