'use client'; import { createContext, useContext, ReactNode, useMemo } from 'react'; import { useTasks } from '@/hooks/useTasks'; import { useTags } from '@/hooks/useTags'; import { useUserPreferences } from './UserPreferencesContext'; import { Task, Tag, TaskStats, TaskStatus } from '@/lib/types'; import { CreateTaskData, TaskFilters } from '@/clients/tasks-client'; import type { KanbanFilters } from '@/lib/types'; import { sortTasks, getSortOption, DEFAULT_SORT, createSortKey } from '@/lib/sort-config'; interface TasksContextType { tasks: Task[]; // Toutes les tâches regularTasks: Task[]; // Tâches sans les épinglées (pour Kanban) stats: TaskStats; loading: boolean; syncing: boolean; error: string | null; createTask: (data: CreateTaskData) => Promise; updateTaskOptimistic: (taskId: string, status: TaskStatus) => Promise; refreshTasks: () => Promise; setFilters: (filters: TaskFilters) => void; // Kanban filters kanbanFilters: KanbanFilters; setKanbanFilters: (filters: KanbanFilters) => void; filteredTasks: Task[]; pinnedTasks: Task[]; // Tâches avec tags épinglés (objectifs) activeFiltersCount: number; // Nombre de filtres actifs // Tags tags: Tag[]; tagsLoading: boolean; tagsError: string | null; } const TasksContext = createContext(null); interface TasksProviderProps { children: ReactNode; initialTasks: Task[]; initialTags?: (Tag & { usage: number })[]; initialStats?: TaskStats; } export function TasksProvider({ children, initialTasks, initialTags, initialStats }: TasksProviderProps) { const tasksState = useTasks( { limit: 20 }, { tasks: initialTasks, stats: initialStats } ); const { tags, loading: tagsLoading, error: tagsError } = useTags(initialTags); const { preferences, updateKanbanFilters, updateViewPreferences } = useUserPreferences(); // Construire l'objet KanbanFilters à partir des préférences const kanbanFilters: KanbanFilters = useMemo(() => ({ search: preferences.kanbanFilters.search || '', tags: preferences.kanbanFilters.tags || [], priorities: preferences.kanbanFilters.priorities || [], showCompleted: preferences.kanbanFilters.showCompleted ?? true, sortBy: preferences.kanbanFilters.sortBy || createSortKey('priority', 'desc'), compactView: preferences.viewPreferences.compactView || false, swimlanesByTags: preferences.viewPreferences.swimlanesByTags || false, swimlanesMode: preferences.viewPreferences.swimlanesMode || 'tags', showWithDueDate: preferences.kanbanFilters.showWithDueDate || false, showCompletedLast7Days: preferences.kanbanFilters.showCompletedLast7Days || false, // Filtres Jira showJiraOnly: preferences.kanbanFilters.showJiraOnly || false, hideJiraTasks: preferences.kanbanFilters.hideJiraTasks || false, jiraProjects: preferences.kanbanFilters.jiraProjects || [], jiraTypes: preferences.kanbanFilters.jiraTypes || [], // Filtres TFS showTfsOnly: preferences.kanbanFilters.showTfsOnly || false, hideTfsTasks: preferences.kanbanFilters.hideTfsTasks || false, tfsProjects: preferences.kanbanFilters.tfsProjects || [] }), [preferences]); // Fonction pour mettre à jour les filtres avec persistance const setKanbanFilters = async (newFilters: KanbanFilters) => { // Séparer les vrais filtres des préférences de vue const kanbanFilterUpdates = { search: newFilters.search || '', tags: newFilters.tags, priorities: newFilters.priorities, showCompleted: newFilters.showCompleted, sortBy: newFilters.sortBy, showWithDueDate: newFilters.showWithDueDate, showCompletedLast7Days: newFilters.showCompletedLast7Days, // Filtres Jira showJiraOnly: newFilters.showJiraOnly, hideJiraTasks: newFilters.hideJiraTasks, jiraProjects: newFilters.jiraProjects, jiraTypes: newFilters.jiraTypes, // Filtres TFS showTfsOnly: newFilters.showTfsOnly, hideTfsTasks: newFilters.hideTfsTasks, tfsProjects: newFilters.tfsProjects }; const viewPreferenceUpdates = { compactView: newFilters.compactView as boolean, swimlanesByTags: newFilters.swimlanesByTags as boolean, swimlanesMode: newFilters.swimlanesMode as 'tags' | 'priority' }; // Mettre à jour via UserPreferencesContext await Promise.all([ updateKanbanFilters(kanbanFilterUpdates), updateViewPreferences(viewPreferenceUpdates) ]); }; // Séparer les tâches épinglées (objectifs) des autres et les trier const { pinnedTasks, regularTasks } = useMemo(() => { const pinnedTagNames = tags.filter(tag => tag.isPinned).map(tag => tag.name); const pinned: Task[] = []; const regular: Task[] = []; tasksState.tasks.forEach(task => { const hasPinnedTag = task.tags?.some(tagName => pinnedTagNames.includes(tagName)); if (hasPinnedTag) { pinned.push(task); } else { regular.push(task); } }); // Trier les tâches épinglées avec le même tri que les autres const sortedPinned = kanbanFilters.sortBy ? (() => { const sortOption = getSortOption(kanbanFilters.sortBy); return sortOption ? sortTasks(pinned, [{ field: sortOption.field, direction: sortOption.direction }]) : sortTasks(pinned, DEFAULT_SORT); })() : sortTasks(pinned, DEFAULT_SORT); return { pinnedTasks: sortedPinned, regularTasks: regular }; }, [tasksState.tasks, tags, kanbanFilters.sortBy]); // Calcul du nombre de filtres actifs const activeFiltersCount = useMemo(() => { return (kanbanFilters.tags?.filter(Boolean).length || 0) + (kanbanFilters.priorities?.filter(Boolean).length || 0) + (kanbanFilters.search ? 1 : 0) + (kanbanFilters.showWithDueDate ? 1 : 0) + (kanbanFilters.showCompletedLast7Days ? 1 : 0) + (kanbanFilters.jiraProjects?.filter(Boolean).length || 0) + (kanbanFilters.jiraTypes?.filter(Boolean).length || 0) + (kanbanFilters.showJiraOnly ? 1 : 0) + (kanbanFilters.hideJiraTasks ? 1 : 0) + (kanbanFilters.tfsProjects?.filter(Boolean).length || 0) + (kanbanFilters.showTfsOnly ? 1 : 0) + (kanbanFilters.hideTfsTasks ? 1 : 0); }, [kanbanFilters]); // Filtrage et tri des tâches régulières (pas les épinglées) const filteredTasks = useMemo(() => { let filtered = regularTasks; // Filtre par recherche if (kanbanFilters.search) { const searchLower = kanbanFilters.search.toLowerCase(); filtered = filtered.filter(task => task.title.toLowerCase().includes(searchLower) || task.description?.toLowerCase().includes(searchLower) || task.tags?.some(tag => tag.toLowerCase().includes(searchLower)) ); } // Filtre par tags if (kanbanFilters.tags?.length) { filtered = filtered.filter(task => kanbanFilters.tags!.some(filterTag => task.tags?.includes(filterTag) ) ); } // Filtre par priorités if (kanbanFilters.priorities?.length) { filtered = filtered.filter(task => kanbanFilters.priorities!.includes(task.priority) ); } // Filtres spécifiques Jira if (kanbanFilters.showJiraOnly) { filtered = filtered.filter(task => task.source === 'jira'); } else if (kanbanFilters.hideJiraTasks) { filtered = filtered.filter(task => task.source !== 'jira'); } // Filtre par projets Jira if (kanbanFilters.jiraProjects?.length) { filtered = filtered.filter(task => task.source !== 'jira' || kanbanFilters.jiraProjects!.includes(task.jiraProject || '') ); } // Filtre par types Jira if (kanbanFilters.jiraTypes?.length) { filtered = filtered.filter(task => task.source !== 'jira' || kanbanFilters.jiraTypes!.includes(task.jiraType || '') ); } // Filtres spécifiques TFS if (kanbanFilters.showTfsOnly) { filtered = filtered.filter(task => task.source === 'tfs'); } else if (kanbanFilters.hideTfsTasks) { filtered = filtered.filter(task => task.source !== 'tfs'); } // Filtre par projets TFS if (kanbanFilters.tfsProjects?.length) { filtered = filtered.filter(task => task.source !== 'tfs' || kanbanFilters.tfsProjects!.includes(task.tfsProject || '') ); } // Filtre par date de fin if (kanbanFilters.showWithDueDate) { filtered = filtered.filter(task => task.dueDate != null); } // Filtre par tâches complétées les 7 derniers jours if (kanbanFilters.showCompletedLast7Days) { const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); filtered = filtered.filter(task => task.status === 'done' && task.completedAt && task.completedAt >= sevenDaysAgo ); } // Tri des tâches if (kanbanFilters.sortBy) { const sortOption = getSortOption(kanbanFilters.sortBy); if (sortOption) { filtered = sortTasks(filtered, [{ field: sortOption.field, direction: sortOption.direction }]); } } else { // Tri par défaut (priorité desc + tags asc) filtered = sortTasks(filtered, DEFAULT_SORT); } return filtered; }, [regularTasks, kanbanFilters]); const contextValue: TasksContextType = { ...tasksState, regularTasks, tags, tagsLoading, tagsError, kanbanFilters, setKanbanFilters, filteredTasks, pinnedTasks, activeFiltersCount }; return ( {children} ); } export function useTasksContext() { const context = useContext(TasksContext); if (!context) { throw new Error('useTasksContext must be used within a TasksProvider'); } return context; }