feat: refactor IntegrationFilter for Kanban and Dashboard compatibility
- Updated IntegrationFilter to support both Kanban and Dashboard modes with new filters for manual tasks. - Replaced SourceQuickFilter with IntegrationFilter in Desktop and Mobile controls for consistency. - Removed deprecated SourceQuickFilter component to streamline codebase. - Enhanced task filtering logic to include pinned tasks and manual task visibility.
This commit is contained in:
@@ -3,12 +3,18 @@
|
|||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { useTasksContext } from '@/contexts/TasksContext';
|
import { useTasksContext } from '@/contexts/TasksContext';
|
||||||
import { Dropdown, Button } from '@/components/ui';
|
import { Dropdown, Button } from '@/components/ui';
|
||||||
|
import type { KanbanFilters } from '@/lib/types';
|
||||||
|
|
||||||
interface IntegrationFilterProps {
|
interface IntegrationFilterProps {
|
||||||
selectedSources: string[];
|
// Interface pour Kanban (nouvelle)
|
||||||
onSourcesChange: (sources: string[]) => void;
|
filters?: KanbanFilters;
|
||||||
hiddenSources: string[];
|
onFiltersChange?: (filters: KanbanFilters) => void;
|
||||||
onHiddenSourcesChange: (sources: string[]) => void;
|
|
||||||
|
// Interface pour Dashboard (ancienne)
|
||||||
|
selectedSources?: string[];
|
||||||
|
onSourcesChange?: (sources: string[]) => void;
|
||||||
|
hiddenSources?: string[];
|
||||||
|
onHiddenSourcesChange?: (sources: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SourceOption {
|
interface SourceOption {
|
||||||
@@ -20,15 +26,24 @@ interface SourceOption {
|
|||||||
|
|
||||||
type FilterMode = 'all' | 'show' | 'hide';
|
type FilterMode = 'all' | 'show' | 'hide';
|
||||||
|
|
||||||
export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSources, onHiddenSourcesChange }: IntegrationFilterProps) {
|
export function IntegrationFilter({
|
||||||
const { regularTasks } = useTasksContext();
|
filters,
|
||||||
|
onFiltersChange,
|
||||||
|
selectedSources,
|
||||||
|
onSourcesChange,
|
||||||
|
hiddenSources,
|
||||||
|
onHiddenSourcesChange
|
||||||
|
}: IntegrationFilterProps) {
|
||||||
|
const { regularTasks, pinnedTasks } = useTasksContext();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
// Vérifier quelles sources ont des tâches
|
// Vérifier quelles sources ont des tâches (regularTasks + pinnedTasks)
|
||||||
const sources = useMemo((): SourceOption[] => {
|
const sources = useMemo((): SourceOption[] => {
|
||||||
const hasJiraTasks = regularTasks.some(task => task.source === 'jira');
|
const allTasks = [...regularTasks, ...pinnedTasks];
|
||||||
const hasTfsTasks = regularTasks.some(task => task.source === 'tfs');
|
const hasJiraTasks = allTasks.some(task => task.source === 'jira');
|
||||||
const hasManualTasks = regularTasks.some(task => task.source === 'manual');
|
const hasTfsTasks = allTasks.some(task => task.source === 'tfs');
|
||||||
|
const hasManualTasks = allTasks.some(task => task.source === 'manual');
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -50,7 +65,7 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
hasTasks: hasManualTasks
|
hasTasks: hasManualTasks
|
||||||
}
|
}
|
||||||
].filter(source => source.hasTasks);
|
].filter(source => source.hasTasks);
|
||||||
}, [regularTasks]);
|
}, [regularTasks, pinnedTasks]);
|
||||||
|
|
||||||
// Si aucune source disponible, on n'affiche rien
|
// Si aucune source disponible, on n'affiche rien
|
||||||
if (sources.length === 0) {
|
if (sources.length === 0) {
|
||||||
@@ -58,53 +73,107 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleModeChange = (sourceId: string, mode: FilterMode) => {
|
// Déterminer le mode d'utilisation (Kanban ou Dashboard)
|
||||||
let newSelectedSources = [...selectedSources];
|
const isKanbanMode = filters && onFiltersChange;
|
||||||
let newHiddenSources = [...hiddenSources];
|
const isDashboardMode = selectedSources && onSourcesChange && hiddenSources && onHiddenSourcesChange;
|
||||||
|
|
||||||
if (mode === 'show') {
|
// Déterminer l'état actuel de chaque source
|
||||||
// Ajouter à selectedSources et retirer de hiddenSources
|
const getSourceMode = (sourceId: 'jira' | 'tfs' | 'manual'): FilterMode => {
|
||||||
if (!newSelectedSources.includes(sourceId)) {
|
if (isKanbanMode && filters) {
|
||||||
newSelectedSources.push(sourceId);
|
if (sourceId === 'jira') {
|
||||||
|
return filters.showJiraOnly ? 'show' : filters.hideJiraTasks ? 'hide' : 'all';
|
||||||
|
} else if (sourceId === 'tfs') {
|
||||||
|
return filters.showTfsOnly ? 'show' : filters.hideTfsTasks ? 'hide' : 'all';
|
||||||
|
} else { // manual
|
||||||
|
return filters.showManualOnly ? 'show' : filters.hideManualTasks ? 'hide' : 'all';
|
||||||
}
|
}
|
||||||
newHiddenSources = newHiddenSources.filter(id => id !== sourceId);
|
} else if (isDashboardMode && selectedSources && hiddenSources) {
|
||||||
} else if (mode === 'hide') {
|
if (selectedSources.includes(sourceId)) {
|
||||||
// Ajouter à hiddenSources et retirer de selectedSources
|
return 'show';
|
||||||
if (!newHiddenSources.includes(sourceId)) {
|
} else if (hiddenSources.includes(sourceId)) {
|
||||||
newHiddenSources.push(sourceId);
|
return 'hide';
|
||||||
|
} else {
|
||||||
|
return 'all';
|
||||||
}
|
}
|
||||||
newSelectedSources = newSelectedSources.filter(id => id !== sourceId);
|
|
||||||
} else { // 'all'
|
|
||||||
// Retirer des deux listes
|
|
||||||
newSelectedSources = newSelectedSources.filter(id => id !== sourceId);
|
|
||||||
newHiddenSources = newHiddenSources.filter(id => id !== sourceId);
|
|
||||||
}
|
}
|
||||||
|
return 'all';
|
||||||
onHiddenSourcesChange(newHiddenSources);
|
};
|
||||||
onSourcesChange(newSelectedSources);
|
|
||||||
|
const handleModeChange = (sourceId: 'jira' | 'tfs' | 'manual', mode: FilterMode) => {
|
||||||
|
if (isKanbanMode && filters && onFiltersChange) {
|
||||||
|
const updates: Partial<KanbanFilters> = {};
|
||||||
|
|
||||||
|
if (sourceId === 'jira') {
|
||||||
|
updates.showJiraOnly = mode === 'show';
|
||||||
|
updates.hideJiraTasks = mode === 'hide';
|
||||||
|
} else if (sourceId === 'tfs') {
|
||||||
|
updates.showTfsOnly = mode === 'show';
|
||||||
|
updates.hideTfsTasks = mode === 'hide';
|
||||||
|
} else if (sourceId === 'manual') {
|
||||||
|
updates.showManualOnly = mode === 'show';
|
||||||
|
updates.hideManualTasks = mode === 'hide';
|
||||||
|
}
|
||||||
|
|
||||||
|
onFiltersChange({ ...filters, ...updates });
|
||||||
|
} else if (isDashboardMode && onSourcesChange && onHiddenSourcesChange && selectedSources && hiddenSources) {
|
||||||
|
let newSelectedSources = [...selectedSources];
|
||||||
|
let newHiddenSources = [...hiddenSources];
|
||||||
|
|
||||||
|
if (mode === 'show') {
|
||||||
|
// Ajouter à selectedSources et retirer de hiddenSources
|
||||||
|
if (!newSelectedSources.includes(sourceId)) {
|
||||||
|
newSelectedSources.push(sourceId);
|
||||||
|
}
|
||||||
|
newHiddenSources = newHiddenSources.filter(id => id !== sourceId);
|
||||||
|
} else if (mode === 'hide') {
|
||||||
|
// Ajouter à hiddenSources et retirer de selectedSources
|
||||||
|
if (!newHiddenSources.includes(sourceId)) {
|
||||||
|
newHiddenSources.push(sourceId);
|
||||||
|
}
|
||||||
|
newSelectedSources = newSelectedSources.filter(id => id !== sourceId);
|
||||||
|
} else { // 'all'
|
||||||
|
// Retirer des deux listes
|
||||||
|
newSelectedSources = newSelectedSources.filter(id => id !== sourceId);
|
||||||
|
newHiddenSources = newHiddenSources.filter(id => id !== sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
onHiddenSourcesChange(newHiddenSources);
|
||||||
|
onSourcesChange(newSelectedSources);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Déterminer la variante du bouton principal
|
||||||
|
const getMainButtonVariant = () => {
|
||||||
|
const activeFilters = sources.filter(source => {
|
||||||
|
const mode = getSourceMode(source.id);
|
||||||
|
return mode !== 'all';
|
||||||
|
});
|
||||||
|
|
||||||
|
return activeFilters.length === 0 ? 'secondary' : 'selected';
|
||||||
|
};
|
||||||
|
|
||||||
const getMainButtonText = () => {
|
const getMainButtonText = () => {
|
||||||
if (selectedSources.length === 0 && hiddenSources.length === 0) {
|
const activeFilters = sources.filter(source => {
|
||||||
return 'Toutes les sources';
|
const mode = getSourceMode(source.id);
|
||||||
} else if (selectedSources.length === 1 && hiddenSources.length === 0) {
|
return mode !== 'all';
|
||||||
const source = sources.find(s => s.id === selectedSources[0]);
|
});
|
||||||
return source ? `Seulement ${source.label}` : 'Source sélectionnée';
|
|
||||||
} else if (hiddenSources.length === 1 && selectedSources.length === 0) {
|
if (activeFilters.length === 0) {
|
||||||
const source = sources.find(s => s.id === hiddenSources[0]);
|
return 'Sources';
|
||||||
return source ? `Sans ${source.label}` : 'Source masquée';
|
} else if (activeFilters.length === 1) {
|
||||||
|
const source = activeFilters[0];
|
||||||
|
const mode = getSourceMode(source.id);
|
||||||
|
return mode === 'show' ? `Seulement ${source.label}` : `Sans ${source.label}`;
|
||||||
} else {
|
} else {
|
||||||
const total = selectedSources.length + hiddenSources.length;
|
return `${activeFilters.length} filtres actifs`;
|
||||||
return `${total} filtres actifs`;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const dropdownContent = (
|
const dropdownContent = (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{sources.map((source) => {
|
{sources.map((source) => {
|
||||||
const isSelected = selectedSources.includes(source.id);
|
const currentMode = getSourceMode(source.id);
|
||||||
const isHidden = hiddenSources.includes(source.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={source.id} className="space-y-2">
|
<div key={source.id} className="space-y-2">
|
||||||
@@ -118,11 +187,13 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
{/* Bouton Afficher */}
|
{/* Bouton Afficher */}
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleModeChange(source.id, 'show');
|
handleModeChange(source.id, 'show');
|
||||||
}}
|
}}
|
||||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
isSelected
|
className={`px-2 py-1 text-xs rounded transition-colors cursor-pointer ${
|
||||||
|
currentMode === 'show'
|
||||||
? 'bg-[var(--primary)] text-[var(--primary-foreground)]'
|
? 'bg-[var(--primary)] text-[var(--primary-foreground)]'
|
||||||
: 'bg-[var(--muted)] text-[var(--muted-foreground)] hover:bg-[var(--primary)]/20'
|
: 'bg-[var(--muted)] text-[var(--muted-foreground)] hover:bg-[var(--primary)]/20'
|
||||||
}`}
|
}`}
|
||||||
@@ -134,11 +205,13 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
{/* Bouton Masquer */}
|
{/* Bouton Masquer */}
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleModeChange(source.id, 'hide');
|
handleModeChange(source.id, 'hide');
|
||||||
}}
|
}}
|
||||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
isHidden
|
className={`px-2 py-1 text-xs rounded transition-colors cursor-pointer ${
|
||||||
|
currentMode === 'hide'
|
||||||
? 'bg-[var(--destructive)] text-white'
|
? 'bg-[var(--destructive)] text-white'
|
||||||
: 'bg-[var(--muted)] text-[var(--muted-foreground)] hover:bg-[var(--destructive)]/20'
|
: 'bg-[var(--muted)] text-[var(--muted-foreground)] hover:bg-[var(--destructive)]/20'
|
||||||
}`}
|
}`}
|
||||||
@@ -158,13 +231,25 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onHiddenSourcesChange([]);
|
if (isKanbanMode && filters && onFiltersChange) {
|
||||||
onSourcesChange([]);
|
const updates: Partial<KanbanFilters> = {
|
||||||
|
showJiraOnly: false,
|
||||||
|
hideJiraTasks: false,
|
||||||
|
showTfsOnly: false,
|
||||||
|
hideTfsTasks: false,
|
||||||
|
showManualOnly: false,
|
||||||
|
hideManualTasks: false
|
||||||
|
};
|
||||||
|
onFiltersChange({ ...filters, ...updates });
|
||||||
|
} else if (isDashboardMode && onHiddenSourcesChange && onSourcesChange) {
|
||||||
|
onHiddenSourcesChange([]);
|
||||||
|
onSourcesChange([]);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="w-full justify-start font-mono"
|
className="w-full justify-start font-mono"
|
||||||
title="Réinitialiser tous les filtres de source"
|
title="Réinitialiser tous les filtres de source"
|
||||||
>
|
>
|
||||||
<span>🔄</span>
|
<span>🔄 </span>
|
||||||
<span className="flex-1">Réinitialiser tout</span>
|
<span className="flex-1">Réinitialiser tout</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -176,7 +261,10 @@ export function IntegrationFilter({ selectedSources, onSourcesChange, hiddenSour
|
|||||||
open={isOpen}
|
open={isOpen}
|
||||||
onOpenChange={setIsOpen}
|
onOpenChange={setIsOpen}
|
||||||
trigger={`🔗 ${getMainButtonText()}`}
|
trigger={`🔗 ${getMainButtonText()}`}
|
||||||
|
variant={getMainButtonVariant()}
|
||||||
content={dropdownContent}
|
content={dropdownContent}
|
||||||
|
placement="bottom-start"
|
||||||
|
className="min-w-[240px]"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { Button, ToggleButton, SearchInput, ControlPanel, ControlSection, ControlGroup } from '@/components/ui';
|
import { Button, ToggleButton, SearchInput, ControlPanel, ControlSection, ControlGroup } from '@/components/ui';
|
||||||
import { SourceQuickFilter } from '@/components/kanban/SourceQuickFilter';
|
import { IntegrationFilter } from '@/components/dashboard/IntegrationFilter';
|
||||||
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
|
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
|
||||||
import type { KanbanFilters } from '@/lib/types';
|
import type { KanbanFilters } from '@/lib/types';
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export function DesktopControls({
|
|||||||
<ControlSection className="justify-between lg:justify-start">
|
<ControlSection className="justify-between lg:justify-start">
|
||||||
<ControlGroup className="border-l border-[var(--border)] ml-2 pl-2 pr-4">
|
<ControlGroup className="border-l border-[var(--border)] ml-2 pl-2 pr-4">
|
||||||
{/* Raccourcis Sources (Jira & TFS) */}
|
{/* Raccourcis Sources (Jira & TFS) */}
|
||||||
<SourceQuickFilter
|
<IntegrationFilter
|
||||||
filters={kanbanFilters}
|
filters={kanbanFilters}
|
||||||
onFiltersChange={onFiltersChange}
|
onFiltersChange={onFiltersChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, ToggleButton, ControlPanel } from '@/components/ui';
|
import { Button, ToggleButton, ControlPanel } from '@/components/ui';
|
||||||
import { SourceQuickFilter } from '@/components/kanban/SourceQuickFilter';
|
import { IntegrationFilter } from '@/components/dashboard/IntegrationFilter';
|
||||||
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
|
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
|
||||||
import type { KanbanFilters } from '@/lib/types';
|
import type { KanbanFilters } from '@/lib/types';
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export function MobileControls({
|
|||||||
Sources
|
Sources
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<SourceQuickFilter
|
<IntegrationFilter
|
||||||
filters={kanbanFilters}
|
filters={kanbanFilters}
|
||||||
onFiltersChange={onFiltersChange}
|
onFiltersChange={onFiltersChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useMemo } from 'react';
|
|
||||||
import { useTasksContext } from '@/contexts/TasksContext';
|
|
||||||
import { Dropdown, Button } from '@/components/ui';
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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]);
|
|
||||||
|
|
||||||
|
|
||||||
// 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<KanbanFilters> = {};
|
|
||||||
|
|
||||||
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 getMainButtonVariant = () => {
|
|
||||||
const activeFilters = sources.filter(source => {
|
|
||||||
const mode = getSourceMode(source.id);
|
|
||||||
return mode !== 'all';
|
|
||||||
});
|
|
||||||
|
|
||||||
return activeFilters.length === 0 ? 'secondary' : 'selected';
|
|
||||||
};
|
|
||||||
|
|
||||||
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 dropdownContent = (
|
|
||||||
<div className="space-y-3">
|
|
||||||
{sources.map((source) => {
|
|
||||||
const currentMode = getSourceMode(source.id);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={source.id} className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2 text-sm font-mono text-[var(--muted-foreground)]">
|
|
||||||
<span>{source.icon}</span>
|
|
||||||
<span>{source.label}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1 ml-6">
|
|
||||||
{[
|
|
||||||
{ 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 }) => (
|
|
||||||
<label
|
|
||||||
key={mode}
|
|
||||||
className="flex items-center gap-2 text-sm cursor-pointer hover:text-[var(--foreground)] transition-colors"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name={`source-${source.id}`}
|
|
||||||
checked={currentMode === mode}
|
|
||||||
onChange={() => handleModeChange(source.id, mode)}
|
|
||||||
className="w-3 h-3 text-[var(--primary)] bg-[var(--background)] border-[var(--border)] focus:ring-[var(--primary)]/20"
|
|
||||||
/>
|
|
||||||
<span className="flex items-center gap-1">
|
|
||||||
<span>{icon}</span>
|
|
||||||
<span>{label}</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* Option pour réinitialiser tous les filtres */}
|
|
||||||
<div className="border-t border-[var(--border)] pt-2 mt-2">
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
const updates: Partial<KanbanFilters> = {
|
|
||||||
showJiraOnly: false,
|
|
||||||
hideJiraTasks: false,
|
|
||||||
showTfsOnly: false,
|
|
||||||
hideTfsTasks: false
|
|
||||||
};
|
|
||||||
onFiltersChange({ ...filters, ...updates });
|
|
||||||
}}
|
|
||||||
className="w-full justify-start font-mono"
|
|
||||||
title="Réinitialiser tous les filtres de source"
|
|
||||||
>
|
|
||||||
<span>🔄</span>
|
|
||||||
<span className="flex-1">Réinitialiser tout</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
open={isOpen}
|
|
||||||
onOpenChange={setIsOpen}
|
|
||||||
trigger={`🔌 ${getMainButtonText()}`}
|
|
||||||
variant={getMainButtonVariant()}
|
|
||||||
content={dropdownContent}
|
|
||||||
placement="bottom-start"
|
|
||||||
className="min-w-[240px]"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -70,7 +70,10 @@ export function TasksProvider({ children, initialTasks, initialTags, initialStat
|
|||||||
// Filtres TFS
|
// Filtres TFS
|
||||||
showTfsOnly: preferences.kanbanFilters.showTfsOnly || false,
|
showTfsOnly: preferences.kanbanFilters.showTfsOnly || false,
|
||||||
hideTfsTasks: preferences.kanbanFilters.hideTfsTasks || false,
|
hideTfsTasks: preferences.kanbanFilters.hideTfsTasks || false,
|
||||||
tfsProjects: preferences.kanbanFilters.tfsProjects || []
|
tfsProjects: preferences.kanbanFilters.tfsProjects || [],
|
||||||
|
// Filtres Manuel
|
||||||
|
showManualOnly: preferences.kanbanFilters.showManualOnly || false,
|
||||||
|
hideManualTasks: preferences.kanbanFilters.hideManualTasks || false
|
||||||
}), [preferences]);
|
}), [preferences]);
|
||||||
|
|
||||||
// Fonction pour mettre à jour les filtres avec persistance
|
// Fonction pour mettre à jour les filtres avec persistance
|
||||||
@@ -92,7 +95,10 @@ export function TasksProvider({ children, initialTasks, initialTags, initialStat
|
|||||||
// Filtres TFS
|
// Filtres TFS
|
||||||
showTfsOnly: newFilters.showTfsOnly,
|
showTfsOnly: newFilters.showTfsOnly,
|
||||||
hideTfsTasks: newFilters.hideTfsTasks,
|
hideTfsTasks: newFilters.hideTfsTasks,
|
||||||
tfsProjects: newFilters.tfsProjects
|
tfsProjects: newFilters.tfsProjects,
|
||||||
|
// Filtres Manuel
|
||||||
|
showManualOnly: newFilters.showManualOnly,
|
||||||
|
hideManualTasks: newFilters.hideManualTasks
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewPreferenceUpdates = {
|
const viewPreferenceUpdates = {
|
||||||
@@ -151,7 +157,9 @@ export function TasksProvider({ children, initialTasks, initialTags, initialStat
|
|||||||
(kanbanFilters.hideJiraTasks ? 1 : 0) +
|
(kanbanFilters.hideJiraTasks ? 1 : 0) +
|
||||||
(kanbanFilters.tfsProjects?.filter(Boolean).length || 0) +
|
(kanbanFilters.tfsProjects?.filter(Boolean).length || 0) +
|
||||||
(kanbanFilters.showTfsOnly ? 1 : 0) +
|
(kanbanFilters.showTfsOnly ? 1 : 0) +
|
||||||
(kanbanFilters.hideTfsTasks ? 1 : 0);
|
(kanbanFilters.hideTfsTasks ? 1 : 0) +
|
||||||
|
(kanbanFilters.showManualOnly ? 1 : 0) +
|
||||||
|
(kanbanFilters.hideManualTasks ? 1 : 0);
|
||||||
}, [kanbanFilters]);
|
}, [kanbanFilters]);
|
||||||
|
|
||||||
// Filtrage et tri des tâches régulières (pas les épinglées)
|
// Filtrage et tri des tâches régulières (pas les épinglées)
|
||||||
@@ -212,6 +220,13 @@ export function TasksProvider({ children, initialTasks, initialTags, initialStat
|
|||||||
filtered = filtered.filter(task => task.source !== 'tfs');
|
filtered = filtered.filter(task => task.source !== 'tfs');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filtres spécifiques Manuel
|
||||||
|
if (kanbanFilters.showManualOnly) {
|
||||||
|
filtered = filtered.filter(task => task.source === 'manual');
|
||||||
|
} else if (kanbanFilters.hideManualTasks) {
|
||||||
|
filtered = filtered.filter(task => task.source !== 'manual');
|
||||||
|
}
|
||||||
|
|
||||||
// Filtre par projets TFS
|
// Filtre par projets TFS
|
||||||
if (kanbanFilters.tfsProjects?.length) {
|
if (kanbanFilters.tfsProjects?.length) {
|
||||||
filtered = filtered.filter(task =>
|
filtered = filtered.filter(task =>
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ export interface KanbanFilters {
|
|||||||
showTfsOnly?: boolean;
|
showTfsOnly?: boolean;
|
||||||
hideTfsTasks?: boolean;
|
hideTfsTasks?: boolean;
|
||||||
tfsProjects?: string[];
|
tfsProjects?: string[];
|
||||||
|
// Filtres spécifiques Manuel
|
||||||
|
showManualOnly?: boolean;
|
||||||
|
hideManualTasks?: boolean;
|
||||||
[key: string]: string | string[] | TaskPriority[] | boolean | undefined;
|
[key: string]: string | string[] | TaskPriority[] | boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user