feat: add column visibility toggle to Kanban and Swimlanes boards

- Integrated `useColumnVisibility` hook for managing column visibility states.
- Added `ColumnVisibilityToggle` component to both `KanbanBoard` and `SwimlanesBoard` for user control over visible columns.
- Updated rendering logic to filter and display only visible columns, enhancing user experience and task organization.
This commit is contained in:
Julien Froidefond
2025-09-14 22:34:51 +02:00
parent 86920d1056
commit 1597f0fea1
4 changed files with 121 additions and 7 deletions

View File

@@ -6,6 +6,8 @@ import { Button } from '@/components/ui/Button';
import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
import { CreateTaskData } from '@/clients/tasks-client';
import { useMemo, useState } from 'react';
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
import { ColumnVisibilityToggle } from './ColumnVisibilityToggle';
import {
DndContext,
DragEndEvent,
@@ -31,6 +33,9 @@ interface KanbanBoardProps {
export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, loading = false, compactView = false }: KanbanBoardProps) {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [activeTask, setActiveTask] = useState<Task | null>(null);
// Gestion de la visibilité des colonnes
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility();
// Configuration des capteurs pour le drag & drop
const sensors = useSensors(
@@ -54,7 +59,7 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
}, [tasks]);
// Configuration des colonnes
const columns: Array<{
const allColumns: Array<{
id: TaskStatus;
title: string;
color: string;
@@ -86,6 +91,9 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
}
];
// Filtrer les colonnes visibles
const visibleColumns = getVisibleStatuses(allColumns);
const handleCreateTask = async (data: CreateTaskData) => {
if (onCreateTask) {
await onCreateTask(data);
@@ -145,9 +153,18 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
)}
</div>
{/* Toggle de visibilité des colonnes */}
<div className="px-6 pb-4">
<ColumnVisibilityToggle
statuses={allColumns}
hiddenStatuses={hiddenStatuses}
onToggleStatus={toggleStatusVisibility}
/>
</div>
{/* Board tech dark */}
<div className="flex-1 flex gap-6 overflow-x-auto p-6">
{columns.map((column) => (
{visibleColumns.map((column) => (
<KanbanColumn
key={column.id}
id={column.id}

View File

@@ -0,0 +1,39 @@
'use client';
import { TaskStatus } from '@/lib/types';
interface ColumnVisibilityToggleProps {
statuses: { id: TaskStatus; title: string; color: string }[];
hiddenStatuses: Set<TaskStatus>;
onToggleStatus: (status: TaskStatus) => void;
className?: string;
}
export function ColumnVisibilityToggle({
statuses,
hiddenStatuses,
onToggleStatus,
className = ""
}: ColumnVisibilityToggleProps) {
return (
<div className={`flex items-center gap-2 ${className}`}>
<span className="text-sm font-mono font-medium text-slate-400">
Colonnes :
</span>
{statuses.map(status => (
<button
key={status.id}
onClick={() => onToggleStatus(status.id)}
className={`px-3 py-1 rounded-lg text-xs font-mono font-medium transition-colors ${
hiddenStatuses.has(status.id)
? 'bg-slate-700/50 text-slate-500 hover:bg-slate-700'
: 'bg-blue-600/20 text-blue-300 border border-blue-500/30 hover:bg-blue-600/30'
}`}
title={hiddenStatuses.has(status.id) ? `Afficher ${status.title}` : `Masquer ${status.title}`}
>
{hiddenStatuses.has(status.id) ? '👁️‍🗨️' : '👁️'} {status.title}
</button>
))}
</div>
);
}

View File

@@ -4,6 +4,8 @@ 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 {
DndContext,
DragEndEvent,
@@ -86,6 +88,9 @@ export function SwimlanesBoard({
const { tags: availableTags } = useTasksContext();
const [activeTask, setActiveTask] = useState<Task | null>(null);
const [collapsedSwimlanes, setCollapsedSwimlanes] = useState<Set<string>>(new Set());
// Gestion de la visibilité des colonnes
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility();
// Configuration des capteurs pour le drag & drop
const sensors = useSensors(
@@ -137,6 +142,9 @@ export function SwimlanesBoard({
});
};
// Filtrer les statuts visibles
const visibleStatuses = getVisibleStatuses(statuses);
// Grouper les tâches par tags
const tasksByTag = useMemo(() => {
const grouped: { [tagName: string]: Task[] } = {};
@@ -184,9 +192,21 @@ export function SwimlanesBoard({
</h2>
</div>
{/* Headers des colonnes */}
<div className="grid grid-cols-4 gap-4 px-6 pb-4 ml-8">
{statuses.map(status => (
{/* Headers des colonnes avec boutons toggle */}
<div className="flex items-center justify-between px-6 pb-4">
<ColumnVisibilityToggle
statuses={statuses}
hiddenStatuses={hiddenStatuses}
onToggleStatus={toggleStatusVisibility}
/>
</div>
{/* Headers des colonnes visibles */}
<div
className={`grid gap-4 px-6 pb-4 ml-8`}
style={{ gridTemplateColumns: `repeat(${visibleStatuses.length}, 1fr)` }}
>
{visibleStatuses.map(status => (
<div key={status.id} className="text-center">
<div className="text-sm font-mono font-bold text-slate-300 uppercase tracking-wider">
{status.title}
@@ -238,8 +258,11 @@ export function SwimlanesBoard({
{/* Contenu de la swimlane */}
{!collapsedSwimlanes.has(tagName) && (
<div className="grid grid-cols-4 gap-4 p-2 ml-8">
{statuses.map(status => {
<div
className="gap-4 p-2 ml-8 grid"
style={{ gridTemplateColumns: `repeat(${visibleStatuses.length}, 1fr)` }}
>
{visibleStatuses.map(status => {
const statusTasks = tagTasks.filter(task => task.status === status.id);
return (
<DroppableColumn