feat: refactor user preferences management

- Marked all user preferences actions as complete in TODO.md.
- Updated `user-preferences-client.ts` to remove outdated mutation methods, now handled by server actions.
- Deleted unused API routes for column visibility, kanban filters, and view preferences.
- Refactored `UserPreferencesContext.tsx` to utilize server actions for updates, improving performance with `useTransition`.
This commit is contained in:
Julien Froidefond
2025-09-18 13:10:04 +02:00
parent cece09d150
commit aeb4e17939
7 changed files with 347 additions and 270 deletions

View File

@@ -1,26 +1,37 @@
'use client';
import { createContext, useContext, ReactNode, useState, useCallback, useEffect } from 'react';
import { userPreferencesClient } from '@/clients/user-preferences-client';
import { createContext, useContext, ReactNode, useState, useCallback, useEffect, useTransition } from 'react';
import { UserPreferences, KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
import {
updateViewPreferences as updateViewPreferencesAction,
updateKanbanFilters as updateKanbanFiltersAction,
updateColumnVisibility as updateColumnVisibilityAction,
toggleObjectivesVisibility as toggleObjectivesVisibilityAction,
toggleObjectivesCollapse as toggleObjectivesCollapseAction,
toggleTheme as toggleThemeAction,
setTheme as setThemeAction,
toggleFontSize as toggleFontSizeAction,
toggleColumnVisibility as toggleColumnVisibilityAction
} from '@/actions/preferences';
interface UserPreferencesContextType {
preferences: UserPreferences;
isPending: boolean;
// Kanban Filters
updateKanbanFilters: (updates: Partial<KanbanFilters>) => Promise<void>;
updateKanbanFilters: (updates: Partial<KanbanFilters>) => void;
// View Preferences
updateViewPreferences: (updates: Partial<ViewPreferences>) => Promise<void>;
toggleObjectivesVisibility: () => Promise<void>;
toggleObjectivesCollapse: () => Promise<void>;
toggleTheme: () => Promise<void>;
setTheme: (theme: 'light' | 'dark') => Promise<void>;
toggleFontSize: () => Promise<void>;
updateViewPreferences: (updates: Partial<ViewPreferences>) => void;
toggleObjectivesVisibility: () => void;
toggleObjectivesCollapse: () => void;
toggleTheme: () => void;
setTheme: (theme: 'light' | 'dark') => void;
toggleFontSize: () => void;
// Column Visibility
updateColumnVisibility: (updates: Partial<ColumnVisibility>) => Promise<void>;
toggleColumnVisibility: (status: TaskStatus) => Promise<void>;
updateColumnVisibility: (updates: Partial<ColumnVisibility>) => void;
toggleColumnVisibility: (status: TaskStatus) => void;
isColumnVisible: (status: TaskStatus) => boolean;
}
@@ -33,6 +44,7 @@ interface UserPreferencesProviderProps {
export function UserPreferencesProvider({ children, initialPreferences }: UserPreferencesProviderProps) {
const [preferences, setPreferences] = useState<UserPreferences>(initialPreferences);
const [isPending, startTransition] = useTransition();
// Synchroniser le thème avec le ThemeProvider global (si disponible)
useEffect(() => {
@@ -44,87 +56,114 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
// === KANBAN FILTERS ===
const updateKanbanFilters = useCallback(async (updates: Partial<KanbanFilters>) => {
const newFilters = { ...preferences.kanbanFilters, ...updates };
setPreferences(prev => ({ ...prev, kanbanFilters: newFilters }));
try {
await userPreferencesClient.updateKanbanFilters(updates);
} catch (error) {
console.warn('Erreur lors de la sauvegarde des filtres Kanban:', error);
// Revert optimistic update on error
setPreferences(prev => ({ ...prev, kanbanFilters: preferences.kanbanFilters }));
}
const updateKanbanFilters = useCallback((updates: Partial<KanbanFilters>) => {
startTransition(async () => {
const originalFilters = preferences.kanbanFilters;
// Optimistic update
setPreferences(prev => ({
...prev,
kanbanFilters: { ...prev.kanbanFilters, ...updates }
}));
try {
const result = await updateKanbanFiltersAction(updates);
if (!result.success) {
console.error('Erreur server action:', result.error);
setPreferences(prev => ({ ...prev, kanbanFilters: originalFilters }));
}
} catch (error) {
console.error('Erreur lors de la mise à jour des filtres Kanban:', error);
setPreferences(prev => ({ ...prev, kanbanFilters: originalFilters }));
}
});
}, [preferences.kanbanFilters]);
// === VIEW PREFERENCES ===
const updateViewPreferences = useCallback(async (updates: Partial<ViewPreferences>) => {
const newPreferences = { ...preferences.viewPreferences, ...updates };
setPreferences(prev => ({ ...prev, viewPreferences: newPreferences }));
try {
await userPreferencesClient.updateViewPreferences(updates);
} catch (error) {
console.warn('Erreur lors de la sauvegarde des préférences de vue:', error);
// Revert optimistic update on error
setPreferences(prev => ({ ...prev, viewPreferences: preferences.viewPreferences }));
}
const updateViewPreferences = useCallback((updates: Partial<ViewPreferences>) => {
startTransition(async () => {
const originalPreferences = preferences.viewPreferences;
// Optimistic update
setPreferences(prev => ({
...prev,
viewPreferences: { ...prev.viewPreferences, ...updates }
}));
try {
const result = await updateViewPreferencesAction(updates);
if (!result.success) {
console.error('Erreur server action:', result.error);
setPreferences(prev => ({ ...prev, viewPreferences: originalPreferences }));
}
} catch (error) {
console.error('Erreur lors de la mise à jour des préférences de vue:', error);
setPreferences(prev => ({ ...prev, viewPreferences: originalPreferences }));
}
});
}, [preferences.viewPreferences]);
const toggleObjectivesVisibility = useCallback(async () => {
const newValue = !preferences.viewPreferences.showObjectives;
await updateViewPreferences({ showObjectives: newValue });
}, [preferences.viewPreferences.showObjectives, updateViewPreferences]);
const toggleObjectivesVisibility = useCallback(() => {
startTransition(async () => {
await toggleObjectivesVisibilityAction();
});
}, []);
const toggleObjectivesCollapse = useCallback(async () => {
const newValue = !preferences.viewPreferences.objectivesCollapsed;
await updateViewPreferences({ objectivesCollapsed: newValue });
}, [preferences.viewPreferences.objectivesCollapsed, updateViewPreferences]);
const toggleObjectivesCollapse = useCallback(() => {
startTransition(async () => {
await toggleObjectivesCollapseAction();
});
}, []);
const toggleTheme = useCallback(async () => {
const newTheme = preferences.viewPreferences.theme === 'dark' ? 'light' : 'dark';
await updateViewPreferences({ theme: newTheme });
}, [preferences.viewPreferences.theme, updateViewPreferences]);
const toggleTheme = useCallback(() => {
startTransition(async () => {
await toggleThemeAction();
});
}, []);
const setTheme = useCallback(async (theme: 'light' | 'dark') => {
await updateViewPreferences({ theme });
}, [updateViewPreferences]);
const setTheme = useCallback((theme: 'light' | 'dark') => {
startTransition(async () => {
await setThemeAction(theme);
});
}, []);
const toggleFontSize = useCallback(async () => {
const fontSizes: ('small' | 'medium' | 'large')[] = ['small', 'medium', 'large'];
const currentIndex = fontSizes.indexOf(preferences.viewPreferences.fontSize);
const nextIndex = (currentIndex + 1) % fontSizes.length;
const newFontSize = fontSizes[nextIndex];
await updateViewPreferences({ fontSize: newFontSize });
}, [preferences.viewPreferences.fontSize, updateViewPreferences]);
const toggleFontSize = useCallback(() => {
startTransition(async () => {
await toggleFontSizeAction();
});
}, []);
// === COLUMN VISIBILITY ===
const updateColumnVisibility = useCallback(async (updates: Partial<ColumnVisibility>) => {
const newVisibility = { ...preferences.columnVisibility, ...updates };
setPreferences(prev => ({ ...prev, columnVisibility: newVisibility }));
try {
await userPreferencesClient.updateColumnVisibility(updates);
} catch (error) {
console.warn('Erreur lors de la sauvegarde de la visibilité des colonnes:', error);
// Revert optimistic update on error
setPreferences(prev => ({ ...prev, columnVisibility: preferences.columnVisibility }));
}
const updateColumnVisibility = useCallback((updates: Partial<ColumnVisibility>) => {
startTransition(async () => {
const originalVisibility = preferences.columnVisibility;
// Optimistic update
setPreferences(prev => ({
...prev,
columnVisibility: { ...prev.columnVisibility, ...updates }
}));
try {
const result = await updateColumnVisibilityAction(updates);
if (!result.success) {
console.error('Erreur server action:', result.error);
setPreferences(prev => ({ ...prev, columnVisibility: originalVisibility }));
}
} catch (error) {
console.error('Erreur lors de la mise à jour de la visibilité des colonnes:', error);
setPreferences(prev => ({ ...prev, columnVisibility: originalVisibility }));
}
});
}, [preferences.columnVisibility]);
const toggleColumnVisibility = useCallback(async (status: TaskStatus) => {
const hiddenStatuses = new Set(preferences.columnVisibility.hiddenStatuses);
if (hiddenStatuses.has(status)) {
hiddenStatuses.delete(status);
} else {
hiddenStatuses.add(status);
}
await updateColumnVisibility({ hiddenStatuses: Array.from(hiddenStatuses) });
}, [preferences.columnVisibility.hiddenStatuses, updateColumnVisibility]);
const toggleColumnVisibility = useCallback((status: TaskStatus) => {
startTransition(async () => {
await toggleColumnVisibilityAction(status);
});
}, []);
const isColumnVisible = useCallback((status: TaskStatus) => {
return !preferences.columnVisibility.hiddenStatuses.includes(status);
@@ -132,6 +171,7 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
const contextValue: UserPreferencesContextType = {
preferences,
isPending,
updateKanbanFilters,
updateViewPreferences,
toggleObjectivesVisibility,