refactor: update Kanban component imports and streamline filters

- Replaced direct imports of `KanbanFilters` with type imports from `@/lib/types` across multiple components for consistency.
- Simplified `KanbanPageClient` by integrating `DesktopControls` for better organization and readability, removing redundant desktop control code.
- Ensured `compactView` is explicitly typed as boolean in relevant components to enhance type safety.
This commit is contained in:
Julien Froidefond
2025-09-26 08:32:07 +02:00
parent c224c644b1
commit a5199a8302
11 changed files with 177 additions and 147 deletions

View File

@@ -9,11 +9,8 @@ import { useUserPreferences } from '@/contexts/UserPreferencesContext';
import { Task, Tag } from '@/lib/types'; import { Task, Tag } from '@/lib/types';
import { CreateTaskData } from '@/clients/tasks-client'; import { CreateTaskData } from '@/clients/tasks-client';
import { CreateTaskForm } from '@/components/forms/CreateTaskForm'; import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
import { Button } from '@/components/ui/Button';
import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter';
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
import { MobileControls } from '@/components/kanban/MobileControls'; import { MobileControls } from '@/components/kanban/MobileControls';
import { DesktopControls } from '@/components/kanban/DesktopControls';
import { useIsMobile } from '@/hooks/useIsMobile'; import { useIsMobile } from '@/hooks/useIsMobile';
interface KanbanPageClientProps { interface KanbanPageClientProps {
@@ -81,112 +78,20 @@ function KanbanPageContent() {
onCreateTask={() => setIsCreateModalOpen(true)} onCreateTask={() => setIsCreateModalOpen(true)}
/> />
) : ( ) : (
/* Barre de contrôles desktop */ <DesktopControls
<div className="bg-[var(--card)]/30 border-b border-[var(--border)]/30"> showFilters={showFilters}
<div className="container mx-auto px-6 py-2"> showObjectives={showObjectives}
<div className="flex items-center justify-between w-full"> compactView={compactView}
<div className="flex items-center gap-4"> swimlanesByTags={swimlanesByTags}
<div className="flex items-center gap-2"> activeFiltersCount={activeFiltersCount}
<button kanbanFilters={kanbanFilters}
onClick={handleToggleFilters} onToggleFilters={handleToggleFilters}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${ onToggleObjectives={handleToggleObjectives}
showFilters onToggleCompactView={handleToggleCompactView}
? 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30' onToggleSwimlanes={handleToggleSwimlanes}
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50' onFiltersChange={setKanbanFilters}
}`} onCreateTask={() => setIsCreateModalOpen(true)}
> />
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" />
</svg>
Filtres{activeFiltersCount > 0 && ` (${activeFiltersCount})`}
</button>
<button
onClick={handleToggleObjectives}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
showObjectives
? 'bg-[var(--accent)]/20 text-[var(--accent)] border border-[var(--accent)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--accent)]/50'
}`}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
Objectifs
</button>
</div>
<div className="flex items-center gap-2 border-l border-[var(--border)] pl-4">
{/* Raccourcis Jira */}
<JiraQuickFilter
filters={kanbanFilters}
onFiltersChange={setKanbanFilters}
/>
{/* Raccourcis TFS */}
<TfsQuickFilter
filters={kanbanFilters}
onFiltersChange={setKanbanFilters}
/>
<button
onClick={handleToggleCompactView}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
compactView
? 'bg-[var(--secondary)]/20 text-[var(--secondary)] border border-[var(--secondary)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--secondary)]/50'
}`}
title={compactView ? "Vue détaillée" : "Vue compacte"}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{compactView ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
{compactView ? 'Détaillée' : 'Compacte'}
</button>
<button
onClick={handleToggleSwimlanes}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
swimlanesByTags
? 'bg-[var(--warning)]/20 text-[var(--warning)] border border-[var(--warning)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--warning)]/50'
}`}
title={swimlanesByTags ? "Vue standard" : "Vue swimlanes"}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{swimlanesByTags ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14-7H5m14 14H5" />
)}
</svg>
{swimlanesByTags ? 'Standard' : 'Swimlanes'}
</button>
{/* Font Size Toggle */}
<FontSizeToggle />
</div>
</div>
{/* Bouton d'ajout de tâche */}
<Button
variant="primary"
onClick={() => setIsCreateModalOpen(true)}
className="flex items-center gap-2"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
Nouvelle tâche
</Button>
</div>
</div>
</div>
)} )}
<main className="h-[calc(100vh-160px)]"> <main className="h-[calc(100vh-160px)]">

View File

