feat: enhance Kanban components with visibleStatuses prop

- Added `visibleStatuses` prop to `KanbanBoard`, `PrioritySwimlanesBoard`, `SwimlanesBase`, and `SwimlanesBoard` for improved column visibility control.
- Updated `KanbanBoardContainer` to derive `visibleStatuses` from `useColumnVisibility`, allowing dynamic filtering of displayed statuses.
- Refactored `KanbanFilters` to accept `hiddenStatuses` and `onToggleStatusVisibility` props, enabling better integration with column visibility management.
- Cleaned up visibility logic across components to ensure consistent behavior and user experience.
This commit is contained in:
Julien Froidefond
2025-09-15 11:05:11 +02:00
parent 2f18c08b55
commit 1a21f9b88b
6 changed files with 47 additions and 20 deletions

View File

@@ -28,14 +28,15 @@ interface KanbanBoardProps {
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>; onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
loading?: boolean; loading?: boolean;
compactView?: boolean; compactView?: boolean;
visibleStatuses?: TaskStatus[];
} }
export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, loading = false, compactView = false }: KanbanBoardProps) { export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, loading = false, compactView = false, visibleStatuses }: KanbanBoardProps) {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [activeTask, setActiveTask] = useState<Task | null>(null); const [activeTask, setActiveTask] = useState<Task | null>(null);
// Gestion de la visibilité des colonnes // Gestion de la visibilité des colonnes (utilise les props si disponibles)
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility(); const { getVisibleStatuses } = useColumnVisibility();
// Configuration des capteurs pour le drag & drop // Configuration des capteurs pour le drag & drop
const sensors = useSensors( const sensors = useSensors(
@@ -67,7 +68,9 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
}, [tasksByStatus]); }, [tasksByStatus]);
// Filtrer les colonnes visibles // Filtrer les colonnes visibles
const visibleColumns = getVisibleStatuses(allColumns); const visibleColumns = visibleStatuses ?
allColumns.filter(column => visibleStatuses.includes(column.id)) :
getVisibleStatuses(allColumns);
const handleCreateTask = async (data: CreateTaskData) => { const handleCreateTask = async (data: CreateTaskData) => {
if (onCreateTask) { if (onCreateTask) {

View File

@@ -10,6 +10,8 @@ import { EditTaskForm } from '@/components/forms/EditTaskForm';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { Task, TaskStatus } from '@/lib/types'; import { Task, TaskStatus } from '@/lib/types';
import { UpdateTaskData } from '@/clients/tasks-client'; import { UpdateTaskData } from '@/clients/tasks-client';
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
import { getAllStatuses } from '@/lib/status-config';
export function KanbanBoardContainer() { export function KanbanBoardContainer() {
const { const {
@@ -25,6 +27,9 @@ export function KanbanBoardContainer() {
tags tags
} = useTasksContext(); } = useTasksContext();
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility();
const allStatuses = getAllStatuses();
const visibleStatuses = getVisibleStatuses(allStatuses.map(s => ({ id: s.key }))).map(s => s.id);
const [editingTask, setEditingTask] = useState<Task | null>(null); const [editingTask, setEditingTask] = useState<Task | null>(null);
const handleEditTask = (task: Task) => { const handleEditTask = (task: Task) => {
@@ -59,6 +64,8 @@ export function KanbanBoardContainer() {
<KanbanFilters <KanbanFilters
filters={kanbanFilters} filters={kanbanFilters}
onFiltersChange={setKanbanFilters} onFiltersChange={setKanbanFilters}
hiddenStatuses={hiddenStatuses}
onToggleStatusVisibility={toggleStatusVisibility}
/> />
{/* Section Objectifs Principaux */} {/* Section Objectifs Principaux */}
@@ -82,6 +89,7 @@ export function KanbanBoardContainer() {
onUpdateTitle={handleUpdateTitle} onUpdateTitle={handleUpdateTitle}
onUpdateStatus={handleUpdateStatus} onUpdateStatus={handleUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView}
visibleStatuses={visibleStatuses}
/> />
) : ( ) : (
<SwimlanesBoard <SwimlanesBoard
@@ -91,6 +99,7 @@ export function KanbanBoardContainer() {
onUpdateTitle={handleUpdateTitle} onUpdateTitle={handleUpdateTitle}
onUpdateStatus={handleUpdateStatus} onUpdateStatus={handleUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView}
visibleStatuses={visibleStatuses}
/> />
) )
) : ( ) : (
@@ -103,6 +112,7 @@ export function KanbanBoardContainer() {
onUpdateStatus={handleUpdateStatus} onUpdateStatus={handleUpdateStatus}
loading={loading} loading={loading}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView}
visibleStatuses={visibleStatuses}
/> />
)} )}

View File

@@ -2,7 +2,7 @@
import { useState, useEffect, useRef, useMemo } from 'react'; import { useState, useEffect, useRef, useMemo } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { TaskPriority } from '@/lib/types'; import { TaskPriority, TaskStatus } from '@/lib/types';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
@@ -26,11 +26,17 @@ export interface KanbanFilters {
interface KanbanFiltersProps { interface KanbanFiltersProps {
filters: KanbanFilters; filters: KanbanFilters;
onFiltersChange: (filters: KanbanFilters) => void; onFiltersChange: (filters: KanbanFilters) => void;
hiddenStatuses?: Set<TaskStatus>;
onToggleStatusVisibility?: (status: TaskStatus) => void;
} }
export function KanbanFilters({ filters, onFiltersChange }: KanbanFiltersProps) { export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsHiddenStatuses, onToggleStatusVisibility }: KanbanFiltersProps) {
const { tags: availableTags, regularTasks } = useTasksContext(); const { tags: availableTags, regularTasks } = useTasksContext();
const { hiddenStatuses, toggleStatusVisibility } = useColumnVisibility(); const { hiddenStatuses: localHiddenStatuses, toggleStatusVisibility: localToggleStatusVisibility } = useColumnVisibility();
// Utiliser les props si disponibles, sinon utiliser l'état local
const hiddenStatuses = propsHiddenStatuses || localHiddenStatuses;
const toggleStatusVisibility = onToggleStatusVisibility || localToggleStatusVisibility;
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
const [isSortExpanded, setIsSortExpanded] = useState(false); const [isSortExpanded, setIsSortExpanded] = useState(false);
const [isSwimlaneModeExpanded, setIsSwimlaneModeExpanded] = useState(false); const [isSwimlaneModeExpanded, setIsSwimlaneModeExpanded] = useState(false);

View File

@@ -12,6 +12,7 @@ interface PrioritySwimlanesoardProps {
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>; onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>; onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
compactView?: boolean; compactView?: boolean;
visibleStatuses?: TaskStatus[];
} }
export function PrioritySwimlanesBoard({ export function PrioritySwimlanesBoard({
@@ -20,7 +21,8 @@ export function PrioritySwimlanesBoard({
onEditTask, onEditTask,
onUpdateTitle, onUpdateTitle,
onUpdateStatus, onUpdateStatus,
compactView = false compactView = false,
visibleStatuses
}: PrioritySwimlanesoardProps) { }: PrioritySwimlanesoardProps) {
// Grouper les tâches par priorités et créer les données de swimlanes // Grouper les tâches par priorités et créer les données de swimlanes
@@ -60,6 +62,7 @@ export function PrioritySwimlanesBoard({
onUpdateTitle={onUpdateTitle} onUpdateTitle={onUpdateTitle}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={compactView} compactView={compactView}
visibleStatuses={visibleStatuses}
/> />
); );
} }

View File

@@ -79,6 +79,7 @@ interface SwimlanesBaseProps {
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>; onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>; onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
compactView?: boolean; compactView?: boolean;
visibleStatuses?: TaskStatus[];
} }
export function SwimlanesBase({ export function SwimlanesBase({
@@ -89,16 +90,17 @@ export function SwimlanesBase({
onEditTask, onEditTask,
onUpdateTitle, onUpdateTitle,
onUpdateStatus, onUpdateStatus,
compactView = false compactView = false,
visibleStatuses
}: SwimlanesBaseProps) { }: SwimlanesBaseProps) {
const [activeTask, setActiveTask] = useState<Task | null>(null); const [activeTask, setActiveTask] = useState<Task | null>(null);
const [collapsedSwimlanes, setCollapsedSwimlanes] = useState<Set<string>>(new Set()); const [collapsedSwimlanes, setCollapsedSwimlanes] = useState<Set<string>>(new Set());
// Gestion de la visibilité des colonnes // Gestion de la visibilité des colonnes
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility(); const { getVisibleStatuses } = useColumnVisibility();
const allStatuses = getAllStatuses(); const allStatuses = getAllStatuses();
const visibleStatuses = getVisibleStatuses(allStatuses.map(s => ({ id: s.key }))) const statusesToShow = visibleStatuses ||
.map(s => s.id); getVisibleStatuses(allStatuses.map(s => ({ id: s.key }))).map(s => s.id);
// Configuration des sensors pour le drag & drop // Configuration des sensors pour le drag & drop
const sensors = useSensors( const sensors = useSensors(
@@ -161,9 +163,9 @@ export function SwimlanesBase({
{/* Headers des colonnes visibles */} {/* Headers des colonnes visibles */}
<div <div
className={`grid gap-4 px-6 py-4 ml-8`} className={`grid gap-4 px-6 py-4 ml-8`}
style={{ gridTemplateColumns: `repeat(${visibleStatuses.length}, minmax(0, 1fr))` }} style={{ gridTemplateColumns: `repeat(${statusesToShow.length}, minmax(0, 1fr))` }}
> >
{visibleStatuses.map(status => { {statusesToShow.map(status => {
const statusConfig = allStatuses.find(s => s.key === status); const statusConfig = allStatuses.find(s => s.key === status);
return ( return (
<div key={status} className="text-center"> <div key={status} className="text-center">
@@ -214,9 +216,9 @@ export function SwimlanesBase({
{!isCollapsed && ( {!isCollapsed && (
<div <div
className={`grid gap-4 p-4`} className={`grid gap-4 p-4`}
style={{ gridTemplateColumns: `repeat(${visibleStatuses.length}, minmax(0, 1fr))` }} style={{ gridTemplateColumns: `repeat(${statusesToShow.length}, minmax(0, 1fr))` }}
> >
{visibleStatuses.map(status => { {statusesToShow.map(status => {
const statusTasks = swimlane.tasks.filter(task => task.status === status); const statusTasks = swimlane.tasks.filter(task => task.status === status);
return ( return (

View File

@@ -12,6 +12,7 @@ interface SwimlanesboardProps {
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>; onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>; onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
compactView?: boolean; compactView?: boolean;
visibleStatuses?: TaskStatus[];
} }
export function SwimlanesBoard({ export function SwimlanesBoard({
@@ -20,7 +21,8 @@ export function SwimlanesBoard({
onEditTask, onEditTask,
onUpdateTitle, onUpdateTitle,
onUpdateStatus, onUpdateStatus,
compactView = false compactView = false,
visibleStatuses
}: SwimlanesboardProps) { }: SwimlanesboardProps) {
const { tags: availableTags } = useTasksContext(); const { tags: availableTags } = useTasksContext();
@@ -80,6 +82,7 @@ export function SwimlanesBoard({
onUpdateTitle={onUpdateTitle} onUpdateTitle={onUpdateTitle}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={compactView} compactView={compactView}
visibleStatuses={visibleStatuses}
/> />
); );
} }