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:
@@ -0,0 +1,23 @@
|
|||||||
|
-- Migration pour ajouter userId aux UserPreferences
|
||||||
|
-- et migrer les données existantes vers le premier utilisateur
|
||||||
|
|
||||||
|
-- 1. Ajouter la colonne userId (nullable temporairement)
|
||||||
|
ALTER TABLE "user_preferences" ADD COLUMN "userId" TEXT;
|
||||||
|
|
||||||
|
-- 2. Créer un index unique sur userId
|
||||||
|
CREATE UNIQUE INDEX "user_preferences_userId_key" ON "user_preferences"("userId");
|
||||||
|
|
||||||
|
-- 3. Migrer les données existantes vers le premier utilisateur
|
||||||
|
-- (on suppose qu'il y a au moins un utilisateur dans la table users)
|
||||||
|
UPDATE "user_preferences"
|
||||||
|
SET "userId" = (SELECT id FROM "users" LIMIT 1)
|
||||||
|
WHERE "userId" IS NULL;
|
||||||
|
|
||||||
|
-- 4. Rendre la colonne userId non-nullable
|
||||||
|
-- Note: SQLite ne supporte pas ALTER COLUMN, donc on doit recréer la table
|
||||||
|
-- Mais comme on a déjà des données, on va juste s'assurer que toutes les entrées ont un userId
|
||||||
|
-- En production, on devrait faire une migration plus complexe
|
||||||
|
|
||||||
|
-- 5. Ajouter la contrainte de clé étrangère
|
||||||
|
-- SQLite ne supporte pas les contraintes de clé étrangère dans ALTER TABLE
|
||||||
|
-- La contrainte sera gérée par Prisma au niveau applicatif
|
||||||
@@ -20,6 +20,7 @@ model User {
|
|||||||
password String // Hashé avec bcrypt
|
password String // Hashé avec bcrypt
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
preferences UserPreferences?
|
||||||
|
|
||||||
@@map("users")
|
@@map("users")
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,7 @@ model DailyCheckbox {
|
|||||||
|
|
||||||
model UserPreferences {
|
model UserPreferences {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
userId String @unique
|
||||||
kanbanFilters Json?
|
kanbanFilters Json?
|
||||||
viewPreferences Json?
|
viewPreferences Json?
|
||||||
columnVisibility Json?
|
columnVisibility Json?
|
||||||
@@ -112,6 +114,7 @@ model UserPreferences {
|
|||||||
tfsSyncInterval String @default("daily")
|
tfsSyncInterval String @default("daily")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@map("user_preferences")
|
@@map("user_preferences")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function testJiraFields() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Récupérer la config Jira
|
// Récupérer la config Jira
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig('default');
|
||||||
|
|
||||||
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
console.log('❌ Configuration Jira manquante');
|
console.log('❌ Configuration Jira manquante');
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function testStoryPoints() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Récupérer la config Jira
|
// Récupérer la config Jira
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig('default');
|
||||||
|
|
||||||
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
console.log('❌ Configuration Jira manquante');
|
console.log('❌ Configuration Jira manquante');
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { JiraAnalyticsService } from '@/services/integrations/jira/analytics';
|
import { JiraAnalyticsService } from '@/services/integrations/jira/analytics';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { JiraAnalytics } from '@/lib/types';
|
import { JiraAnalytics } from '@/lib/types';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
export type JiraAnalyticsResult = {
|
export type JiraAnalyticsResult = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -15,8 +17,13 @@ export type JiraAnalyticsResult = {
|
|||||||
*/
|
*/
|
||||||
export async function getJiraAnalytics(forceRefresh = false): Promise<JiraAnalyticsResult> {
|
export async function getJiraAnalytics(forceRefresh = false): Promise<JiraAnalyticsResult> {
|
||||||
try {
|
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
|
// 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) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { jiraAnomalyDetection, JiraAnomaly, AnomalyDetectionConfig } from '@/services/integrations/jira/anomaly-detection';
|
import { jiraAnomalyDetection, JiraAnomaly, AnomalyDetectionConfig } from '@/services/integrations/jira/anomaly-detection';
|
||||||
import { JiraAnalyticsService, JiraAnalyticsConfig } from '@/services/integrations/jira/analytics';
|
import { JiraAnalyticsService, JiraAnalyticsConfig } from '@/services/integrations/jira/analytics';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
export interface AnomalyDetectionResult {
|
export interface AnomalyDetectionResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -15,8 +17,13 @@ export interface AnomalyDetectionResult {
|
|||||||
*/
|
*/
|
||||||
export async function detectJiraAnomalies(forceRefresh = false): Promise<AnomalyDetectionResult> {
|
export async function detectJiraAnomalies(forceRefresh = false): Promise<AnomalyDetectionResult> {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' };
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira
|
// 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) {
|
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { JiraAnalyticsService, JiraAnalyticsConfig } from '@/services/integratio
|
|||||||
import { JiraAdvancedFiltersService } from '@/services/integrations/jira/advanced-filters';
|
import { JiraAdvancedFiltersService } from '@/services/integrations/jira/advanced-filters';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { AvailableFilters, JiraAnalyticsFilters, JiraAnalytics } from '@/lib/types';
|
import { AvailableFilters, JiraAnalyticsFilters, JiraAnalytics } from '@/lib/types';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
export interface FiltersResult {
|
export interface FiltersResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -22,8 +24,13 @@ export interface FilteredAnalyticsResult {
|
|||||||
*/
|
*/
|
||||||
export async function getAvailableJiraFilters(): Promise<FiltersResult> {
|
export async function getAvailableJiraFilters(): Promise<FiltersResult> {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' };
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira
|
// 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) {
|
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
|
||||||
return {
|
return {
|
||||||
@@ -63,8 +70,13 @@ export async function getAvailableJiraFilters(): Promise<FiltersResult> {
|
|||||||
*/
|
*/
|
||||||
export async function getFilteredJiraAnalytics(filters: Partial<JiraAnalyticsFilters>): Promise<FilteredAnalyticsResult> {
|
export async function getFilteredJiraAnalytics(filters: Partial<JiraAnalyticsFilters>): Promise<FilteredAnalyticsResult> {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' };
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira
|
// 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) {
|
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { userPreferencesService } from '@/services/core/user-preferences';
|
|||||||
import { SprintDetails } from '@/components/jira/SprintDetailModal';
|
import { SprintDetails } from '@/components/jira/SprintDetailModal';
|
||||||
import { JiraTask, AssigneeDistribution, StatusDistribution, SprintVelocity } from '@/lib/types';
|
import { JiraTask, AssigneeDistribution, StatusDistribution, SprintVelocity } from '@/lib/types';
|
||||||
import { parseDate } from '@/lib/date-utils';
|
import { parseDate } from '@/lib/date-utils';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
export interface SprintDetailsResult {
|
export interface SprintDetailsResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -17,8 +19,13 @@ export interface SprintDetailsResult {
|
|||||||
*/
|
*/
|
||||||
export async function getSprintDetails(sprintName: string): Promise<SprintDetailsResult> {
|
export async function getSprintDetails(sprintName: string): Promise<SprintDetailsResult> {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' };
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira
|
// 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) {
|
if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken || !jiraConfig?.projectKey) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { userPreferencesService } from '@/services/core/user-preferences';
|
|||||||
import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
|
import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types';
|
||||||
import { Theme } from '@/lib/theme-config';
|
import { Theme } from '@/lib/theme-config';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Met à jour les préférences de vue
|
* Met à jour les préférences de vue
|
||||||
@@ -13,7 +15,12 @@ export async function updateViewPreferences(updates: Partial<ViewPreferences>):
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -33,7 +40,12 @@ export async function updateKanbanFilters(updates: Partial<KanbanFilters>): Prom
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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');
|
revalidatePath('/kanban');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -53,13 +65,18 @@ export async function updateColumnVisibility(updates: Partial<ColumnVisibility>)
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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 = {
|
const newColumnVisibility: ColumnVisibility = {
|
||||||
...preferences.columnVisibility,
|
...preferences.columnVisibility,
|
||||||
...updates
|
...updates
|
||||||
};
|
};
|
||||||
|
|
||||||
await userPreferencesService.saveColumnVisibility(newColumnVisibility);
|
await userPreferencesService.saveColumnVisibility(session.user.id, newColumnVisibility);
|
||||||
revalidatePath('/kanban');
|
revalidatePath('/kanban');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -79,10 +96,15 @@ export async function toggleObjectivesVisibility(): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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;
|
const showObjectives = !preferences.viewPreferences.showObjectives;
|
||||||
|
|
||||||
await userPreferencesService.updateViewPreferences({ showObjectives });
|
await userPreferencesService.updateViewPreferences(session.user.id, { showObjectives });
|
||||||
revalidatePath('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -102,10 +124,15 @@ export async function toggleObjectivesCollapse(): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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;
|
const collapseObjectives = !preferences.viewPreferences.collapseObjectives;
|
||||||
|
|
||||||
await userPreferencesService.updateViewPreferences({ collapseObjectives });
|
await userPreferencesService.updateViewPreferences(session.user.id, { collapseObjectives });
|
||||||
revalidatePath('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -125,7 +152,12 @@ export async function setTheme(theme: Theme): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -145,10 +177,15 @@ export async function toggleTheme(): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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';
|
const newTheme = preferences.viewPreferences.theme === 'dark' ? 'light' : 'dark';
|
||||||
|
|
||||||
await userPreferencesService.updateViewPreferences({ theme: newTheme });
|
await userPreferencesService.updateViewPreferences(session.user.id, { theme: newTheme });
|
||||||
revalidatePath('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -168,13 +205,18 @@ export async function toggleFontSize(): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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 fontSizes: ('small' | 'medium' | 'large')[] = ['small', 'medium', 'large'];
|
||||||
const currentIndex = fontSizes.indexOf(preferences.viewPreferences.fontSize);
|
const currentIndex = fontSizes.indexOf(preferences.viewPreferences.fontSize);
|
||||||
const nextIndex = (currentIndex + 1) % fontSizes.length;
|
const nextIndex = (currentIndex + 1) % fontSizes.length;
|
||||||
const newFontSize = fontSizes[nextIndex];
|
const newFontSize = fontSizes[nextIndex];
|
||||||
|
|
||||||
await userPreferencesService.updateViewPreferences({ fontSize: newFontSize });
|
await userPreferencesService.updateViewPreferences(session.user.id, { fontSize: newFontSize });
|
||||||
revalidatePath('/');
|
revalidatePath('/');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -194,7 +236,12 @@ export async function toggleColumnVisibility(status: TaskStatus): Promise<{
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> {
|
}> {
|
||||||
try {
|
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);
|
const hiddenStatuses = new Set(preferences.columnVisibility.hiddenStatuses);
|
||||||
|
|
||||||
if (hiddenStatuses.has(status)) {
|
if (hiddenStatuses.has(status)) {
|
||||||
@@ -203,7 +250,7 @@ export async function toggleColumnVisibility(status: TaskStatus): Promise<{
|
|||||||
hiddenStatuses.add(status);
|
hiddenStatuses.add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
await userPreferencesService.saveColumnVisibility({
|
await userPreferencesService.saveColumnVisibility(session.user.id, {
|
||||||
hiddenStatuses: Array.from(hiddenStatuses)
|
hiddenStatuses: Array.from(hiddenStatuses)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,20 @@
|
|||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { tfsService, TfsConfig } from '@/services/integrations/tfs';
|
import { tfsService, TfsConfig } from '@/services/integrations/tfs';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sauvegarde la configuration TFS
|
* Sauvegarde la configuration TFS
|
||||||
*/
|
*/
|
||||||
export async function saveTfsConfig(config: TfsConfig) {
|
export async function saveTfsConfig(config: TfsConfig) {
|
||||||
try {
|
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
|
// Réinitialiser le service pour prendre en compte la nouvelle config
|
||||||
tfsService.reset();
|
tfsService.reset();
|
||||||
@@ -34,7 +41,12 @@ export async function saveTfsConfig(config: TfsConfig) {
|
|||||||
*/
|
*/
|
||||||
export async function getTfsConfig() {
|
export async function getTfsConfig() {
|
||||||
try {
|
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 };
|
return { success: true, data: config };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur récupération config TFS:', error);
|
console.error('Erreur récupération config TFS:', error);
|
||||||
@@ -64,7 +76,13 @@ export async function saveTfsSchedulerConfig(
|
|||||||
tfsSyncInterval: 'hourly' | 'daily' | 'weekly'
|
tfsSyncInterval: 'hourly' | 'daily' | 'weekly'
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' };
|
||||||
|
}
|
||||||
|
|
||||||
await userPreferencesService.saveTfsSchedulerConfig(
|
await userPreferencesService.saveTfsSchedulerConfig(
|
||||||
|
session.user.id,
|
||||||
tfsAutoSync,
|
tfsAutoSync,
|
||||||
tfsSyncInterval
|
tfsSyncInterval
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
|
|||||||
import { createJiraService, JiraService } from '@/services/integrations/jira/jira';
|
import { createJiraService, JiraService } from '@/services/integrations/jira/jira';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { jiraScheduler } from '@/services/integrations/jira/scheduler';
|
import { jiraScheduler } from '@/services/integrations/jira/scheduler';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route POST /api/jira/sync
|
* Route POST /api/jira/sync
|
||||||
@@ -10,6 +12,14 @@ import { jiraScheduler } from '@/services/integrations/jira/scheduler';
|
|||||||
*/
|
*/
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Vérifier s'il y a des actions spécifiques (scheduler)
|
// Vérifier s'il y a des actions spécifiques (scheduler)
|
||||||
const body = await request.json().catch(() => ({}));
|
const body = await request.json().catch(() => ({}));
|
||||||
const { action, ...params } = body;
|
const { action, ...params } = body;
|
||||||
@@ -30,6 +40,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
case 'config':
|
case 'config':
|
||||||
await userPreferencesService.saveJiraSchedulerConfig(
|
await userPreferencesService.saveJiraSchedulerConfig(
|
||||||
|
session.user.id,
|
||||||
params.jiraAutoSync,
|
params.jiraAutoSync,
|
||||||
params.jiraSyncInterval
|
params.jiraSyncInterval
|
||||||
);
|
);
|
||||||
@@ -50,7 +61,7 @@ export async function POST(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Synchronisation normale (manuelle)
|
// Synchronisation normale (manuelle)
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
|
||||||
|
|
||||||
let jiraService: JiraService | null = null;
|
let jiraService: JiraService | null = null;
|
||||||
|
|
||||||
@@ -124,8 +135,16 @@ export async function POST(request: Request) {
|
|||||||
*/
|
*/
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Essayer d'abord la config depuis la base de données
|
// Essayer d'abord la config depuis la base de données
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
|
||||||
|
|
||||||
let jiraService: JiraService | null = null;
|
let jiraService: JiraService | null = null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { createJiraService } from '@/services/integrations/jira/jira';
|
import { createJiraService } from '@/services/integrations/jira/jira';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/jira/validate-project
|
* POST /api/jira/validate-project
|
||||||
@@ -8,6 +10,14 @@ import { userPreferencesService } from '@/services/core/user-preferences';
|
|||||||
*/
|
*/
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { projectKey } = body;
|
const { projectKey } = body;
|
||||||
|
|
||||||
@@ -19,7 +29,7 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira depuis la base de données
|
// 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) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { JiraConfig } from '@/lib/types';
|
import { JiraConfig } from '@/lib/types';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/user-preferences/jira-config
|
* GET /api/user-preferences/jira-config
|
||||||
@@ -8,7 +10,15 @@ import { JiraConfig } from '@/lib/types';
|
|||||||
*/
|
*/
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
|
||||||
return NextResponse.json({ jiraConfig });
|
return NextResponse.json({ jiraConfig });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors de la récupération de la config Jira:', error);
|
console.error('Erreur lors de la récupération de la config Jira:', error);
|
||||||
@@ -25,6 +35,14 @@ export async function GET() {
|
|||||||
*/
|
*/
|
||||||
export async function PUT(request: NextRequest) {
|
export async function PUT(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { baseUrl, email, apiToken, projectKey, ignoredProjects } = body;
|
const { baseUrl, email, apiToken, projectKey, ignoredProjects } = body;
|
||||||
|
|
||||||
@@ -66,7 +84,7 @@ export async function PUT(request: NextRequest) {
|
|||||||
: []
|
: []
|
||||||
};
|
};
|
||||||
|
|
||||||
await userPreferencesService.saveJiraConfig(jiraConfig);
|
await userPreferencesService.saveJiraConfig(session.user.id, jiraConfig);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -91,6 +109,14 @@ export async function PUT(request: NextRequest) {
|
|||||||
*/
|
*/
|
||||||
export async function DELETE() {
|
export async function DELETE() {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const defaultConfig: JiraConfig = {
|
const defaultConfig: JiraConfig = {
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
email: '',
|
email: '',
|
||||||
@@ -99,7 +125,7 @@ export async function DELETE() {
|
|||||||
ignoredProjects: []
|
ignoredProjects: []
|
||||||
};
|
};
|
||||||
|
|
||||||
await userPreferencesService.saveJiraConfig(defaultConfig);
|
await userPreferencesService.saveJiraConfig(session.user.id, defaultConfig);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/user-preferences - Récupère toutes les préférences utilisateur
|
* GET /api/user-preferences - Récupère toutes les préférences utilisateur
|
||||||
*/
|
*/
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const preferences = await userPreferencesService.getAllPreferences();
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences(session.user.id);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -29,9 +39,17 @@ export async function GET() {
|
|||||||
*/
|
*/
|
||||||
export async function PUT(request: NextRequest) {
|
export async function PUT(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: 'Non authentifié' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const preferences = await request.json();
|
const preferences = await request.json();
|
||||||
|
|
||||||
await userPreferencesService.saveAllPreferences(preferences);
|
await userPreferencesService.saveAllPreferences(session.user.id, preferences);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { getJiraAnalytics } from '@/actions/jira-analytics';
|
import { getJiraAnalytics } from '@/actions/jira-analytics';
|
||||||
import { JiraDashboardPageClient } from './JiraDashboardPageClient';
|
import { JiraDashboardPageClient } from './JiraDashboardPageClient';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
// Force dynamic rendering
|
// Force dynamic rendering
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export default async function JiraDashboardPage() {
|
export default async function JiraDashboardPage() {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return <div>Non authentifié</div>;
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer la config Jira côté serveur
|
// Récupérer la config Jira côté serveur
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
|
||||||
|
|
||||||
// Récupérer les analytics côté serveur (utilise le cache du service)
|
// Récupérer les analytics côté serveur (utilise le cache du service)
|
||||||
let initialAnalytics = null;
|
let initialAnalytics = null;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { KeyboardShortcutsProvider } from "@/contexts/KeyboardShortcutsContext";
|
|||||||
import { userPreferencesService } from "@/services/core/user-preferences";
|
import { userPreferencesService } from "@/services/core/user-preferences";
|
||||||
import { KeyboardShortcuts } from "@/components/KeyboardShortcuts";
|
import { KeyboardShortcuts } from "@/components/KeyboardShortcuts";
|
||||||
import { AuthProvider } from "../components/AuthProvider";
|
import { AuthProvider } from "../components/AuthProvider";
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@@ -29,8 +31,14 @@ export default async function RootLayout({
|
|||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
// Récupérer toutes les préférences côté serveur pour le SSR
|
// Récupérer la session côté serveur pour le SSR
|
||||||
const initialPreferences = await userPreferencesService.getAllPreferences();
|
const session = await getServerSession(authOptions);
|
||||||
|
|
||||||
|
// Charger les préférences seulement si l'utilisateur est connecté
|
||||||
|
// Sinon, les préférences par défaut seront chargées côté client
|
||||||
|
const initialPreferences = session?.user?.id
|
||||||
|
? await userPreferencesService.getAllPreferences(session.user.id)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
@@ -39,12 +47,12 @@ export default async function RootLayout({
|
|||||||
>
|
>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
initialTheme={initialPreferences.viewPreferences.theme}
|
initialTheme={initialPreferences?.viewPreferences.theme || 'light'}
|
||||||
userPreferredTheme={initialPreferences.viewPreferences.theme === 'light' ? 'dark' : initialPreferences.viewPreferences.theme}
|
userPreferredTheme={initialPreferences?.viewPreferences.theme === 'light' ? 'dark' : initialPreferences?.viewPreferences.theme || 'light'}
|
||||||
>
|
>
|
||||||
<KeyboardShortcutsProvider>
|
<KeyboardShortcutsProvider>
|
||||||
<KeyboardShortcuts />
|
<KeyboardShortcuts />
|
||||||
<JiraConfigProvider config={initialPreferences.jiraConfig}>
|
<JiraConfigProvider config={initialPreferences?.jiraConfig || { enabled: false }}>
|
||||||
<UserPreferencesProvider initialPreferences={initialPreferences}>
|
<UserPreferencesProvider initialPreferences={initialPreferences}>
|
||||||
{children}
|
{children}
|
||||||
</UserPreferencesProvider>
|
</UserPreferencesProvider>
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||||
import { IntegrationsSettingsPageClient } from '@/components/settings/IntegrationsSettingsPageClient';
|
import { IntegrationsSettingsPageClient } from '@/components/settings/IntegrationsSettingsPageClient';
|
||||||
|
import { getServerSession } from 'next-auth';
|
||||||
|
import { authOptions } from '@/lib/auth';
|
||||||
|
|
||||||
// Force dynamic rendering for real-time data
|
// Force dynamic rendering for real-time data
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export default async function IntegrationsSettingsPage() {
|
export default async function IntegrationsSettingsPage() {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return <div>Non authentifié</div>;
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch data server-side
|
// Fetch data server-side
|
||||||
// Preferences are now available via context
|
// Preferences are now available via context
|
||||||
const [jiraConfig, tfsConfig] = await Promise.all([
|
const [jiraConfig, tfsConfig] = await Promise.all([
|
||||||
userPreferencesService.getJiraConfig(),
|
userPreferencesService.getJiraConfig(session.user.id),
|
||||||
userPreferencesService.getTfsConfig()
|
userPreferencesService.getTfsConfig(session.user.id)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function TfsConfigForm() {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const result = await getTfsConfig();
|
const result = await getTfsConfig();
|
||||||
if (result.success) {
|
if (result.success && result.data) {
|
||||||
setConfig(result.data);
|
setConfig(result.data);
|
||||||
// Afficher le formulaire par défaut si TFS n'est pas configuré
|
// Afficher le formulaire par défaut si TFS n'est pas configuré
|
||||||
const isConfigured =
|
const isConfigured =
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
toggleColumnVisibility as toggleColumnVisibilityAction
|
toggleColumnVisibility as toggleColumnVisibilityAction
|
||||||
} from '@/actions/preferences';
|
} from '@/actions/preferences';
|
||||||
import { useTheme } from './ThemeContext';
|
import { useTheme } from './ThemeContext';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
|
|
||||||
interface UserPreferencesContextType {
|
interface UserPreferencesContextType {
|
||||||
preferences: UserPreferences;
|
preferences: UserPreferences;
|
||||||
@@ -77,6 +78,38 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
|
|||||||
const [preferences, setPreferences] = useState<UserPreferences>(initialPreferences || defaultPreferences);
|
const [preferences, setPreferences] = useState<UserPreferences>(initialPreferences || defaultPreferences);
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const { theme, toggleTheme: themeToggleTheme, setTheme: themeSetTheme } = useTheme();
|
const { theme, toggleTheme: themeToggleTheme, setTheme: themeSetTheme } = useTheme();
|
||||||
|
const { data: session, status } = useSession();
|
||||||
|
|
||||||
|
// Fonction pour charger les préférences côté client
|
||||||
|
const loadUserPreferences = useCallback(async () => {
|
||||||
|
if (status === 'loading') return; // Attendre que la session soit chargée
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user-preferences');
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success) {
|
||||||
|
setPreferences(result.data);
|
||||||
|
// Synchroniser le thème avec le ThemeContext
|
||||||
|
if (result.data.viewPreferences.theme !== theme) {
|
||||||
|
themeSetTheme(result.data.viewPreferences.theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors du chargement des préférences:', error);
|
||||||
|
}
|
||||||
|
}, [status, theme, themeSetTheme]);
|
||||||
|
|
||||||
|
// Recharger les préférences quand la session change (login/logout)
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'authenticated') {
|
||||||
|
loadUserPreferences();
|
||||||
|
} else if (status === 'unauthenticated') {
|
||||||
|
// Reset aux préférences par défaut quand l'utilisateur se déconnecte
|
||||||
|
setPreferences(defaultPreferences);
|
||||||
|
}
|
||||||
|
}, [status, loadUserPreferences]);
|
||||||
|
|
||||||
// Synchroniser les préférences avec le thème actuel du ThemeContext
|
// Synchroniser les préférences avec le thème actuel du ThemeContext
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -59,18 +59,16 @@ const DEFAULT_PREFERENCES: UserPreferences = {
|
|||||||
* Service pour gérer les préférences utilisateur en base de données
|
* Service pour gérer les préférences utilisateur en base de données
|
||||||
*/
|
*/
|
||||||
class 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)
|
* Récupère ou crée l'entrée user preferences (avec upsert pour éviter les doublons)
|
||||||
*/
|
*/
|
||||||
private async getOrCreateUserPreferences() {
|
private async getOrCreateUserPreferences(userId: string) {
|
||||||
// Utiliser upsert pour éviter les conditions de course
|
// Utiliser upsert pour éviter les conditions de course
|
||||||
const userPrefs = await prisma.userPreferences.upsert({
|
const userPrefs = await prisma.userPreferences.upsert({
|
||||||
where: { id: 'default' }, // ID fixe pour l'utilisateur unique
|
where: { userId }, // Utiliser userId au lieu de id
|
||||||
update: {}, // Ne rien mettre à jour si existe
|
update: {}, // Ne rien mettre à jour si existe
|
||||||
create: {
|
create: {
|
||||||
id: 'default',
|
userId,
|
||||||
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
|
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
|
||||||
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
|
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
|
||||||
columnVisibility: DEFAULT_PREFERENCES.columnVisibility,
|
columnVisibility: DEFAULT_PREFERENCES.columnVisibility,
|
||||||
@@ -79,7 +77,7 @@ class UserPreferencesService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// S'assurer que les nouveaux champs existent (migration douce)
|
// S'assurer que les nouveaux champs existent (migration douce)
|
||||||
await this.ensureJiraSchedulerFields();
|
await this.ensureJiraSchedulerFields(userId);
|
||||||
|
|
||||||
return userPrefs;
|
return userPrefs;
|
||||||
}
|
}
|
||||||
@@ -87,13 +85,13 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* S'assure que les champs jiraAutoSync et jiraSyncInterval existent
|
* S'assure que les champs jiraAutoSync et jiraSyncInterval existent
|
||||||
*/
|
*/
|
||||||
private async ensureJiraSchedulerFields(): Promise<void> {
|
private async ensureJiraSchedulerFields(userId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await prisma.$executeRaw`
|
await prisma.$executeRaw`
|
||||||
UPDATE user_preferences
|
UPDATE user_preferences
|
||||||
SET jiraAutoSync = COALESCE(jiraAutoSync, ${DEFAULT_PREFERENCES.jiraAutoSync}),
|
SET jiraAutoSync = COALESCE(jiraAutoSync, ${DEFAULT_PREFERENCES.jiraAutoSync}),
|
||||||
jiraSyncInterval = COALESCE(jiraSyncInterval, ${DEFAULT_PREFERENCES.jiraSyncInterval})
|
jiraSyncInterval = COALESCE(jiraSyncInterval, ${DEFAULT_PREFERENCES.jiraSyncInterval})
|
||||||
WHERE id = 'default'
|
WHERE userId = ${userId}
|
||||||
`;
|
`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignorer les erreurs si les colonnes n'existent pas encore
|
// Ignorer les erreurs si les colonnes n'existent pas encore
|
||||||
@@ -106,11 +104,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde les filtres Kanban
|
* Sauvegarde les filtres Kanban
|
||||||
*/
|
*/
|
||||||
async saveKanbanFilters(filters: KanbanFilters): Promise<void> {
|
async saveKanbanFilters(userId: string, filters: KanbanFilters): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId },
|
||||||
data: { kanbanFilters: filters }
|
data: { kanbanFilters: filters }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -122,9 +120,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère les filtres Kanban
|
* Récupère les filtres Kanban
|
||||||
*/
|
*/
|
||||||
async getKanbanFilters(): Promise<KanbanFilters> {
|
async getKanbanFilters(userId: string): Promise<KanbanFilters> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const filters = userPrefs.kanbanFilters as KanbanFilters | null;
|
const filters = userPrefs.kanbanFilters as KanbanFilters | null;
|
||||||
return { ...DEFAULT_PREFERENCES.kanbanFilters, ...(filters || {}) };
|
return { ...DEFAULT_PREFERENCES.kanbanFilters, ...(filters || {}) };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -138,11 +136,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde les préférences de vue
|
* Sauvegarde les préférences de vue
|
||||||
*/
|
*/
|
||||||
async saveViewPreferences(preferences: ViewPreferences): Promise<void> {
|
async saveViewPreferences(userId: string, preferences: ViewPreferences): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId },
|
||||||
data: { viewPreferences: preferences }
|
data: { viewPreferences: preferences }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -157,9 +155,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère les préférences de vue
|
* Récupère les préférences de vue
|
||||||
*/
|
*/
|
||||||
async getViewPreferences(): Promise<ViewPreferences> {
|
async getViewPreferences(userId: string): Promise<ViewPreferences> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const preferences = userPrefs.viewPreferences as ViewPreferences | null;
|
const preferences = userPrefs.viewPreferences as ViewPreferences | null;
|
||||||
return { ...DEFAULT_PREFERENCES.viewPreferences, ...(preferences || {}) };
|
return { ...DEFAULT_PREFERENCES.viewPreferences, ...(preferences || {}) };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -176,11 +174,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde la visibilité des colonnes
|
* Sauvegarde la visibilité des colonnes
|
||||||
*/
|
*/
|
||||||
async saveColumnVisibility(visibility: ColumnVisibility): Promise<void> {
|
async saveColumnVisibility(userId: string, visibility: ColumnVisibility): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId },
|
||||||
data: { columnVisibility: visibility },
|
data: { columnVisibility: visibility },
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -195,9 +193,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère la visibilité des colonnes
|
* Récupère la visibilité des colonnes
|
||||||
*/
|
*/
|
||||||
async getColumnVisibility(): Promise<ColumnVisibility> {
|
async getColumnVisibility(userId: string): Promise<ColumnVisibility> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const visibility = userPrefs.columnVisibility as ColumnVisibility | null;
|
const visibility = userPrefs.columnVisibility as ColumnVisibility | null;
|
||||||
return { ...DEFAULT_PREFERENCES.columnVisibility, ...(visibility || {}) };
|
return { ...DEFAULT_PREFERENCES.columnVisibility, ...(visibility || {}) };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -214,9 +212,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère uniquement le thème pour le SSR (optimisé)
|
* Récupère uniquement le thème pour le SSR (optimisé)
|
||||||
*/
|
*/
|
||||||
async getTheme(): Promise<Theme> {
|
async getTheme(userId: string): Promise<Theme> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const viewPrefs = userPrefs.viewPreferences as ViewPreferences;
|
const viewPrefs = userPrefs.viewPreferences as ViewPreferences;
|
||||||
return viewPrefs.theme;
|
return viewPrefs.theme;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -230,11 +228,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde la configuration Jira
|
* Sauvegarde la configuration Jira
|
||||||
*/
|
*/
|
||||||
async saveJiraConfig(config: JiraConfig): Promise<void> {
|
async saveJiraConfig(userId: string, config: JiraConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId },
|
||||||
data: { jiraConfig: config as any } // eslint-disable-line @typescript-eslint/no-explicit-any
|
data: { jiraConfig: config as any } // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -246,9 +244,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère la configuration Jira depuis la base de données avec fallback sur les variables d'environnement
|
* Récupère la configuration Jira depuis la base de données avec fallback sur les variables d'environnement
|
||||||
*/
|
*/
|
||||||
async getJiraConfig(): Promise<JiraConfig> {
|
async getJiraConfig(userId: string): Promise<JiraConfig> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const dbConfig = userPrefs.jiraConfig as JiraConfig | null;
|
const dbConfig = userPrefs.jiraConfig as JiraConfig | null;
|
||||||
|
|
||||||
// Si config en DB, l'utiliser
|
// Si config en DB, l'utiliser
|
||||||
@@ -278,11 +276,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde la configuration TFS
|
* Sauvegarde la configuration TFS
|
||||||
*/
|
*/
|
||||||
async saveTfsConfig(config: TfsConfig): Promise<void> {
|
async saveTfsConfig(userId: string, config: TfsConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId },
|
||||||
data: { tfsConfig: config as any }, // eslint-disable-line @typescript-eslint/no-explicit-any
|
data: { tfsConfig: config as any }, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -294,9 +292,9 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère la configuration TFS depuis la base de données
|
* Récupère la configuration TFS depuis la base de données
|
||||||
*/
|
*/
|
||||||
async getTfsConfig(): Promise<TfsConfig> {
|
async getTfsConfig(userId: string): Promise<TfsConfig> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const dbConfig = userPrefs.tfsConfig as TfsConfig | null;
|
const dbConfig = userPrefs.tfsConfig as TfsConfig | null;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -319,15 +317,16 @@ class UserPreferencesService {
|
|||||||
* Sauvegarde les préférences du scheduler TFS
|
* Sauvegarde les préférences du scheduler TFS
|
||||||
*/
|
*/
|
||||||
async saveTfsSchedulerConfig(
|
async saveTfsSchedulerConfig(
|
||||||
|
userId: string,
|
||||||
tfsAutoSync: boolean,
|
tfsAutoSync: boolean,
|
||||||
tfsSyncInterval: 'hourly' | 'daily' | 'weekly'
|
tfsSyncInterval: 'hourly' | 'daily' | 'weekly'
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.$executeRaw`
|
await prisma.$executeRaw`
|
||||||
UPDATE user_preferences
|
UPDATE user_preferences
|
||||||
SET tfsAutoSync = ${tfsAutoSync}, tfsSyncInterval = ${tfsSyncInterval}
|
SET tfsAutoSync = ${tfsAutoSync}, tfsSyncInterval = ${tfsSyncInterval}
|
||||||
WHERE id = ${userPrefs.id}
|
WHERE userId = ${userId}
|
||||||
`;
|
`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -341,16 +340,16 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère les préférences du scheduler TFS
|
* Récupère les préférences du scheduler TFS
|
||||||
*/
|
*/
|
||||||
async getTfsSchedulerConfig(): Promise<{
|
async getTfsSchedulerConfig(userId: string): Promise<{
|
||||||
tfsAutoSync: boolean;
|
tfsAutoSync: boolean;
|
||||||
tfsSyncInterval: 'hourly' | 'daily' | 'weekly';
|
tfsSyncInterval: 'hourly' | 'daily' | 'weekly';
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
const result = await prisma.$queryRaw<
|
const result = await prisma.$queryRaw<
|
||||||
Array<{ tfsAutoSync: number; tfsSyncInterval: string }>
|
Array<{ tfsAutoSync: number; tfsSyncInterval: string }>
|
||||||
>`
|
>`
|
||||||
SELECT tfsAutoSync, tfsSyncInterval FROM user_preferences WHERE id = ${userPrefs.id}
|
SELECT tfsAutoSync, tfsSyncInterval FROM user_preferences WHERE userId = ${userId}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
@@ -384,16 +383,17 @@ class UserPreferencesService {
|
|||||||
* Sauvegarde les préférences du scheduler Jira
|
* Sauvegarde les préférences du scheduler Jira
|
||||||
*/
|
*/
|
||||||
async saveJiraSchedulerConfig(
|
async saveJiraSchedulerConfig(
|
||||||
|
userId: string,
|
||||||
jiraAutoSync: boolean,
|
jiraAutoSync: boolean,
|
||||||
jiraSyncInterval: 'hourly' | 'daily' | 'weekly'
|
jiraSyncInterval: 'hourly' | 'daily' | 'weekly'
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
// Utiliser une requête SQL brute temporairement pour éviter les problèmes de types
|
// Utiliser une requête SQL brute temporairement pour éviter les problèmes de types
|
||||||
await prisma.$executeRaw`
|
await prisma.$executeRaw`
|
||||||
UPDATE user_preferences
|
UPDATE user_preferences
|
||||||
SET jiraAutoSync = ${jiraAutoSync}, jiraSyncInterval = ${jiraSyncInterval}
|
SET jiraAutoSync = ${jiraAutoSync}, jiraSyncInterval = ${jiraSyncInterval}
|
||||||
WHERE id = ${userPrefs.id}
|
WHERE userId = ${userId}
|
||||||
`;
|
`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -407,17 +407,17 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère les préférences du scheduler Jira
|
* Récupère les préférences du scheduler Jira
|
||||||
*/
|
*/
|
||||||
async getJiraSchedulerConfig(): Promise<{
|
async getJiraSchedulerConfig(userId: string): Promise<{
|
||||||
jiraAutoSync: boolean;
|
jiraAutoSync: boolean;
|
||||||
jiraSyncInterval: 'hourly' | 'daily' | 'weekly';
|
jiraSyncInterval: 'hourly' | 'daily' | 'weekly';
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
// Utiliser une requête SQL brute pour récupérer les nouveaux champs
|
// Utiliser une requête SQL brute pour récupérer les nouveaux champs
|
||||||
const result = await prisma.$queryRaw<
|
const result = await prisma.$queryRaw<
|
||||||
Array<{ jiraAutoSync: number; jiraSyncInterval: string }>
|
Array<{ jiraAutoSync: number; jiraSyncInterval: string }>
|
||||||
>`
|
>`
|
||||||
SELECT jiraAutoSync, jiraSyncInterval FROM user_preferences WHERE id = ${userPrefs.id}
|
SELECT jiraAutoSync, jiraSyncInterval FROM user_preferences WHERE userId = ${userPrefs.userId}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
@@ -445,14 +445,14 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Récupère les préférences utilisateur (alias pour getAllPreferences)
|
* Récupère les préférences utilisateur (alias pour getAllPreferences)
|
||||||
*/
|
*/
|
||||||
async getUserPreferences(): Promise<UserPreferences> {
|
async getUserPreferences(userId: string): Promise<UserPreferences> {
|
||||||
return this.getAllPreferences();
|
return this.getAllPreferences(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère toutes les préférences utilisateur
|
* Récupère toutes les préférences utilisateur
|
||||||
*/
|
*/
|
||||||
async getAllPreferences(): Promise<UserPreferences> {
|
async getAllPreferences(userId: string): Promise<UserPreferences> {
|
||||||
const [
|
const [
|
||||||
kanbanFilters,
|
kanbanFilters,
|
||||||
viewPreferences,
|
viewPreferences,
|
||||||
@@ -462,13 +462,13 @@ class UserPreferencesService {
|
|||||||
tfsConfig,
|
tfsConfig,
|
||||||
tfsSchedulerConfig,
|
tfsSchedulerConfig,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getKanbanFilters(),
|
this.getKanbanFilters(userId),
|
||||||
this.getViewPreferences(),
|
this.getViewPreferences(userId),
|
||||||
this.getColumnVisibility(),
|
this.getColumnVisibility(userId),
|
||||||
this.getJiraConfig(),
|
this.getJiraConfig(userId),
|
||||||
this.getJiraSchedulerConfig(),
|
this.getJiraSchedulerConfig(userId),
|
||||||
this.getTfsConfig(),
|
this.getTfsConfig(userId),
|
||||||
this.getTfsSchedulerConfig(),
|
this.getTfsSchedulerConfig(userId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -487,18 +487,20 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Sauvegarde toutes les préférences utilisateur
|
* Sauvegarde toutes les préférences utilisateur
|
||||||
*/
|
*/
|
||||||
async saveAllPreferences(preferences: UserPreferences): Promise<void> {
|
async saveAllPreferences(userId: string, preferences: UserPreferences): Promise<void> {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.saveKanbanFilters(preferences.kanbanFilters),
|
this.saveKanbanFilters(userId, preferences.kanbanFilters),
|
||||||
this.saveViewPreferences(preferences.viewPreferences),
|
this.saveViewPreferences(userId, preferences.viewPreferences),
|
||||||
this.saveColumnVisibility(preferences.columnVisibility),
|
this.saveColumnVisibility(userId, preferences.columnVisibility),
|
||||||
this.saveJiraConfig(preferences.jiraConfig),
|
this.saveJiraConfig(userId, preferences.jiraConfig),
|
||||||
this.saveJiraSchedulerConfig(
|
this.saveJiraSchedulerConfig(
|
||||||
|
userId,
|
||||||
preferences.jiraAutoSync,
|
preferences.jiraAutoSync,
|
||||||
preferences.jiraSyncInterval
|
preferences.jiraSyncInterval
|
||||||
),
|
),
|
||||||
this.saveTfsConfig(preferences.tfsConfig),
|
this.saveTfsConfig(userId, preferences.tfsConfig),
|
||||||
this.saveTfsSchedulerConfig(
|
this.saveTfsSchedulerConfig(
|
||||||
|
userId,
|
||||||
preferences.tfsAutoSync,
|
preferences.tfsAutoSync,
|
||||||
preferences.tfsSyncInterval
|
preferences.tfsSyncInterval
|
||||||
),
|
),
|
||||||
@@ -508,11 +510,11 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Remet à zéro toutes les préférences
|
* Remet à zéro toutes les préférences
|
||||||
*/
|
*/
|
||||||
async resetAllPreferences(): Promise<void> {
|
async resetAllPreferences(userId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userPrefs = await this.getOrCreateUserPreferences();
|
const userPrefs = await this.getOrCreateUserPreferences(userId);
|
||||||
await prisma.userPreferences.update({
|
await prisma.userPreferences.update({
|
||||||
where: { id: userPrefs.id },
|
where: { userId: userPrefs.userId },
|
||||||
data: {
|
data: {
|
||||||
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
|
kanbanFilters: DEFAULT_PREFERENCES.kanbanFilters,
|
||||||
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
|
viewPreferences: DEFAULT_PREFERENCES.viewPreferences,
|
||||||
@@ -531,24 +533,24 @@ class UserPreferencesService {
|
|||||||
/**
|
/**
|
||||||
* Met à jour partiellement les filtres Kanban
|
* Met à jour partiellement les filtres Kanban
|
||||||
*/
|
*/
|
||||||
async updateKanbanFilters(updates: Partial<KanbanFilters>): Promise<void> {
|
async updateKanbanFilters(userId: string, updates: Partial<KanbanFilters>): Promise<void> {
|
||||||
const current = await this.getKanbanFilters();
|
const current = await this.getKanbanFilters(userId);
|
||||||
await this.saveKanbanFilters({ ...current, ...updates });
|
await this.saveKanbanFilters(userId, { ...current, ...updates });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Met à jour partiellement les préférences de vue
|
* Met à jour partiellement les préférences de vue
|
||||||
*/
|
*/
|
||||||
async updateViewPreferences(updates: Partial<ViewPreferences>): Promise<void> {
|
async updateViewPreferences(userId: string, updates: Partial<ViewPreferences>): Promise<void> {
|
||||||
const current = await this.getViewPreferences();
|
const current = await this.getViewPreferences(userId);
|
||||||
await this.saveViewPreferences({ ...current, ...updates });
|
await this.saveViewPreferences(userId, { ...current, ...updates });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Met à jour la visibilité d'une colonne spécifique
|
* Met à jour la visibilité d'une colonne spécifique
|
||||||
*/
|
*/
|
||||||
async toggleColumnVisibility(status: TaskStatus): Promise<void> {
|
async toggleColumnVisibility(userId: string, status: TaskStatus): Promise<void> {
|
||||||
const current = await this.getColumnVisibility();
|
const current = await this.getColumnVisibility(userId);
|
||||||
const hiddenStatuses = new Set(current.hiddenStatuses);
|
const hiddenStatuses = new Set(current.hiddenStatuses);
|
||||||
|
|
||||||
if (hiddenStatuses.has(status)) {
|
if (hiddenStatuses.has(status)) {
|
||||||
@@ -557,7 +559,7 @@ class UserPreferencesService {
|
|||||||
hiddenStatuses.add(status);
|
hiddenStatuses.add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.saveColumnVisibility({
|
await this.saveColumnVisibility(userId, {
|
||||||
hiddenStatuses: Array.from(hiddenStatuses)
|
hiddenStatuses: Array.from(hiddenStatuses)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ export class BackupService {
|
|||||||
*/
|
*/
|
||||||
private async loadConfigFromDB(): Promise<void> {
|
private async loadConfigFromDB(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const preferences = await userPreferencesService.getAllPreferences();
|
// Pour le service de backup, on utilise un userId par défaut
|
||||||
|
// car il n'a pas accès à la session
|
||||||
|
const preferences = await userPreferencesService.getAllPreferences('default');
|
||||||
if (preferences.viewPreferences && typeof preferences.viewPreferences === 'object') {
|
if (preferences.viewPreferences && typeof preferences.viewPreferences === 'object') {
|
||||||
const backupConfig = (preferences.viewPreferences as Record<string, unknown>).backupConfig;
|
const backupConfig = (preferences.viewPreferences as Record<string, unknown>).backupConfig;
|
||||||
if (backupConfig) {
|
if (backupConfig) {
|
||||||
@@ -75,15 +77,15 @@ export class BackupService {
|
|||||||
// Pour l'instant, on stocke la config backup en tant que JSON dans viewPreferences
|
// Pour l'instant, on stocke la config backup en tant que JSON dans viewPreferences
|
||||||
// TODO: Ajouter un champ dédié dans le schéma pour la config backup
|
// TODO: Ajouter un champ dédié dans le schéma pour la config backup
|
||||||
await prisma.userPreferences.upsert({
|
await prisma.userPreferences.upsert({
|
||||||
where: { id: 'default' },
|
where: { userId: 'default' },
|
||||||
update: {
|
update: {
|
||||||
viewPreferences: JSON.parse(JSON.stringify({
|
viewPreferences: JSON.parse(JSON.stringify({
|
||||||
...(await userPreferencesService.getViewPreferences()),
|
...(await userPreferencesService.getViewPreferences('default')),
|
||||||
backupConfig: this.config
|
backupConfig: this.config
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: 'default',
|
userId: 'default',
|
||||||
kanbanFilters: {},
|
kanbanFilters: {},
|
||||||
viewPreferences: JSON.parse(JSON.stringify({ backupConfig: this.config })),
|
viewPreferences: JSON.parse(JSON.stringify({ backupConfig: this.config })),
|
||||||
columnVisibility: {},
|
columnVisibility: {},
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export class JiraScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier que Jira est configuré
|
// Vérifier que Jira est configuré
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
// Pour les services système, on utilise un userId par défaut
|
||||||
|
const jiraConfig = await userPreferencesService.getJiraConfig('default');
|
||||||
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
console.log('⚠️ Jira not configured, scheduler cannot start');
|
console.log('⚠️ Jira not configured, scheduler cannot start');
|
||||||
return;
|
return;
|
||||||
@@ -84,7 +85,7 @@ export class JiraScheduler {
|
|||||||
console.log('🔄 Starting scheduled Jira sync...');
|
console.log('🔄 Starting scheduled Jira sync...');
|
||||||
|
|
||||||
// Récupérer la config Jira
|
// Récupérer la config Jira
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig('default');
|
||||||
|
|
||||||
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
|
||||||
console.log('⚠️ Jira config incomplete, skipping scheduled sync');
|
console.log('⚠️ Jira config incomplete, skipping scheduled sync');
|
||||||
@@ -154,8 +155,8 @@ export class JiraScheduler {
|
|||||||
private async getConfig(): Promise<JiraSchedulerConfig> {
|
private async getConfig(): Promise<JiraSchedulerConfig> {
|
||||||
try {
|
try {
|
||||||
const [jiraConfig, schedulerConfig] = await Promise.all([
|
const [jiraConfig, schedulerConfig] = await Promise.all([
|
||||||
userPreferencesService.getJiraConfig(),
|
userPreferencesService.getJiraConfig('default'),
|
||||||
userPreferencesService.getJiraSchedulerConfig()
|
userPreferencesService.getJiraSchedulerConfig('default')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -180,7 +181,7 @@ export class JiraScheduler {
|
|||||||
*/
|
*/
|
||||||
async getStatus() {
|
async getStatus() {
|
||||||
const config = await this.getConfig();
|
const config = await this.getConfig();
|
||||||
const jiraConfig = await userPreferencesService.getJiraConfig();
|
const jiraConfig = await userPreferencesService.getJiraConfig('default');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isRunning: this.isRunning,
|
isRunning: this.isRunning,
|
||||||
|
|||||||
@@ -1054,7 +1054,7 @@ class TfsServiceInstance extends TfsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getConfig(): Promise<TfsConfig> {
|
private async getConfig(): Promise<TfsConfig> {
|
||||||
const userConfig = await userPreferencesService.getTfsConfig();
|
const userConfig = await userPreferencesService.getTfsConfig('default');
|
||||||
return userConfig;
|
return userConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user