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

@@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
import { createJiraService, JiraService } from '@/services/integrations/jira/jira';
import { userPreferencesService } from '@/services/core/user-preferences';
import { jiraScheduler } from '@/services/integrations/jira/scheduler';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
/**
* Route POST /api/jira/sync
@@ -10,6 +12,14 @@ import { jiraScheduler } from '@/services/integrations/jira/scheduler';
*/
export async function POST(request: Request) {
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)
const body = await request.json().catch(() => ({}));
const { action, ...params } = body;
@@ -30,6 +40,7 @@ export async function POST(request: Request) {
case 'config':
await userPreferencesService.saveJiraSchedulerConfig(
session.user.id,
params.jiraAutoSync,
params.jiraSyncInterval
);
@@ -50,7 +61,7 @@ export async function POST(request: Request) {
}
// Synchronisation normale (manuelle)
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
let jiraService: JiraService | null = null;
@@ -124,8 +135,16 @@ export async function POST(request: Request) {
*/
export async function GET() {
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
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
let jiraService: JiraService | null = null;

View File

@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
import { createJiraService } from '@/services/integrations/jira/jira';
import { userPreferencesService } from '@/services/core/user-preferences';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
/**
* POST /api/jira/validate-project
@@ -8,6 +10,14 @@ import { userPreferencesService } from '@/services/core/user-preferences';
*/
export async function POST(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Non authentifié' },
{ status: 401 }
);
}
const body = await request.json();
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
const jiraConfig = await userPreferencesService.getJiraConfig();
const jiraConfig = await userPreferencesService.getJiraConfig(session.user.id);
if (!jiraConfig.enabled || !jiraConfig.baseUrl || !jiraConfig.email || !jiraConfig.apiToken) {
return NextResponse.json(

View File

@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
import { userPreferencesService } from '@/services/core/user-preferences';
import { JiraConfig } from '@/lib/types';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
/**
* GET /api/user-preferences/jira-config
@@ -8,7 +10,15 @@ import { JiraConfig } from '@/lib/types';
*/
export async function GET() {
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 });
} catch (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) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Non authentifié' },
{ status: 401 }
);
}
const body = await request.json();
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({
success: true,
@@ -91,6 +109,14 @@ export async function PUT(request: NextRequest) {
*/
export async function DELETE() {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Non authentifié' },
{ status: 401 }
);
}
const defaultConfig: JiraConfig = {
baseUrl: '',
email: '',
@@ -99,7 +125,7 @@ export async function DELETE() {
ignoredProjects: []
};
await userPreferencesService.saveJiraConfig(defaultConfig);
await userPreferencesService.saveJiraConfig(session.user.id, defaultConfig);
return NextResponse.json({
success: true,

View File

@@ -1,12 +1,22 @@
import { NextRequest, NextResponse } from 'next/server';
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
*/
export async function GET() {
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({
success: true,
@@ -29,9 +39,17 @@ export async function GET() {
*/
export async function PUT(request: NextRequest) {
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();
await userPreferencesService.saveAllPreferences(preferences);
await userPreferencesService.saveAllPreferences(session.user.id, preferences);
return NextResponse.json({
success: true,

View File

@@ -1,13 +1,20 @@
import { userPreferencesService } from '@/services/core/user-preferences';
import { getJiraAnalytics } from '@/actions/jira-analytics';
import { JiraDashboardPageClient } from './JiraDashboardPageClient';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
// Force dynamic rendering
export const dynamic = 'force-dynamic';
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
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)
let initialAnalytics = null;

View File

@@ -8,6 +8,8 @@ import { KeyboardShortcutsProvider } from "@/contexts/KeyboardShortcutsContext";
import { userPreferencesService } from "@/services/core/user-preferences";
import { KeyboardShortcuts } from "@/components/KeyboardShortcuts";
import { AuthProvider } from "../components/AuthProvider";
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -29,8 +31,14 @@ export default async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
// Récupérer toutes les préférences côté serveur pour le SSR
const initialPreferences = await userPreferencesService.getAllPreferences();
// Récupérer la session côté serveur pour le SSR
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 (
<html lang="fr">
@@ -39,12 +47,12 @@ export default async function RootLayout({
>
<AuthProvider>
<ThemeProvider
initialTheme={initialPreferences.viewPreferences.theme}
userPreferredTheme={initialPreferences.viewPreferences.theme === 'light' ? 'dark' : initialPreferences.viewPreferences.theme}
initialTheme={initialPreferences?.viewPreferences.theme || 'light'}
userPreferredTheme={initialPreferences?.viewPreferences.theme === 'light' ? 'dark' : initialPreferences?.viewPreferences.theme || 'light'}
>
<KeyboardShortcutsProvider>
<KeyboardShortcuts />
<JiraConfigProvider config={initialPreferences.jiraConfig}>
<JiraConfigProvider config={initialPreferences?.jiraConfig || { enabled: false }}>
<UserPreferencesProvider initialPreferences={initialPreferences}>
{children}
</UserPreferencesProvider>

View File

@@ -1,15 +1,22 @@
import { userPreferencesService } from '@/services/core/user-preferences';
import { IntegrationsSettingsPageClient } from '@/components/settings/IntegrationsSettingsPageClient';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
// Force dynamic rendering for real-time data
export const dynamic = 'force-dynamic';
export default async function IntegrationsSettingsPage() {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return <div>Non authentifié</div>;
}
// Fetch data server-side
// Preferences are now available via context
const [jiraConfig, tfsConfig] = await Promise.all([
userPreferencesService.getJiraConfig(),
userPreferencesService.getTfsConfig()
userPreferencesService.getJiraConfig(session.user.id),
userPreferencesService.getTfsConfig(session.user.id)
]);
return (