feat: integrate Jira and TFS filters into KanbanFilters
- Replaced existing Jira and TFS toggle handlers with `JiraFilters` and `TfsFilters` components for improved modularity and maintainability. - Streamlined filter management by encapsulating logic within dedicated components, enhancing readability and future extensibility.
This commit is contained in:
@@ -11,6 +11,8 @@ import { SORT_OPTIONS } from '@/lib/sort-config';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { ColumnVisibilityToggle } from './ColumnVisibilityToggle';
|
||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||
import { JiraFilters } from './filters/JiraFilters';
|
||||
import { TfsFilters } from './filters/TfsFilters';
|
||||
|
||||
export interface KanbanFilters {
|
||||
search?: string;
|
||||
@@ -145,126 +147,11 @@ export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsH
|
||||
setIsSortExpanded(!isSortExpanded);
|
||||
};
|
||||
|
||||
const handleJiraToggle = (mode: 'show' | 'hide' | 'all') => {
|
||||
const updates: Partial<KanbanFilters> = {};
|
||||
|
||||
switch (mode) {
|
||||
case 'show':
|
||||
updates.showJiraOnly = true;
|
||||
updates.hideJiraTasks = false;
|
||||
break;
|
||||
case 'hide':
|
||||
updates.showJiraOnly = false;
|
||||
updates.hideJiraTasks = true;
|
||||
break;
|
||||
case 'all':
|
||||
updates.showJiraOnly = false;
|
||||
updates.hideJiraTasks = false;
|
||||
break;
|
||||
}
|
||||
|
||||
onFiltersChange({ ...filters, ...updates });
|
||||
};
|
||||
|
||||
const handleTfsToggle = (mode: 'show' | 'hide' | 'all') => {
|
||||
const updates: Partial<KanbanFilters> = {};
|
||||
|
||||
switch (mode) {
|
||||
case 'show':
|
||||
updates.showTfsOnly = true;
|
||||
updates.hideTfsTasks = false;
|
||||
break;
|
||||
case 'hide':
|
||||
updates.showTfsOnly = false;
|
||||
updates.hideTfsTasks = true;
|
||||
break;
|
||||
case 'all':
|
||||
updates.showTfsOnly = false;
|
||||
updates.hideTfsTasks = false;
|
||||
break;
|
||||
}
|
||||
|
||||
onFiltersChange({ ...filters, ...updates });
|
||||
};
|
||||
|
||||
const handleJiraProjectToggle = (project: string) => {
|
||||
const currentProjects = filters.jiraProjects || [];
|
||||
const newProjects = currentProjects.includes(project)
|
||||
? currentProjects.filter(p => p !== project)
|
||||
: [...currentProjects, project];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
jiraProjects: newProjects
|
||||
});
|
||||
};
|
||||
|
||||
const handleJiraTypeToggle = (type: string) => {
|
||||
const currentTypes = filters.jiraTypes || [];
|
||||
const newTypes = currentTypes.includes(type)
|
||||
? currentTypes.filter(t => t !== type)
|
||||
: [...currentTypes, type];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
jiraTypes: newTypes
|
||||
});
|
||||
};
|
||||
|
||||
const handleTfsProjectToggle = (project: string) => {
|
||||
const currentProjects = filters.tfsProjects || [];
|
||||
const newProjects = currentProjects.includes(project)
|
||||
? currentProjects.filter(p => p !== project)
|
||||
: [...currentProjects, project];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
tfsProjects: newProjects
|
||||
});
|
||||
};
|
||||
|
||||
const handleClearFilters = () => {
|
||||
onFiltersChange({});
|
||||
};
|
||||
|
||||
// Récupérer les projets et types Jira disponibles dans TOUTES les tâches (pas seulement les filtrées)
|
||||
// regularTasks est déjà disponible depuis la ligne 39
|
||||
const availableJiraProjects = useMemo(() => {
|
||||
const projects = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'jira' && task.jiraProject) {
|
||||
projects.add(task.jiraProject);
|
||||
}
|
||||
});
|
||||
return Array.from(projects).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
const availableJiraTypes = useMemo(() => {
|
||||
const types = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'jira' && task.jiraType) {
|
||||
types.add(task.jiraType);
|
||||
}
|
||||
});
|
||||
return Array.from(types).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
// Vérifier s'il y a des tâches Jira dans le système (même masquées)
|
||||
const hasJiraTasks = regularTasks.some(task => task.source === 'jira');
|
||||
|
||||
// Récupérer les projets TFS disponibles dans TOUTES les tâches
|
||||
const availableTfsProjects = useMemo(() => {
|
||||
const projects = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'tfs' && task.tfsProject) {
|
||||
projects.add(task.tfsProject);
|
||||
}
|
||||
});
|
||||
return Array.from(projects).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
// Vérifier s'il y a des tâches TFS dans le système (même masquées)
|
||||
const hasTfsTasks = regularTasks.some(task => task.source === 'tfs');
|
||||
|
||||
// Calculer les compteurs pour les priorités
|
||||
const priorityCounts = useMemo(() => {
|
||||
@@ -464,164 +351,17 @@ export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsH
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Filtres Jira - Ligne séparée mais intégrée */}
|
||||
{hasJiraTasks && (
|
||||
<div className="border-t border-[var(--border)]/30 pt-4 mt-4">
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
<label className="block text-xs font-mono font-medium text-[var(--muted-foreground)] uppercase tracking-wider">
|
||||
🔌 Jira
|
||||
</label>
|
||||
|
||||
{/* Toggle Jira Show/Hide - inline avec le titre */}
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant={filters.showJiraOnly ? "primary" : "ghost"}
|
||||
onClick={() => handleJiraToggle('show')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🔹 Seul
|
||||
</Button>
|
||||
<Button
|
||||
variant={filters.hideJiraTasks ? "danger" : "ghost"}
|
||||
onClick={() => handleJiraToggle('hide')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🚫 Mask
|
||||
</Button>
|
||||
<Button
|
||||
variant={(!filters.showJiraOnly && !filters.hideJiraTasks) ? "primary" : "ghost"}
|
||||
onClick={() => handleJiraToggle('all')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
📋 All
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Filtres Jira */}
|
||||
<JiraFilters
|
||||
filters={filters}
|
||||
onFiltersChange={onFiltersChange}
|
||||
/>
|
||||
|
||||
{/* Projets et Types en 2 colonnes */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Projets Jira */}
|
||||
{availableJiraProjects.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Projets
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableJiraProjects.map((project) => (
|
||||
<button
|
||||
key={project}
|
||||
onClick={() => handleJiraProjectToggle(project)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.jiraProjects?.includes(project)
|
||||
? 'border-blue-400 bg-blue-400/10 text-blue-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
📋 {project}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Types Jira */}
|
||||
{availableJiraTypes.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Types
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableJiraTypes.map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => handleJiraTypeToggle(type)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.jiraTypes?.includes(type)
|
||||
? 'border-purple-400 bg-purple-400/10 text-purple-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
{type === 'Feature' && '✨ '}
|
||||
{type === 'Story' && '📖 '}
|
||||
{type === 'Task' && '📝 '}
|
||||
{type === 'Bug' && '🐛 '}
|
||||
{type === 'Support' && '🛠️ '}
|
||||
{type === 'Enabler' && '🔧 '}
|
||||
{type}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Filtres TFS - Ligne séparée mais intégrée */}
|
||||
{hasTfsTasks && (
|
||||
<div className="border-t border-[var(--border)]/30 pt-4 mt-4">
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
<label className="block text-xs font-mono font-medium text-[var(--muted-foreground)] uppercase tracking-wider">
|
||||
📦 TFS
|
||||
</label>
|
||||
|
||||
{/* Toggle TFS Show/Hide - inline avec le titre */}
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant={filters.showTfsOnly ? "primary" : "ghost"}
|
||||
onClick={() => handleTfsToggle('show')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🔷 Seul
|
||||
</Button>
|
||||
<Button
|
||||
variant={filters.hideTfsTasks ? "danger" : "ghost"}
|
||||
onClick={() => handleTfsToggle('hide')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🚫 Mask
|
||||
</Button>
|
||||
<Button
|
||||
variant={(!filters.showTfsOnly && !filters.hideTfsTasks) ? "primary" : "ghost"}
|
||||
onClick={() => handleTfsToggle('all')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
📋 All
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projets TFS */}
|
||||
{availableTfsProjects.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Projets
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableTfsProjects.map((project) => (
|
||||
<button
|
||||
key={project}
|
||||
onClick={() => handleTfsProjectToggle(project)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.tfsProjects?.includes(project)
|
||||
? 'border-orange-400 bg-orange-400/10 text-orange-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
📦 {project}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Filtres TFS */}
|
||||
<TfsFilters
|
||||
filters={filters}
|
||||
onFiltersChange={onFiltersChange}
|
||||
/>
|
||||
|
||||
{/* Visibilité des colonnes */}
|
||||
<div className="col-span-full border-t border-[var(--border)]/50 pt-6 mt-4">
|
||||
|
||||
185
src/components/kanban/filters/JiraFilters.tsx
Normal file
185
src/components/kanban/filters/JiraFilters.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { KanbanFilters } from '../KanbanFilters';
|
||||
|
||||
interface JiraFiltersProps {
|
||||
filters: KanbanFilters;
|
||||
onFiltersChange: (filters: KanbanFilters) => void;
|
||||
}
|
||||
|
||||
export function JiraFilters({ filters, onFiltersChange }: JiraFiltersProps) {
|
||||
const { regularTasks } = useTasksContext();
|
||||
|
||||
// Vérifier s'il y a des tâches Jira dans le système (même masquées)
|
||||
const hasJiraTasks = regularTasks.some(task => task.source === 'jira');
|
||||
|
||||
// Récupérer les projets et types Jira disponibles dans TOUTES les tâches
|
||||
const availableJiraProjects = useMemo(() => {
|
||||
const projects = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'jira' && task.jiraProject) {
|
||||
projects.add(task.jiraProject);
|
||||
}
|
||||
});
|
||||
return Array.from(projects).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
const availableJiraTypes = useMemo(() => {
|
||||
const types = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'jira' && task.jiraType) {
|
||||
types.add(task.jiraType);
|
||||
}
|
||||
});
|
||||
return Array.from(types).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
const handleJiraToggle = (mode: 'show' | 'hide' | 'all') => {
|
||||
const updates: Partial<KanbanFilters> = {};
|
||||
|
||||
switch (mode) {
|
||||
case 'show':
|
||||
updates.showJiraOnly = true;
|
||||
updates.hideJiraTasks = false;
|
||||
break;
|
||||
case 'hide':
|
||||
updates.showJiraOnly = false;
|
||||
updates.hideJiraTasks = true;
|
||||
break;
|
||||
case 'all':
|
||||
updates.showJiraOnly = false;
|
||||
updates.hideJiraTasks = false;
|
||||
break;
|
||||
}
|
||||
|
||||
onFiltersChange({ ...filters, ...updates });
|
||||
};
|
||||
|
||||
const handleJiraProjectToggle = (project: string) => {
|
||||
const currentProjects = filters.jiraProjects || [];
|
||||
const newProjects = currentProjects.includes(project)
|
||||
? currentProjects.filter(p => p !== project)
|
||||
: [...currentProjects, project];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
jiraProjects: newProjects
|
||||
});
|
||||
};
|
||||
|
||||
const handleJiraTypeToggle = (type: string) => {
|
||||
const currentTypes = filters.jiraTypes || [];
|
||||
const newTypes = currentTypes.includes(type)
|
||||
? currentTypes.filter(t => t !== type)
|
||||
: [...currentTypes, type];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
jiraTypes: newTypes
|
||||
});
|
||||
};
|
||||
|
||||
// Si pas de tâches Jira, on n'affiche rien
|
||||
if (!hasJiraTasks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-t border-[var(--border)]/30 pt-4 mt-4">
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
<label className="block text-xs font-mono font-medium text-[var(--muted-foreground)] uppercase tracking-wider">
|
||||
🔌 Jira
|
||||
</label>
|
||||
|
||||
{/* Toggle Jira Show/Hide - inline avec le titre */}
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant={filters.showJiraOnly ? "primary" : "ghost"}
|
||||
onClick={() => handleJiraToggle('show')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🔹 Seul
|
||||
</Button>
|
||||
<Button
|
||||
variant={filters.hideJiraTasks ? "danger" : "ghost"}
|
||||
onClick={() => handleJiraToggle('hide')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🚫 Mask
|
||||
</Button>
|
||||
<Button
|
||||
variant={(!filters.showJiraOnly && !filters.hideJiraTasks) ? "primary" : "ghost"}
|
||||
onClick={() => handleJiraToggle('all')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
📋 All
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projets et Types en 2 colonnes */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Projets Jira */}
|
||||
{availableJiraProjects.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Projets
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableJiraProjects.map((project) => (
|
||||
<button
|
||||
key={project}
|
||||
onClick={() => handleJiraProjectToggle(project)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.jiraProjects?.includes(project)
|
||||
? 'border-blue-400 bg-blue-400/10 text-blue-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
📋 {project}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Types Jira */}
|
||||
{availableJiraTypes.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Types
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableJiraTypes.map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => handleJiraTypeToggle(type)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.jiraTypes?.includes(type)
|
||||
? 'border-purple-400 bg-purple-400/10 text-purple-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
{type === 'Feature' && '✨ '}
|
||||
{type === 'Story' && '📖 '}
|
||||
{type === 'Task' && '📝 '}
|
||||
{type === 'Bug' && '🐛 '}
|
||||
{type === 'Support' && '🛠️ '}
|
||||
{type === 'Enabler' && '🔧 '}
|
||||
{type}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
130
src/components/kanban/filters/TfsFilters.tsx
Normal file
130
src/components/kanban/filters/TfsFilters.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { KanbanFilters } from '../KanbanFilters';
|
||||
|
||||
interface TfsFiltersProps {
|
||||
filters: KanbanFilters;
|
||||
onFiltersChange: (filters: KanbanFilters) => void;
|
||||
}
|
||||
|
||||
export function TfsFilters({ filters, onFiltersChange }: TfsFiltersProps) {
|
||||
const { regularTasks } = useTasksContext();
|
||||
|
||||
// Vérifier s'il y a des tâches TFS dans le système (même masquées)
|
||||
const hasTfsTasks = regularTasks.some(task => task.source === 'tfs');
|
||||
|
||||
// Récupérer les projets TFS disponibles dans TOUTES les tâches
|
||||
const availableTfsProjects = useMemo(() => {
|
||||
const projects = new Set<string>();
|
||||
regularTasks.forEach(task => {
|
||||
if (task.source === 'tfs' && task.tfsProject) {
|
||||
projects.add(task.tfsProject);
|
||||
}
|
||||
});
|
||||
return Array.from(projects).sort();
|
||||
}, [regularTasks]);
|
||||
|
||||
const handleTfsToggle = (mode: 'show' | 'hide' | 'all') => {
|
||||
const updates: Partial<KanbanFilters> = {};
|
||||
|
||||
switch (mode) {
|
||||
case 'show':
|
||||
updates.showTfsOnly = true;
|
||||
updates.hideTfsTasks = false;
|
||||
break;
|
||||
case 'hide':
|
||||
updates.showTfsOnly = false;
|
||||
updates.hideTfsTasks = true;
|
||||
break;
|
||||
case 'all':
|
||||
updates.showTfsOnly = false;
|
||||
updates.hideTfsTasks = false;
|
||||
break;
|
||||
}
|
||||
|
||||
onFiltersChange({ ...filters, ...updates });
|
||||
};
|
||||
|
||||
const handleTfsProjectToggle = (project: string) => {
|
||||
const currentProjects = filters.tfsProjects || [];
|
||||
const newProjects = currentProjects.includes(project)
|
||||
? currentProjects.filter(p => p !== project)
|
||||
: [...currentProjects, project];
|
||||
|
||||
onFiltersChange({
|
||||
...filters,
|
||||
tfsProjects: newProjects
|
||||
});
|
||||
};
|
||||
|
||||
// Si pas de tâches TFS, on n'affiche rien
|
||||
if (!hasTfsTasks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-t border-[var(--border)]/30 pt-4 mt-4">
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
<label className="block text-xs font-mono font-medium text-[var(--muted-foreground)] uppercase tracking-wider">
|
||||
📦 TFS
|
||||
</label>
|
||||
|
||||
{/* Toggle TFS Show/Hide - inline avec le titre */}
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant={filters.showTfsOnly ? "primary" : "ghost"}
|
||||
onClick={() => handleTfsToggle('show')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🔷 Seul
|
||||
</Button>
|
||||
<Button
|
||||
variant={filters.hideTfsTasks ? "danger" : "ghost"}
|
||||
onClick={() => handleTfsToggle('hide')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
🚫 Mask
|
||||
</Button>
|
||||
<Button
|
||||
variant={(!filters.showTfsOnly && !filters.hideTfsTasks) ? "primary" : "ghost"}
|
||||
onClick={() => handleTfsToggle('all')}
|
||||
size="sm"
|
||||
className="text-xs px-2 py-1 h-auto"
|
||||
>
|
||||
📋 All
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projets TFS */}
|
||||
{availableTfsProjects.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-[var(--muted-foreground)] mb-2">
|
||||
Projets
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{availableTfsProjects.map((project) => (
|
||||
<button
|
||||
key={project}
|
||||
onClick={() => handleTfsProjectToggle(project)}
|
||||
className={`px-2 py-1 rounded border transition-all text-xs font-medium ${
|
||||
filters.tfsProjects?.includes(project)
|
||||
? 'border-orange-400 bg-orange-400/10 text-orange-400'
|
||||
: 'border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)] hover:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
📦 {project}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user