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:
18
TODO.md
18
TODO.md
@@ -187,15 +187,15 @@
|
|||||||
- [ ] **Nettoyage** : Modifier composants Daily pour `useTransition`
|
- [ ] **Nettoyage** : Modifier composants Daily pour `useTransition`
|
||||||
|
|
||||||
#### Actions User Preferences (Priorité 3)
|
#### Actions User Preferences (Priorité 3)
|
||||||
- [ ] Créer `actions/preferences.ts` pour les toggles
|
- [x] Créer `actions/preferences.ts` pour les toggles
|
||||||
- [ ] `updateViewPreferences(preferences)` - Préférences d'affichage
|
- [x] `updateViewPreferences(preferences)` - Préférences d'affichage
|
||||||
- [ ] `updateKanbanFilters(filters)` - Filtres Kanban
|
- [x] `updateKanbanFilters(filters)` - Filtres Kanban
|
||||||
- [ ] `updateColumnVisibility(columns)` - Visibilité colonnes
|
- [x] `updateColumnVisibility(columns)` - Visibilité colonnes
|
||||||
- [ ] `updateTheme(theme)` - Changement de thème
|
- [x] `updateTheme(theme)` - Changement de thème
|
||||||
- [ ] Remplacer les hooks par server actions directes
|
- [x] Remplacer les hooks par server actions directes
|
||||||
- [ ] **Nettoyage** : Supprimer routes `/api/user-preferences/*` (PUT/PATCH)
|
- [x] **Nettoyage** : Supprimer routes `/api/user-preferences/*` (PUT/PATCH)
|
||||||
- [ ] **Nettoyage** : Simplifier `user-preferences-client.ts` (GET uniquement)
|
- [x] **Nettoyage** : Simplifier `user-preferences-client.ts` (GET uniquement)
|
||||||
- [ ] **Nettoyage** : Modifier `useUserPreferences.ts` pour server actions
|
- [x] **Nettoyage** : Modifier `UserPreferencesContext.tsx` pour server actions
|
||||||
|
|
||||||
#### Actions Tags (Priorité 4)
|
#### Actions Tags (Priorité 4)
|
||||||
- [ ] Créer `actions/tags.ts` pour la gestion tags
|
- [ ] Créer `actions/tags.ts` pour la gestion tags
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { httpClient } from './base/http-client';
|
import { httpClient } from './base/http-client';
|
||||||
import { UserPreferences, KanbanFilters, ViewPreferences, ColumnVisibility } from '@/lib/types';
|
import { UserPreferences } from '@/lib/types';
|
||||||
|
|
||||||
export interface UserPreferencesResponse {
|
export interface UserPreferencesResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -8,14 +8,9 @@ export interface UserPreferencesResponse {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserPreferencesUpdateResponse {
|
|
||||||
success: boolean;
|
|
||||||
message?: string;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client HTTP pour les préférences utilisateur
|
* Client HTTP pour les préférences utilisateur (lecture seule)
|
||||||
|
* Les mutations sont gérées par les server actions dans actions/preferences.ts
|
||||||
*/
|
*/
|
||||||
export const userPreferencesClient = {
|
export const userPreferencesClient = {
|
||||||
/**
|
/**
|
||||||
@@ -29,49 +24,5 @@ export const userPreferencesClient = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sauvegarde toutes les préférences utilisateur
|
|
||||||
*/
|
|
||||||
async savePreferences(preferences: UserPreferences): Promise<void> {
|
|
||||||
const response = await httpClient.put<UserPreferencesUpdateResponse>('/user-preferences', preferences);
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
throw new Error(response.error || 'Erreur lors de la sauvegarde des préférences');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Met à jour les filtres Kanban
|
|
||||||
*/
|
|
||||||
async updateKanbanFilters(filters: Partial<KanbanFilters>): Promise<void> {
|
|
||||||
const response = await httpClient.patch<UserPreferencesUpdateResponse>('/user-preferences/kanban-filters', filters);
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
throw new Error(response.error || 'Erreur lors de la mise à jour des filtres Kanban');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Met à jour les préférences de vue
|
|
||||||
*/
|
|
||||||
async updateViewPreferences(preferences: Partial<ViewPreferences>): Promise<void> {
|
|
||||||
const response = await httpClient.patch<UserPreferencesUpdateResponse>('/user-preferences/view-preferences', preferences);
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
throw new Error(response.error || 'Erreur lors de la mise à jour des préférences de vue');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Met à jour la visibilité des colonnes
|
|
||||||
*/
|
|
||||||
async updateColumnVisibility(visibility: Partial<ColumnVisibility>): Promise<void> {
|
|
||||||
const response = await httpClient.patch<UserPreferencesUpdateResponse>('/user-preferences/column-visibility', visibility);
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
throw new Error(response.error || 'Erreur lors de la mise à jour de la visibilité des colonnes');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
218
src/actions/preferences.ts
Normal file
218
src/actions/preferences.ts
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { userPreferencesService } from '@/services/user-preferences';
|
||||||
|
import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
|
||||||
|
import { revalidatePath } from 'next/cache';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour les préférences de vue
|
||||||
|
*/
|
||||||
|
export async function updateViewPreferences(updates: Partial<ViewPreferences>): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
await userPreferencesService.updateViewPreferences(updates);
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur updateViewPreferences:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour les filtres Kanban
|
||||||
|
*/
|
||||||
|
export async function updateKanbanFilters(updates: Partial<KanbanFilters>): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
await userPreferencesService.updateKanbanFilters(updates);
|
||||||
|
revalidatePath('/kanban');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur updateKanbanFilters:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour la visibilité des colonnes
|
||||||
|
*/
|
||||||
|
export async function updateColumnVisibility(updates: Partial<ColumnVisibility>): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
const newColumnVisibility: ColumnVisibility = {
|
||||||
|
...preferences.columnVisibility,
|
||||||
|
...updates
|
||||||
|
};
|
||||||
|
|
||||||
|
await userPreferencesService.saveColumnVisibility(newColumnVisibility);
|
||||||
|
revalidatePath('/kanban');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur updateColumnVisibility:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle la visibilité des objectifs
|
||||||
|
*/
|
||||||
|
export async function toggleObjectivesVisibility(): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
const showObjectives = !preferences.viewPreferences.showObjectives;
|
||||||
|
|
||||||
|
await userPreferencesService.updateViewPreferences({ showObjectives });
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur toggleObjectivesVisibility:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle le mode collapse des objectifs
|
||||||
|
*/
|
||||||
|
export async function toggleObjectivesCollapse(): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
const collapseObjectives = !preferences.viewPreferences.collapseObjectives;
|
||||||
|
|
||||||
|
await userPreferencesService.updateViewPreferences({ collapseObjectives });
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur toggleObjectivesCollapse:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change le thème (light/dark)
|
||||||
|
*/
|
||||||
|
export async function setTheme(theme: 'light' | 'dark'): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
await userPreferencesService.updateViewPreferences({ theme });
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur setTheme:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle le thème entre light et dark
|
||||||
|
*/
|
||||||
|
export async function toggleTheme(): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
const newTheme = preferences.viewPreferences.theme === 'dark' ? 'light' : 'dark';
|
||||||
|
|
||||||
|
await userPreferencesService.updateViewPreferences({ theme: newTheme });
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur toggleTheme:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle la taille de police
|
||||||
|
*/
|
||||||
|
export async function toggleFontSize(): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
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 userPreferencesService.updateViewPreferences({ fontSize: newFontSize });
|
||||||
|
revalidatePath('/');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur toggleFontSize:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle la visibilité d'une colonne Kanban
|
||||||
|
*/
|
||||||
|
export async function toggleColumnVisibility(status: TaskStatus): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences();
|
||||||
|
const hiddenStatuses = new Set(preferences.columnVisibility.hiddenStatuses);
|
||||||
|
|
||||||
|
if (hiddenStatuses.has(status)) {
|
||||||
|
hiddenStatuses.delete(status);
|
||||||
|
} else {
|
||||||
|
hiddenStatuses.add(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
await userPreferencesService.saveColumnVisibility({
|
||||||
|
hiddenStatuses: Array.from(hiddenStatuses)
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidatePath('/kanban');
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur toggleColumnVisibility:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { userPreferencesService } from '@/services/user-preferences';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PATCH /api/user-preferences/column-visibility - Met à jour partiellement la visibilité des colonnes
|
|
||||||
*/
|
|
||||||
export async function PATCH(request: NextRequest) {
|
|
||||||
try {
|
|
||||||
const updates = await request.json();
|
|
||||||
|
|
||||||
const current = await userPreferencesService.getColumnVisibility();
|
|
||||||
await userPreferencesService.saveColumnVisibility({ ...current, ...updates });
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: 'Visibilité des colonnes mise à jour avec succès'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la mise à jour de la visibilité des colonnes:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
error: 'Erreur lors de la mise à jour de la visibilité des colonnes'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { userPreferencesService } from '@/services/user-preferences';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /api/user-preferences/kanban-filters - Récupère les filtres Kanban
|
|
||||||
*/
|
|
||||||
export async function GET() {
|
|
||||||
try {
|
|
||||||
const filters = await userPreferencesService.getKanbanFilters();
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: filters
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la récupération des filtres Kanban:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
error: 'Erreur lors de la récupération des filtres Kanban'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PUT /api/user-preferences/kanban-filters - Met à jour les filtres Kanban
|
|
||||||
*/
|
|
||||||
export async function PUT(request: NextRequest) {
|
|
||||||
try {
|
|
||||||
const filters = await request.json();
|
|
||||||
|
|
||||||
await userPreferencesService.saveKanbanFilters(filters);
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: 'Filtres Kanban sauvegardés avec succès'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la sauvegarde des filtres Kanban:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
error: 'Erreur lors de la sauvegarde des filtres Kanban'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PATCH /api/user-preferences/kanban-filters - Met à jour partiellement les filtres Kanban
|
|
||||||
*/
|
|
||||||
export async function PATCH(request: NextRequest) {
|
|
||||||
try {
|
|
||||||
const updates = await request.json();
|
|
||||||
|
|
||||||
const current = await userPreferencesService.getKanbanFilters();
|
|
||||||
await userPreferencesService.saveKanbanFilters({ ...current, ...updates });
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: 'Filtres Kanban mis à jour avec succès'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la mise à jour des filtres Kanban:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
error: 'Erreur lors de la mise à jour des filtres Kanban'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { userPreferencesService } from '@/services/user-preferences';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PATCH /api/user-preferences/view-preferences - Met à jour partiellement les préférences de vue
|
|
||||||
*/
|
|
||||||
export async function PATCH(request: NextRequest) {
|
|
||||||
try {
|
|
||||||
const updates = await request.json();
|
|
||||||
|
|
||||||
const current = await userPreferencesService.getViewPreferences();
|
|
||||||
await userPreferencesService.saveViewPreferences({ ...current, ...updates });
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: 'Préférences de vue mises à jour avec succès'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la mise à jour des préférences de vue:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
error: 'Erreur lors de la mise à jour des préférences de vue'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,37 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContext, ReactNode, useState, useCallback, useEffect } from 'react';
|
import { createContext, useContext, ReactNode, useState, useCallback, useEffect, useTransition } from 'react';
|
||||||
import { userPreferencesClient } from '@/clients/user-preferences-client';
|
|
||||||
import { UserPreferences, KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
|
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 {
|
interface UserPreferencesContextType {
|
||||||
preferences: UserPreferences;
|
preferences: UserPreferences;
|
||||||
|
isPending: boolean;
|
||||||
|
|
||||||
// Kanban Filters
|
// Kanban Filters
|
||||||
updateKanbanFilters: (updates: Partial<KanbanFilters>) => Promise<void>;
|
updateKanbanFilters: (updates: Partial<KanbanFilters>) => void;
|
||||||
|
|
||||||
// View Preferences
|
// View Preferences
|
||||||
updateViewPreferences: (updates: Partial<ViewPreferences>) => Promise<void>;
|
updateViewPreferences: (updates: Partial<ViewPreferences>) => void;
|
||||||
toggleObjectivesVisibility: () => Promise<void>;
|
toggleObjectivesVisibility: () => void;
|
||||||
toggleObjectivesCollapse: () => Promise<void>;
|
toggleObjectivesCollapse: () => void;
|
||||||
toggleTheme: () => Promise<void>;
|
toggleTheme: () => void;
|
||||||
setTheme: (theme: 'light' | 'dark') => Promise<void>;
|
setTheme: (theme: 'light' | 'dark') => void;
|
||||||
toggleFontSize: () => Promise<void>;
|
toggleFontSize: () => void;
|
||||||
|
|
||||||
// Column Visibility
|
// Column Visibility
|
||||||
updateColumnVisibility: (updates: Partial<ColumnVisibility>) => Promise<void>;
|
updateColumnVisibility: (updates: Partial<ColumnVisibility>) => void;
|
||||||
toggleColumnVisibility: (status: TaskStatus) => Promise<void>;
|
toggleColumnVisibility: (status: TaskStatus) => void;
|
||||||
isColumnVisible: (status: TaskStatus) => boolean;
|
isColumnVisible: (status: TaskStatus) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +44,7 @@ interface UserPreferencesProviderProps {
|
|||||||
|
|
||||||
export function UserPreferencesProvider({ children, initialPreferences }: UserPreferencesProviderProps) {
|
export function UserPreferencesProvider({ children, initialPreferences }: UserPreferencesProviderProps) {
|
||||||
const [preferences, setPreferences] = useState<UserPreferences>(initialPreferences);
|
const [preferences, setPreferences] = useState<UserPreferences>(initialPreferences);
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
// Synchroniser le thème avec le ThemeProvider global (si disponible)
|
// Synchroniser le thème avec le ThemeProvider global (si disponible)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -44,87 +56,114 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
|
|||||||
|
|
||||||
// === KANBAN FILTERS ===
|
// === KANBAN FILTERS ===
|
||||||
|
|
||||||
const updateKanbanFilters = useCallback(async (updates: Partial<KanbanFilters>) => {
|
const updateKanbanFilters = useCallback((updates: Partial<KanbanFilters>) => {
|
||||||
const newFilters = { ...preferences.kanbanFilters, ...updates };
|
startTransition(async () => {
|
||||||
setPreferences(prev => ({ ...prev, kanbanFilters: newFilters }));
|
const originalFilters = preferences.kanbanFilters;
|
||||||
|
|
||||||
try {
|
// Optimistic update
|
||||||
await userPreferencesClient.updateKanbanFilters(updates);
|
setPreferences(prev => ({
|
||||||
} catch (error) {
|
...prev,
|
||||||
console.warn('Erreur lors de la sauvegarde des filtres Kanban:', error);
|
kanbanFilters: { ...prev.kanbanFilters, ...updates }
|
||||||
// Revert optimistic update on error
|
}));
|
||||||
setPreferences(prev => ({ ...prev, kanbanFilters: preferences.kanbanFilters }));
|
|
||||||
}
|
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]);
|
}, [preferences.kanbanFilters]);
|
||||||
|
|
||||||
// === VIEW PREFERENCES ===
|
// === VIEW PREFERENCES ===
|
||||||
|
|
||||||
const updateViewPreferences = useCallback(async (updates: Partial<ViewPreferences>) => {
|
const updateViewPreferences = useCallback((updates: Partial<ViewPreferences>) => {
|
||||||
const newPreferences = { ...preferences.viewPreferences, ...updates };
|
startTransition(async () => {
|
||||||
setPreferences(prev => ({ ...prev, viewPreferences: newPreferences }));
|
const originalPreferences = preferences.viewPreferences;
|
||||||
|
|
||||||
try {
|
// Optimistic update
|
||||||
await userPreferencesClient.updateViewPreferences(updates);
|
setPreferences(prev => ({
|
||||||
} catch (error) {
|
...prev,
|
||||||
console.warn('Erreur lors de la sauvegarde des préférences de vue:', error);
|
viewPreferences: { ...prev.viewPreferences, ...updates }
|
||||||
// Revert optimistic update on error
|
}));
|
||||||
setPreferences(prev => ({ ...prev, viewPreferences: preferences.viewPreferences }));
|
|
||||||
}
|
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]);
|
}, [preferences.viewPreferences]);
|
||||||
|
|
||||||
const toggleObjectivesVisibility = useCallback(async () => {
|
const toggleObjectivesVisibility = useCallback(() => {
|
||||||
const newValue = !preferences.viewPreferences.showObjectives;
|
startTransition(async () => {
|
||||||
await updateViewPreferences({ showObjectives: newValue });
|
await toggleObjectivesVisibilityAction();
|
||||||
}, [preferences.viewPreferences.showObjectives, updateViewPreferences]);
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const toggleObjectivesCollapse = useCallback(async () => {
|
const toggleObjectivesCollapse = useCallback(() => {
|
||||||
const newValue = !preferences.viewPreferences.objectivesCollapsed;
|
startTransition(async () => {
|
||||||
await updateViewPreferences({ objectivesCollapsed: newValue });
|
await toggleObjectivesCollapseAction();
|
||||||
}, [preferences.viewPreferences.objectivesCollapsed, updateViewPreferences]);
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const toggleTheme = useCallback(async () => {
|
const toggleTheme = useCallback(() => {
|
||||||
const newTheme = preferences.viewPreferences.theme === 'dark' ? 'light' : 'dark';
|
startTransition(async () => {
|
||||||
await updateViewPreferences({ theme: newTheme });
|
await toggleThemeAction();
|
||||||
}, [preferences.viewPreferences.theme, updateViewPreferences]);
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const setTheme = useCallback(async (theme: 'light' | 'dark') => {
|
const setTheme = useCallback((theme: 'light' | 'dark') => {
|
||||||
await updateViewPreferences({ theme });
|
startTransition(async () => {
|
||||||
}, [updateViewPreferences]);
|
await setThemeAction(theme);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const toggleFontSize = useCallback(async () => {
|
const toggleFontSize = useCallback(() => {
|
||||||
const fontSizes: ('small' | 'medium' | 'large')[] = ['small', 'medium', 'large'];
|
startTransition(async () => {
|
||||||
const currentIndex = fontSizes.indexOf(preferences.viewPreferences.fontSize);
|
await toggleFontSizeAction();
|
||||||
const nextIndex = (currentIndex + 1) % fontSizes.length;
|
});
|
||||||
const newFontSize = fontSizes[nextIndex];
|
}, []);
|
||||||
await updateViewPreferences({ fontSize: newFontSize });
|
|
||||||
}, [preferences.viewPreferences.fontSize, updateViewPreferences]);
|
|
||||||
|
|
||||||
// === COLUMN VISIBILITY ===
|
// === COLUMN VISIBILITY ===
|
||||||
|
|
||||||
const updateColumnVisibility = useCallback(async (updates: Partial<ColumnVisibility>) => {
|
const updateColumnVisibility = useCallback((updates: Partial<ColumnVisibility>) => {
|
||||||
const newVisibility = { ...preferences.columnVisibility, ...updates };
|
startTransition(async () => {
|
||||||
setPreferences(prev => ({ ...prev, columnVisibility: newVisibility }));
|
const originalVisibility = preferences.columnVisibility;
|
||||||
|
|
||||||
try {
|
// Optimistic update
|
||||||
await userPreferencesClient.updateColumnVisibility(updates);
|
setPreferences(prev => ({
|
||||||
} catch (error) {
|
...prev,
|
||||||
console.warn('Erreur lors de la sauvegarde de la visibilité des colonnes:', error);
|
columnVisibility: { ...prev.columnVisibility, ...updates }
|
||||||
// Revert optimistic update on error
|
}));
|
||||||
setPreferences(prev => ({ ...prev, columnVisibility: preferences.columnVisibility }));
|
|
||||||
}
|
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]);
|
}, [preferences.columnVisibility]);
|
||||||
|
|
||||||
const toggleColumnVisibility = useCallback(async (status: TaskStatus) => {
|
const toggleColumnVisibility = useCallback((status: TaskStatus) => {
|
||||||
const hiddenStatuses = new Set(preferences.columnVisibility.hiddenStatuses);
|
startTransition(async () => {
|
||||||
|
await toggleColumnVisibilityAction(status);
|
||||||
if (hiddenStatuses.has(status)) {
|
});
|
||||||
hiddenStatuses.delete(status);
|
}, []);
|
||||||
} else {
|
|
||||||
hiddenStatuses.add(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateColumnVisibility({ hiddenStatuses: Array.from(hiddenStatuses) });
|
|
||||||
}, [preferences.columnVisibility.hiddenStatuses, updateColumnVisibility]);
|
|
||||||
|
|
||||||
const isColumnVisible = useCallback((status: TaskStatus) => {
|
const isColumnVisible = useCallback((status: TaskStatus) => {
|
||||||
return !preferences.columnVisibility.hiddenStatuses.includes(status);
|
return !preferences.columnVisibility.hiddenStatuses.includes(status);
|
||||||
@@ -132,6 +171,7 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
|
|||||||
|
|
||||||
const contextValue: UserPreferencesContextType = {
|
const contextValue: UserPreferencesContextType = {
|
||||||
preferences,
|
preferences,
|
||||||
|
isPending,
|
||||||
updateKanbanFilters,
|
updateKanbanFilters,
|
||||||
updateViewPreferences,
|
updateViewPreferences,
|
||||||
toggleObjectivesVisibility,
|
toggleObjectivesVisibility,
|
||||||
|
|||||||
Reference in New Issue
Block a user