Files
towercontrol/components/kanban/Board.tsx
2025-09-18 09:37:46 +02:00

131 lines
3.7 KiB
TypeScript

'use client';
import { Task, TaskStatus } from '@/lib/types';
import { KanbanColumn } from './Column';
import { CreateTaskData } from '@/clients/tasks-client';
import { useMemo, useState } from 'react';
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
import { useDragAndDrop } from '@/hooks/useDragAndDrop';
import { getAllStatuses } from '@/lib/status-config';
import {
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent
} from '@dnd-kit/core';
import { TaskCard } from './TaskCard';
interface KanbanBoardProps {
tasks: Task[];
onCreateTask?: (data: CreateTaskData) => Promise<void>;
onEditTask?: (task: Task) => void;
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
compactView?: boolean;
visibleStatuses?: TaskStatus[];
}
export function KanbanBoard({ tasks, onCreateTask, onEditTask, onUpdateStatus, compactView = false, visibleStatuses }: KanbanBoardProps) {
const [activeTask, setActiveTask] = useState<Task | null>(null);
const { isColumnVisible } = useUserPreferences();
const { isMounted, sensors } = useDragAndDrop();
// Organiser les tâches par statut
const tasksByStatus = useMemo(() => {
const grouped = tasks.reduce((acc, task) => {
if (!acc[task.status]) {
acc[task.status] = [];
}
acc[task.status].push(task);
return acc;
}, {} as Record<TaskStatus, Task[]>);
return grouped;
}, [tasks]);
// Configuration des colonnes basée sur la config centralisée
const allColumns = useMemo(() => {
return getAllStatuses().map(statusConfig => ({
id: statusConfig.key,
tasks: tasksByStatus[statusConfig.key] || []
}));
}, [tasksByStatus]);
// Filtrer les colonnes visibles
const visibleColumns = visibleStatuses ?
allColumns.filter(column => visibleStatuses.includes(column.id)) :
allColumns.filter(column => isColumnVisible(column.id));
// Gestion du début du drag
const handleDragStart = (event: DragStartEvent) => {
const { active } = event;
const task = tasks.find(t => t.id === active.id);
setActiveTask(task || null);
};
// Gestion de la fin du drag
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;
// Trouver la tâche actuelle
const task = tasks.find(t => t.id === taskId);
if (!task || task.status === newStatus) return;
// Mettre à jour le statut
await onUpdateStatus(taskId, newStatus);
};
const content = (
<div className="h-full flex flex-col bg-[var(--background)]">
{/* Espacement supérieur */}
<div className="pt-4"></div>
{/* Board tech dark */}
<div className="flex-1 flex gap-3 overflow-x-auto p-6">
{visibleColumns.map((column) => (
<KanbanColumn
key={column.id}
id={column.id}
tasks={column.tasks}
onCreateTask={onCreateTask}
onEditTask={onEditTask}
compactView={compactView}
/>
))}
</div>
</div>
);
if (!isMounted) {
return content;
}
return (
<DndContext
id="kanban-board"
sensors={sensors}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
{content}
{/* Overlay pour le drag & drop */}
<DragOverlay>
{activeTask ? (
<div className="rotate-3 opacity-90">
<TaskCard
task={activeTask}
onEdit={undefined}
/>
</div>
) : null}
</DragOverlay>
</DndContext>
);
}