refactor: userpreferences are now in the DB

This commit is contained in:
Julien Froidefond
2025-09-17 08:30:36 +02:00
parent 4f137455f4
commit 14d300c682
24 changed files with 763 additions and 404 deletions

View File

@@ -1,31 +1,5 @@
import { TaskPriority, TaskStatus } from '@/lib/types';
// Types pour les préférences utilisateur
export interface KanbanFilters {
search?: string;
tags?: string[];
priorities?: TaskPriority[];
showCompleted?: boolean;
sortBy?: string;
}
export interface ViewPreferences {
compactView: boolean;
swimlanesByTags: boolean;
swimlanesMode?: 'tags' | 'priority';
showObjectives: boolean;
showFilters: boolean;
}
export interface ColumnVisibility {
hiddenStatuses: TaskStatus[];
}
export interface UserPreferences {
kanbanFilters: KanbanFilters;
viewPreferences: ViewPreferences;
columnVisibility: ColumnVisibility;
}
import { TaskStatus, KanbanFilters, ViewPreferences, ColumnVisibility, UserPreferences } from '@/lib/types';
import { prisma } from './database';
// Valeurs par défaut
const DEFAULT_PREFERENCES: UserPreferences = {
@@ -33,186 +7,217 @@ const DEFAULT_PREFERENCES: UserPreferences = {
search: '',
tags: [],
priorities: [],
showCompleted: true
showCompleted: true,
sortBy: ''
},
viewPreferences: {
compactView: false,
swimlanesByTags: false,
swimlanesMode: 'tags',
showObjectives: true,
showFilters: true
showFilters: true,
objectivesCollapsed: false,
theme: 'dark'
},
columnVisibility: {
hiddenStatuses: []
}
};
// Clés pour le localStorage
const STORAGE_KEYS = {
KANBAN_FILTERS: 'towercontrol_kanban_filters',
VIEW_PREFERENCES: 'towercontrol_view_preferences',
COLUMN_VISIBILITY: 'towercontrol_column_visibility'
} as const;
/**
* Service pour gérer les préférences utilisateur dans le localStorage
* Service pour gérer les préférences utilisateur en base de données
*/
export const userPreferencesService = {
class UserPreferencesService {
private readonly USER_ID = 'default'; // Pour l'instant, un seul utilisateur
/**
* Récupère ou crée l'entrée user preferences (avec upsert pour éviter les doublons)
*/
private async getOrCreateUserPreferences() {
// Utiliser upsert pour éviter les conditions de course
const userPrefs = await prisma.userPreferences.upsert({
where: { id: 'default' }, // ID fixe pour l'utilisateur unique
update: {}, // Ne rien mettre à jour si existe
create: {
id: 'default',
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
columnVisibility: DEFAULT_PREFERENCES.columnVisibility,
}
});
return userPrefs;
}
// === FILTRES KANBAN ===
/**
* Sauvegarde les filtres Kanban
*/
saveKanbanFilters(filters: KanbanFilters): void {
async saveKanbanFilters(filters: KanbanFilters): Promise<void> {
try {
localStorage.setItem(STORAGE_KEYS.KANBAN_FILTERS, JSON.stringify(filters));
const userPrefs = await this.getOrCreateUserPreferences();
await prisma.userPreferences.update({
where: { id: userPrefs.id },
data: { kanbanFilters: filters }
});
} catch (error) {
console.warn('Erreur lors de la sauvegarde des filtres Kanban:', error);
throw error;
}
},
}
/**
* Récupère les filtres Kanban
*/
getKanbanFilters(): KanbanFilters {
async getKanbanFilters(): Promise<KanbanFilters> {
try {
const stored = localStorage.getItem(STORAGE_KEYS.KANBAN_FILTERS);
if (stored) {
return { ...DEFAULT_PREFERENCES.kanbanFilters, ...JSON.parse(stored) };
}
const userPrefs = await this.getOrCreateUserPreferences();
const filters = userPrefs.kanbanFilters as KanbanFilters | null;
return { ...DEFAULT_PREFERENCES.kanbanFilters, ...(filters || {}) };
} catch (error) {
console.warn('Erreur lors de la récupération des filtres Kanban:', error);
return DEFAULT_PREFERENCES.kanbanFilters;
}
return DEFAULT_PREFERENCES.kanbanFilters;
},
}
// === PRÉFÉRENCES DE VUE ===
/**
* Sauvegarde les préférences de vue
*/
saveViewPreferences(preferences: ViewPreferences): void {
async saveViewPreferences(preferences: ViewPreferences): Promise<void> {
try {
localStorage.setItem(STORAGE_KEYS.VIEW_PREFERENCES, JSON.stringify(preferences));
const userPrefs = await this.getOrCreateUserPreferences();
await prisma.userPreferences.update({
where: { id: userPrefs.id },
data: { viewPreferences: preferences }
});
} catch (error) {
console.warn('Erreur lors de la sauvegarde des préférences de vue:', error);
throw error;
}
},
}
/**
* Récupère les préférences de vue
*/
getViewPreferences(): ViewPreferences {
async getViewPreferences(): Promise<ViewPreferences> {
try {
const stored = localStorage.getItem(STORAGE_KEYS.VIEW_PREFERENCES);
if (stored) {
return { ...DEFAULT_PREFERENCES.viewPreferences, ...JSON.parse(stored) };
}
const userPrefs = await this.getOrCreateUserPreferences();
const preferences = userPrefs.viewPreferences as ViewPreferences | null;
return { ...DEFAULT_PREFERENCES.viewPreferences, ...(preferences || {}) };
} catch (error) {
console.warn('Erreur lors de la récupération des préférences de vue:', error);
return DEFAULT_PREFERENCES.viewPreferences;
}
return DEFAULT_PREFERENCES.viewPreferences;
},
}
// === VISIBILITÉ DES COLONNES ===
/**
* Sauvegarde la visibilité des colonnes
*/
saveColumnVisibility(visibility: ColumnVisibility): void {
async saveColumnVisibility(visibility: ColumnVisibility): Promise<void> {
try {
localStorage.setItem(STORAGE_KEYS.COLUMN_VISIBILITY, JSON.stringify(visibility));
const userPrefs = await this.getOrCreateUserPreferences();
await prisma.userPreferences.update({
where: { id: userPrefs.id },
data: { columnVisibility: visibility }
});
} catch (error) {
console.warn('Erreur lors de la sauvegarde de la visibilité des colonnes:', error);
throw error;
}
},
}
/**
* Récupère la visibilité des colonnes
*/
getColumnVisibility(): ColumnVisibility {
async getColumnVisibility(): Promise<ColumnVisibility> {
try {
const stored = localStorage.getItem(STORAGE_KEYS.COLUMN_VISIBILITY);
if (stored) {
return { ...DEFAULT_PREFERENCES.columnVisibility, ...JSON.parse(stored) };
}
const userPrefs = await this.getOrCreateUserPreferences();
const visibility = userPrefs.columnVisibility as ColumnVisibility | null;
return { ...DEFAULT_PREFERENCES.columnVisibility, ...(visibility || {}) };
} catch (error) {
console.warn('Erreur lors de la récupération de la visibilité des colonnes:', error);
return DEFAULT_PREFERENCES.columnVisibility;
}
return DEFAULT_PREFERENCES.columnVisibility;
},
}
// === MÉTHODES GLOBALES ===
/**
* Récupère toutes les préférences utilisateur
*/
getAllPreferences(): UserPreferences {
async getAllPreferences(): Promise<UserPreferences> {
const [kanbanFilters, viewPreferences, columnVisibility] = await Promise.all([
this.getKanbanFilters(),
this.getViewPreferences(),
this.getColumnVisibility()
]);
return {
kanbanFilters: this.getKanbanFilters(),
viewPreferences: this.getViewPreferences(),
columnVisibility: this.getColumnVisibility()
kanbanFilters,
viewPreferences,
columnVisibility
};
},
}
/**
* Sauvegarde toutes les préférences utilisateur
*/
saveAllPreferences(preferences: UserPreferences): void {
this.saveKanbanFilters(preferences.kanbanFilters);
this.saveViewPreferences(preferences.viewPreferences);
this.saveColumnVisibility(preferences.columnVisibility);
},
async saveAllPreferences(preferences: UserPreferences): Promise<void> {
await Promise.all([
this.saveKanbanFilters(preferences.kanbanFilters),
this.saveViewPreferences(preferences.viewPreferences),
this.saveColumnVisibility(preferences.columnVisibility)
]);
}
/**
* Remet à zéro toutes les préférences
*/
resetAllPreferences(): void {
async resetAllPreferences(): Promise<void> {
try {
Object.values(STORAGE_KEYS).forEach(key => {
localStorage.removeItem(key);
const userPrefs = await this.getOrCreateUserPreferences();
await prisma.userPreferences.update({
where: { id: userPrefs.id },
data: {
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
columnVisibility: DEFAULT_PREFERENCES.columnVisibility,
}
});
} catch (error) {
console.warn('Erreur lors de la remise à zéro des préférences:', error);
throw error;
}
},
/**
* Vérifie si le localStorage est disponible
*/
isStorageAvailable(): boolean {
try {
const test = '__storage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch {
return false;
}
},
}
// === MÉTHODES UTILITAIRES ===
/**
* Met à jour partiellement les filtres Kanban
*/
updateKanbanFilters(updates: Partial<KanbanFilters>): void {
const current = this.getKanbanFilters();
this.saveKanbanFilters({ ...current, ...updates });
},
async updateKanbanFilters(updates: Partial<KanbanFilters>): Promise<void> {
const current = await this.getKanbanFilters();
await this.saveKanbanFilters({ ...current, ...updates });
}
/**
* Met à jour partiellement les préférences de vue
*/
updateViewPreferences(updates: Partial<ViewPreferences>): void {
const current = this.getViewPreferences();
this.saveViewPreferences({ ...current, ...updates });
},
async updateViewPreferences(updates: Partial<ViewPreferences>): Promise<void> {
const current = await this.getViewPreferences();
await this.saveViewPreferences({ ...current, ...updates });
}
/**
* Met à jour la visibilité d'une colonne spécifique
*/
toggleColumnVisibility(status: TaskStatus): void {
const current = this.getColumnVisibility();
async toggleColumnVisibility(status: TaskStatus): Promise<void> {
const current = await this.getColumnVisibility();
const hiddenStatuses = new Set(current.hiddenStatuses);
if (hiddenStatuses.has(status)) {
@@ -221,8 +226,11 @@ export const userPreferencesService = {
hiddenStatuses.add(status);
}
this.saveColumnVisibility({
await this.saveColumnVisibility({
hiddenStatuses: Array.from(hiddenStatuses)
});
}
};
}
// Export de l'instance singleton
export const userPreferencesService = new UserPreferencesService();