@@ -5,7 +5,7 @@ import { SwimlanesBoard } from './SwimlanesBoard';
import { PrioritySwimlanesBoard } from './PrioritySwimlanesBoard'; import { PrioritySwimlanesBoard } from './PrioritySwimlanesBoard';
import { Task, TaskStatus } from '@/lib/types'; import { Task, TaskStatus } from '@/lib/types';
import { CreateTaskData } from '@/clients/tasks-client'; import { CreateTaskData } from '@/clients/tasks-client';
import { KanbanFilters } from './KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
import { useIsMobile } from '@/hooks/useIsMobile'; import { useIsMobile } from '@/hooks/useIsMobile';
interface BoardRouterProps { interface BoardRouterProps {
@@ -41,7 +41,7 @@ export function BoardRouter({
onCreateTask={onCreateTask} onCreateTask={onCreateTask}
onEditTask={onEditTask} onEditTask={onEditTask}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView as boolean}
visibleStatuses={visibleStatuses} visibleStatuses={visibleStatuses}
loading={loading} loading={loading}
/> />
@@ -53,7 +53,7 @@ export function BoardRouter({
onCreateTask={onCreateTask} onCreateTask={onCreateTask}
onEditTask={onEditTask} onEditTask={onEditTask}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView as boolean}
visibleStatuses={visibleStatuses} visibleStatuses={visibleStatuses}
loading={loading} loading={loading}
/> />
@@ -68,7 +68,7 @@ export function BoardRouter({
onCreateTask={onCreateTask} onCreateTask={onCreateTask}
onEditTask={onEditTask} onEditTask={onEditTask}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView as boolean}
visibleStatuses={visibleStatuses} visibleStatuses={visibleStatuses}
/> />
); );

View File

@@ -0,0 +1,145 @@
'use client';
import { Button } from '@/components/ui/Button';
import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter';
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
import type { KanbanFilters } from '@/lib/types';
interface DesktopControlsProps {
showFilters: boolean;
showObjectives: boolean;
compactView: boolean;
swimlanesByTags: boolean;
activeFiltersCount: number;
kanbanFilters: KanbanFilters;
onToggleFilters: () => void;
onToggleObjectives: () => void;
onToggleCompactView: () => void;
onToggleSwimlanes: () => void;
onFiltersChange: (filters: KanbanFilters) => void;
onCreateTask: () => void;
}
export function DesktopControls({
showFilters,
showObjectives,
compactView,
swimlanesByTags,
activeFiltersCount,
kanbanFilters,
onToggleFilters,
onToggleObjectives,
onToggleCompactView,
onToggleSwimlanes,
onFiltersChange,
onCreateTask,
}: DesktopControlsProps) {
return (
<div className="bg-[var(--card)]/30 border-b border-[var(--border)]/30">
<div className="container mx-auto px-6 py-2">
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<button
onClick={onToggleFilters}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
showFilters
? 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50'
}`}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" />
</svg>
Filtres{activeFiltersCount > 0 && ` (${activeFiltersCount})`}
</button>
<button
onClick={onToggleObjectives}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
showObjectives
? 'bg-[var(--accent)]/20 text-[var(--accent)] border border-[var(--accent)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--accent)]/50'
}`}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
Objectifs
</button>
</div>
<div className="flex items-center gap-2 border-l border-[var(--border)] pl-4">
{/* Raccourcis Jira */}
<JiraQuickFilter
filters={kanbanFilters}
onFiltersChange={onFiltersChange}
/>
{/* Raccourcis TFS */}
<TfsQuickFilter
filters={kanbanFilters}
onFiltersChange={onFiltersChange}
/>
<button
onClick={onToggleCompactView}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
compactView
? 'bg-[var(--secondary)]/20 text-[var(--secondary)] border border-[var(--secondary)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--secondary)]/50'
}`}
title={compactView ? "Vue détaillée" : "Vue compacte"}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{compactView ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
{compactView ? 'Détaillée' : 'Compacte'}
</button>
<button
onClick={onToggleSwimlanes}
className={`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all ${
swimlanesByTags
? 'bg-[var(--warning)]/20 text-[var(--warning)] border border-[var(--warning)]/30'
: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--warning)]/50'
}`}
title={swimlanesByTags ? "Vue standard" : "Vue swimlanes"}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{swimlanesByTags ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14-7H5m14 14H5" />
)}
</svg>
{swimlanesByTags ? 'Standard' : 'Swimlanes'}
</button>
{/* Font Size Toggle */}
<FontSizeToggle />
</div>
</div>
{/* Bouton d'ajout de tâche */}
<Button
variant="primary"
onClick={onCreateTask}
className="flex items-center gap-2"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
Nouvelle tâche
</Button>
</div>
</div>
</div>
);
}

View File

@@ -2,7 +2,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { KanbanFilters } from './KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
interface JiraQuickFilterProps { interface JiraQuickFilterProps {
filters: KanbanFilters; filters: KanbanFilters;

View File

@@ -16,27 +16,7 @@ import { TagFilters } from './filters/TagFilters';
import { GeneralFilters } from './filters/GeneralFilters'; import { GeneralFilters } from './filters/GeneralFilters';
import { ColumnFilters } from './filters/ColumnFilters'; import { ColumnFilters } from './filters/ColumnFilters';
export interface KanbanFilters { import type { KanbanFilters } from '@/lib/types';
search?: string;
tags?: string[];
priorities?: TaskPriority[];
showCompleted?: boolean;
compactView?: boolean;
swimlanesByTags?: boolean;
swimlanesMode?: 'tags' | 'priority'; // Mode des swimlanes
pinnedTag?: string; // Tag pour les objectifs principaux
sortBy?: string; // Clé de l'option de tri sélectionnée
showWithDueDate?: boolean; // Afficher seulement les tâches avec une date de fin
// Filtres spécifiques Jira
showJiraOnly?: boolean; // Afficher seulement les tâches Jira
hideJiraTasks?: boolean; // Masquer toutes les tâches Jira
jiraProjects?: string[]; // Filtrer par projet Jira
jiraTypes?: string[]; // Filtrer par type Jira (Story, Task, Bug, etc.)
// Filtres spécifiques TFS
showTfsOnly?: boolean; // Afficher seulement les tâches TFS
hideTfsTasks?: boolean; // Masquer toutes les tâches TFS
tfsProjects?: string[]; // Filtrer par projet TFS
}
interface KanbanFiltersProps { interface KanbanFiltersProps {
filters: KanbanFilters; filters: KanbanFilters;

View File

@@ -1,10 +1,10 @@
'use client'; 'use client';
import { KanbanFilters } from './KanbanFilters'; import type { KanbanFilters as KanbanFiltersType } from '@/lib/types';
import { ObjectivesBoard } from './ObjectivesBoard'; import { ObjectivesBoard } from './ObjectivesBoard';
import { Task, TaskStatus } from '@/lib/types'; import { Task, TaskStatus } from '@/lib/types';
import { KanbanFilters as KanbanFiltersType } from './KanbanFilters';
import { UserPreferences } from '@/lib/types'; import { UserPreferences } from '@/lib/types';
import { KanbanFilters } from './KanbanFilters';
interface KanbanHeaderProps { interface KanbanHeaderProps {
showFilters: boolean; showFilters: boolean;
@@ -49,7 +49,7 @@ export function KanbanHeader({
tasks={pinnedTasks} tasks={pinnedTasks}
onEditTask={onEditTask} onEditTask={onEditTask}
onUpdateStatus={onUpdateStatus} onUpdateStatus={onUpdateStatus}
compactView={kanbanFilters.compactView} compactView={kanbanFilters.compactView as boolean}
pinnedTagName={pinnedTagName} pinnedTagName={pinnedTagName}
/> />
)} )}

View File

@@ -5,7 +5,7 @@ import { Button } from '@/components/ui/Button';
import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter'; import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter'; import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter';
import { FontSizeToggle } from '@/components/ui/FontSizeToggle'; import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
import { KanbanFilters } from '@/components/kanban/KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
interface MobileControlsProps { interface MobileControlsProps {
showFilters: boolean; showFilters: boolean;

View File

@@ -2,7 +2,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { KanbanFilters } from './KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
interface TfsQuickFilterProps { interface TfsQuickFilterProps {
filters: KanbanFilters; filters: KanbanFilters;

View File

@@ -3,7 +3,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { KanbanFilters } from '../KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
interface JiraFiltersProps { interface JiraFiltersProps {
filters: KanbanFilters; filters: KanbanFilters;

View File

@@ -3,7 +3,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { KanbanFilters } from '../KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
interface TfsFiltersProps { interface TfsFiltersProps {
filters: KanbanFilters; filters: KanbanFilters;

View File

@@ -6,7 +6,7 @@ import { useTags } from '@/hooks/useTags';
import { useUserPreferences } from './UserPreferencesContext'; import { useUserPreferences } from './UserPreferencesContext';
import { Task, Tag, TaskStats, TaskStatus } from '@/lib/types'; import { Task, Tag, TaskStats, TaskStatus } from '@/lib/types';
import { CreateTaskData, TaskFilters } from '@/clients/tasks-client'; import { CreateTaskData, TaskFilters } from '@/clients/tasks-client';
import { KanbanFilters } from '@/components/kanban/KanbanFilters'; import type { KanbanFilters } from '@/lib/types';
import { sortTasks, getSortOption, DEFAULT_SORT, createSortKey } from '@/lib/sort-config'; import { sortTasks, getSortOption, DEFAULT_SORT, createSortKey } from '@/lib/sort-config';
interface TasksContextType { interface TasksContextType {
@@ -94,9 +94,9 @@ export function TasksProvider({ children, initialTasks, initialTags, initialStat
}; };
const viewPreferenceUpdates = { const viewPreferenceUpdates = {
compactView: newFilters.compactView, compactView: newFilters.compactView as boolean,
swimlanesByTags: newFilters.swimlanesByTags, swimlanesByTags: newFilters.swimlanesByTags as boolean,
swimlanesMode: newFilters.swimlanesMode swimlanesMode: newFilters.swimlanesMode as 'tags' | 'priority'
}; };
// Mettre à jour via UserPreferencesContext // Mettre à jour via UserPreferencesContext