feat: enhance user preferences management with userId integration

- Added `userId` field to `UserPreferences` model in Prisma schema for user-specific preferences.
- Implemented migration to populate existing preferences with the first user.
- Updated user preferences service methods to handle user-specific data retrieval and updates.
- Modified API routes and components to ensure user authentication and fetch preferences based on the authenticated user.
- Enhanced session management in various components to load user preferences accordingly.
This commit is contained in:
Julien Froidefond
2025-09-30 22:15:44 +02:00
parent 17b86b6087
commit 30aaca4877
23 changed files with 381 additions and 124 deletions

View File

@@ -3,6 +3,8 @@
import { JiraAnalyticsService } from '@/services/integrations/jira/analytics';
import { userPreferencesService } from '@/services/core/user-preferences';
import { JiraAnalytics } from '@/lib/types';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export type JiraAnalyticsResult = {
success: boolean;
@@ -15,8 +17,13 @@ export type JiraAnalyticsResult = {
*/
export async function getJiraAnalytics(forceRefresh = false): Promise<JiraAnalyticsResult> {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
// Récupérer la config Jira depuis la base de données
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
return {

View File

@@ -3,6 +3,8 @@
import { jiraAnomalyDetection, JiraAnomaly, AnomalyDetectionConfig } from '@/services/integrations/jira/anomaly-detection';
import { JiraAnalyticsService, JiraAnalyticsConfig } from '@/services/integrations/jira/analytics';
import { userPreferencesService } from '@/services/core/user-preferences';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export interface AnomalyDetectionResult {
success: boolean;
@@ -15,8 +17,13 @@ export interface AnomalyDetectionResult {
*/
export async function detectJiraAnomalies(forceRefresh = false): Promise<AnomalyDetectionResult> {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
// Récupérer la config Jira
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
return {

View File

@@ -4,6 +4,8 @@ import { JiraAnalyticsService, JiraAnalyticsConfig } from '@/services/integratio
import { JiraAdvancedFiltersService } from '@/services/integrations/jira/advanced-filters';
import { userPreferencesService } from '@/services/core/user-preferences';
import { AvailableFilters, JiraAnalyticsFilters, JiraAnalytics } from '@/lib/types';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export interface FiltersResult {
success: boolean;
@@ -22,8 +24,13 @@ export interface FilteredAnalyticsResult {
*/
export async function getAvailableJiraFilters(): Promise<FiltersResult> {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
// Récupérer la config Jira
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
return {
@@ -63,8 +70,13 @@ export async function getAvailableJiraFilters(): Promise<FiltersResult> {
*/
export async function getFilteredJiraAnalytics(filters: Partial<JiraAnalyticsFilters>): Promise<FilteredAnalyticsResult> {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
// Récupérer la config Jira
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
return {

View File

@@ -5,6 +5,8 @@ import { userPreferencesService } from '@/services/core/user-preferences';
import { SprintDetails } from '@/components/jira/SprintDetailModal';
import { JiraTask, AssigneeDistribution, StatusDistribution, SprintVelocity } from '@/lib/types';
import { parseDate } from '@/lib/date-utils';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export interface SprintDetailsResult {
success: boolean;
@@ -17,8 +19,13 @@ export interface SprintDetailsResult {
*/
export async function getSprintDetails(sprintName: string): Promise<SprintDetailsResult> {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
// Récupérer la config Jira
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
return {

View File

@@ -4,6 +4,8 @@ import { userPreferencesService } from '@/services/core/user-preferences';
import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
import { Theme } from '@/lib/theme-config';
import { revalidatePath } from 'next/cache';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
/**
* Met à jour les préférences de vue
@@ -13,7 +15,12 @@ export async function updateViewPreferences(updates: Partial<ViewPreferences>):
error?: string;
}> {
try {
await userPreferencesService.updateViewPreferences(updates);
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
await userPreferencesService.updateViewPreferences(session.user.id, updates);
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -33,7 +40,12 @@ export async function updateKanbanFilters(updates: Partial<KanbanFilters>): Prom
error?: string;
}> {
try {
await userPreferencesService.updateKanbanFilters(updates);
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
await userPreferencesService.updateKanbanFilters(session.user.id, updates);
revalidatePath('/kanban');
return { success: true };
} catch (error) {
@@ -53,13 +65,18 @@ export async function updateColumnVisibility(updates: Partial<ColumnVisibility>)
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
const newColumnVisibility: ColumnVisibility = {
...preferences.columnVisibility,
...updates
};
await userPreferencesService.saveColumnVisibility(newColumnVisibility);
await userPreferencesService.saveColumnVisibility(session.user.id, newColumnVisibility);
revalidatePath('/kanban');
return { success: true };
} catch (error) {
@@ -79,10 +96,15 @@ export async function toggleObjectivesVisibility(): Promise<{
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
const showObjectives = !preferences.viewPreferences.showObjectives;
await userPreferencesService.updateViewPreferences({ showObjectives });
await userPreferencesService.updateViewPreferences(session.user.id, { showObjectives });
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -102,10 +124,15 @@ export async function toggleObjectivesCollapse(): Promise<{
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
const collapseObjectives = !preferences.viewPreferences.collapseObjectives;
await userPreferencesService.updateViewPreferences({ collapseObjectives });
await userPreferencesService.updateViewPreferences(session.user.id, { collapseObjectives });
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -125,7 +152,12 @@ export async function setTheme(theme: Theme): Promise<{
error?: string;
}> {
try {
await userPreferencesService.updateViewPreferences({ theme });
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
await userPreferencesService.updateViewPreferences(session.user.id, { theme });
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -145,10 +177,15 @@ export async function toggleTheme(): Promise<{
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
const newTheme = preferences.viewPreferences.theme === 'dark' ? 'light' : 'dark';
await userPreferencesService.updateViewPreferences({ theme: newTheme });
await userPreferencesService.updateViewPreferences(session.user.id, { theme: newTheme });
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -168,13 +205,18 @@ export async function toggleFontSize(): Promise<{
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
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 });
await userPreferencesService.updateViewPreferences(session.user.id, { fontSize: newFontSize });
revalidatePath('/');
return { success: true };
} catch (error) {
@@ -194,7 +236,12 @@ export async function toggleColumnVisibility(status: TaskStatus): Promise<{
error?: string;
}> {
try {
const preferences = await userPreferencesService.getAllPreferences();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
const hiddenStatuses = new Set(preferences.columnVisibility.hiddenStatuses);
if (hiddenStatuses.has(status)) {
@@ -203,7 +250,7 @@ export async function toggleColumnVisibility(status: TaskStatus): Promise<{
hiddenStatuses.add(status);
}
await userPreferencesService.saveColumnVisibility({
await userPreferencesService.saveColumnVisibility(session.user.id, {
hiddenStatuses: Array.from(hiddenStatuses)
});

View File

@@ -3,13 +3,20 @@
import { userPreferencesService } from '@/services/core/user-preferences';
import { revalidatePath } from 'next/cache';
import { tfsService, TfsConfig } from '@/services/integrations/tfs';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
/**
* Sauvegarde la configuration TFS
*/
export async function saveTfsConfig(config: TfsConfig) {
try {
await userPreferencesService.saveTfsConfig(config);
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
await userPreferencesService.saveTfsConfig(session.user.id, config);
// Réinitialiser le service pour prendre en compte la nouvelle config
tfsService.reset();
@@ -34,7 +41,12 @@ export async function saveTfsConfig(config: TfsConfig) {
*/
export async function getTfsConfig() {
try {
const config = await userPreferencesService.getTfsConfig();
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
const config = await userPreferencesService.getTfsConfig(session.user.id);
return { success: true, data: config };
} catch (error) {
console.error('Erreur récupération config TFS:', error);
@@ -64,7 +76,13 @@ export async function saveTfsSchedulerConfig(
tfsSyncInterval: 'hourly' | 'daily' | 'weekly'
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return { success: false, error: 'Non authentifié' };
}
await userPreferencesService.saveTfsSchedulerConfig(
session.user.id,
tfsAutoSync,
tfsSyncInterval
);