diff --git a/src/components/kanban/DesktopControls.tsx b/src/components/kanban/DesktopControls.tsx index 88dbdd8..849e6ec 100644 --- a/src/components/kanban/DesktopControls.tsx +++ b/src/components/kanban/DesktopControls.tsx @@ -3,8 +3,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; -import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter'; -import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter'; +import { SourceQuickFilter } from '@/components/kanban/SourceQuickFilter'; import { FontSizeToggle } from '@/components/ui/FontSizeToggle'; import type { KanbanFilters } from '@/lib/types'; @@ -143,14 +142,8 @@ export function DesktopControls({ {/* Section droite : Raccourcis + Bouton Nouvelle tâche */}
- {/* Raccourcis Jira */} - - - {/* Raccourcis TFS */} - diff --git a/src/components/kanban/JiraQuickFilter.tsx b/src/components/kanban/JiraQuickFilter.tsx deleted file mode 100644 index 9770f08..0000000 --- a/src/components/kanban/JiraQuickFilter.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import { useMemo } from 'react'; -import { useTasksContext } from '@/contexts/TasksContext'; -import type { KanbanFilters } from '@/lib/types'; - -interface JiraQuickFilterProps { - filters: KanbanFilters; - onFiltersChange: (filters: KanbanFilters) => void; -} - -export function JiraQuickFilter({ filters, onFiltersChange }: JiraQuickFilterProps) { - const { regularTasks } = useTasksContext(); - - // Vérifier s'il y a des tâches Jira dans le système - const hasJiraTasks = useMemo(() => { - return regularTasks.some(task => task.source === 'jira'); - }, [regularTasks]); - - // Si pas de tâches Jira, on n'affiche rien - if (!hasJiraTasks) { - return null; - } - - // Déterminer l'état actuel - const currentMode = filters.showJiraOnly ? 'show' : filters.hideJiraTasks ? 'hide' : 'all'; - - const handleJiraCycle = () => { - const updates: Partial = {}; - - // Cycle : All -> Jira only -> No Jira -> All - switch (currentMode) { - case 'all': - // All -> Jira only - updates.showJiraOnly = true; - updates.hideJiraTasks = false; - break; - case 'show': - // Jira only -> No Jira - updates.showJiraOnly = false; - updates.hideJiraTasks = true; - break; - case 'hide': - // No Jira -> All - updates.showJiraOnly = false; - updates.hideJiraTasks = false; - break; - } - - onFiltersChange({ ...filters, ...updates }); - }; - - // Définir l'apparence selon l'état - const getButtonStyle = () => { - switch (currentMode) { - case 'show': - return 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30'; - case 'hide': - return 'bg-red-500/20 text-red-400 border border-red-400/30'; - default: - return 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50'; - } - }; - - const getButtonContent = () => { - switch (currentMode) { - case 'show': - return { icon: '🔹', text: 'Jira only' }; - case 'hide': - return { icon: '🚫', text: 'No Jira' }; - default: - return { icon: '🔌', text: 'All tasks' }; - } - }; - - const getTooltip = () => { - switch (currentMode) { - case 'all': - return 'Cliquer pour afficher seulement Jira'; - case 'show': - return 'Cliquer pour masquer Jira'; - case 'hide': - return 'Cliquer pour afficher tout'; - default: - return 'Filtrer les tâches Jira'; - } - }; - - const content = getButtonContent(); - - return ( - - ); -} diff --git a/src/components/kanban/MobileControls.tsx b/src/components/kanban/MobileControls.tsx index 198d634..eb2f6d3 100644 --- a/src/components/kanban/MobileControls.tsx +++ b/src/components/kanban/MobileControls.tsx @@ -2,8 +2,7 @@ import { useState } from 'react'; import { Button } from '@/components/ui/Button'; -import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter'; -import { TfsQuickFilter } from '@/components/kanban/TfsQuickFilter'; +import { SourceQuickFilter } from '@/components/kanban/SourceQuickFilter'; import { FontSizeToggle } from '@/components/ui/FontSizeToggle'; import type { KanbanFilters } from '@/lib/types'; @@ -148,17 +147,13 @@ export function MobileControls({
- {/* Section Jira & TFS */} + {/* Section Sources */}

- Raccourcis + Sources

- - diff --git a/src/components/kanban/SourceQuickFilter.tsx b/src/components/kanban/SourceQuickFilter.tsx new file mode 100644 index 0000000..0b895eb --- /dev/null +++ b/src/components/kanban/SourceQuickFilter.tsx @@ -0,0 +1,206 @@ +'use client'; + +import { useState, useMemo, useRef, useEffect } from 'react'; +import { useTasksContext } from '@/contexts/TasksContext'; +import type { KanbanFilters } from '@/lib/types'; + +interface SourceQuickFilterProps { + filters: KanbanFilters; + onFiltersChange: (filters: KanbanFilters) => void; +} + +interface SourceOption { + id: 'jira' | 'tfs'; + label: string; + icon: string; + hasTasks: boolean; +} + +type FilterMode = 'all' | 'show' | 'hide'; + +export function SourceQuickFilter({ filters, onFiltersChange }: SourceQuickFilterProps) { + const { regularTasks } = useTasksContext(); + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + // Vérifier quelles sources ont des tâches + const sources = useMemo((): SourceOption[] => { + const hasJiraTasks = regularTasks.some(task => task.source === 'jira'); + const hasTfsTasks = regularTasks.some(task => task.source === 'tfs'); + + return [ + { + id: 'jira' as const, + label: 'Jira', + icon: '🔹', + hasTasks: hasJiraTasks + }, + { + id: 'tfs' as const, + label: 'TFS', + icon: '🔷', + hasTasks: hasTfsTasks + } + ].filter(source => source.hasTasks); + }, [regularTasks]); + + // Fermer le dropdown quand on clique ailleurs + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // Si aucune source disponible, on n'affiche rien + if (sources.length === 0) { + return null; + } + + // Déterminer l'état actuel de chaque source + const getSourceMode = (sourceId: 'jira' | 'tfs'): FilterMode => { + if (sourceId === 'jira') { + return filters.showJiraOnly ? 'show' : filters.hideJiraTasks ? 'hide' : 'all'; + } else { + return filters.showTfsOnly ? 'show' : filters.hideTfsTasks ? 'hide' : 'all'; + } + }; + + const handleModeChange = (sourceId: 'jira' | 'tfs', mode: FilterMode) => { + const updates: Partial = {}; + + if (sourceId === 'jira') { + updates.showJiraOnly = mode === 'show'; + updates.hideJiraTasks = mode === 'hide'; + } else { + updates.showTfsOnly = mode === 'show'; + updates.hideTfsTasks = mode === 'hide'; + } + + onFiltersChange({ ...filters, ...updates }); + }; + + // Déterminer le texte du bouton principal + const getMainButtonText = () => { + const activeFilters = sources.filter(source => { + const mode = getSourceMode(source.id); + return mode !== 'all'; + }); + + if (activeFilters.length === 0) { + return 'All sources'; + } else if (activeFilters.length === 1) { + const source = activeFilters[0]; + const mode = getSourceMode(source.id); + return mode === 'show' ? `${source.label} only` : `No ${source.label}`; + } else { + return `${activeFilters.length} filters`; + } + }; + + const getMainButtonStyle = () => { + const activeFilters = sources.filter(source => { + const mode = getSourceMode(source.id); + return mode !== 'all'; + }); + + if (activeFilters.length === 0) { + return 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50'; + } else { + return 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30'; + } + }; + + return ( +
+ {/* Bouton principal */} + + + {/* Dropdown */} + {isOpen && ( +
+
+ {sources.map((source) => { + const currentMode = getSourceMode(source.id); + + return ( +
+
+ {source.icon} + {source.label} +
+ +
+ {[ + { mode: 'all' as FilterMode, label: 'Afficher tout', icon: '👁️' }, + { mode: 'show' as FilterMode, label: 'Seulement cette source', icon: '✅' }, + { mode: 'hide' as FilterMode, label: 'Masquer cette source', icon: '🚫' } + ].map(({ mode, label, icon }) => ( + + ))} +
+
+ ); + })} + + {/* Option pour réinitialiser tous les filtres */} +
+ +
+
+
+ )} +
+ ); +} + diff --git a/src/components/kanban/TfsQuickFilter.tsx b/src/components/kanban/TfsQuickFilter.tsx deleted file mode 100644 index 692374f..0000000 --- a/src/components/kanban/TfsQuickFilter.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import { useMemo } from 'react'; -import { useTasksContext } from '@/contexts/TasksContext'; -import type { KanbanFilters } from '@/lib/types'; - -interface TfsQuickFilterProps { - filters: KanbanFilters; - onFiltersChange: (filters: KanbanFilters) => void; -} - -export function TfsQuickFilter({ filters, onFiltersChange }: TfsQuickFilterProps) { - const { regularTasks } = useTasksContext(); - - // Vérifier s'il y a des tâches TFS dans le système - const hasTfsTasks = useMemo(() => { - return regularTasks.some(task => task.source === 'tfs'); - }, [regularTasks]); - - // Si pas de tâches TFS, on n'affiche rien - if (!hasTfsTasks) { - return null; - } - - // Déterminer l'état actuel - const currentMode = filters.showTfsOnly ? 'show' : filters.hideTfsTasks ? 'hide' : 'all'; - - const handleTfsCycle = () => { - const updates: Partial = {}; - - // Cycle : All -> TFS only -> No TFS -> All - switch (currentMode) { - case 'all': - // All -> TFS only - updates.showTfsOnly = true; - updates.hideTfsTasks = false; - break; - case 'show': - // TFS only -> No TFS - updates.showTfsOnly = false; - updates.hideTfsTasks = true; - break; - case 'hide': - // No TFS -> All - updates.showTfsOnly = false; - updates.hideTfsTasks = false; - break; - } - - onFiltersChange({ ...filters, ...updates }); - }; - - // Définir l'apparence selon l'état - const getButtonStyle = () => { - switch (currentMode) { - case 'show': - return 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30'; - case 'hide': - return 'bg-red-500/20 text-red-400 border border-red-400/30'; - default: - return 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50'; - } - }; - - const getButtonContent = () => { - switch (currentMode) { - case 'show': - return { icon: '🔷', text: 'TFS only' }; - case 'hide': - return { icon: '🚫', text: 'No TFS' }; - default: - return { icon: '📦', text: 'All tasks' }; - } - }; - - const getTooltip = () => { - switch (currentMode) { - case 'all': - return 'Cliquer pour afficher seulement TFS'; - case 'show': - return 'Cliquer pour masquer TFS'; - case 'hide': - return 'Cliquer pour afficher tout'; - default: - return 'Filtrer les tâches TFS'; - } - }; - - const content = getButtonContent(); - - return ( - - ); -}