'use client'; import { Task, TaskStatus } from '@/lib/types'; import { TaskCard } from './TaskCard'; import { useMemo, useState } from 'react'; import { useTasksContext } from '@/contexts/TasksContext'; import { useColumnVisibility } from '@/hooks/useColumnVisibility'; import { ColumnVisibilityToggle } from './ColumnVisibilityToggle'; import { getAllStatuses } from '@/lib/status-config'; 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[]; onDeleteTask?: (taskId: string) => Promise; onEditTask?: (task: Task) => void; onUpdateTitle?: (taskId: string, newTitle: string) => Promise; onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise; compactView?: boolean; } export function SwimlanesBoard({ tasks, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, compactView = false }: SwimlanesboardProps) { const { tags: availableTags } = useTasksContext(); const [activeTask, setActiveTask] = useState(null); const [collapsedSwimlanes, setCollapsedSwimlanes] = useState>(new Set()); // Gestion de la visibilité des colonnes const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility(); // Configuration des capteurs pour le drag & drop const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8, }, }) ); // Configuration des statuts basée sur la config centralisée const statuses = useMemo(() => { return getAllStatuses().map(statusConfig => ({ id: statusConfig.key, title: statusConfig.label, color: statusConfig.color })); }, []); // 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; }); }; // Filtrer les statuts visibles const visibleStatuses = getVisibleStatuses(statuses); // Grouper les tâches par tags const tasksByTag = useMemo(() => { const grouped: { [tagName: string]: Task[] } = {}; // Ajouter une catégorie pour les tâches sans tags grouped['Sans tag'] = []; tasks.forEach(task => { if (!task.tags || task.tags.length === 0) { grouped['Sans tag'].push(task); } else { task.tags.forEach(tagName => { if (!grouped[tagName]) { grouped[tagName] = []; } grouped[tagName].push(task); }); } }); return grouped; }, [tasks]); // Obtenir la couleur d'un tag const getTagColor = (tagName: string) => { if (tagName === 'Sans tag') return '#64748b'; // slate-500 const tag = availableTags.find(t => t.name === tagName); return tag?.color || '#64748b'; }; return (
{/* Header */}

Kanban Swimlanes

{/* Headers des colonnes avec boutons toggle */}
{/* Headers des colonnes visibles */}
{visibleStatuses.map(status => (
{status.title}
))}
{/* Swimlanes */}
{Object.entries(tasksByTag) .sort(([a], [b]) => { // Mettre "Sans tag" à la fin if (a === 'Sans tag') return 1; if (b === 'Sans tag') return -1; return a.localeCompare(b); }) .map(([tagName, tagTasks]) => (
{/* Header de la swimlane */}
{/* Contenu de la swimlane */} {!collapsedSwimlanes.has(tagName) && (
{visibleStatuses.map(status => { const statusTasks = tagTasks.filter(task => task.status === status.id); return ( ); })}
)}
))}
{/* DragOverlay pour l'aperçu pendant le drag */} {activeTask && ( )}
); }