diff --git a/.env.production b/.env.production
index 0ccf621..17562e9 100644
--- a/.env.production
+++ b/.env.production
@@ -1,28 +1,28 @@
-# Production environment configuration for BTP Xpress Frontend
-# This file is used during the Docker build process for production deployments
-
-# API Backend - Points to the backend via Ingress at api.lions.dev/btpxpress
-NEXT_PUBLIC_API_URL=https://api.lions.dev/btpxpress
-
-# Keycloak Configuration
-NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
-NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
-NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
-
-# Application
-NEXT_PUBLIC_APP_NAME=BTP Xpress
-NEXT_PUBLIC_APP_VERSION=1.0.0
-NEXT_PUBLIC_APP_DESCRIPTION=Plateforme de gestion BTP
-NEXT_PUBLIC_APP_ENV=production
-
-# Theme
-NEXT_PUBLIC_DEFAULT_THEME=blue
-NEXT_PUBLIC_DEFAULT_THEME_MODE=light
-
-# Security
-NEXT_PUBLIC_SESSION_TIMEOUT=1800
-NEXT_PUBLIC_SECURITY_STRICT_MODE=true
-NEXT_PUBLIC_ENABLE_CLIENT_VALIDATION=true
-
-# Debug - disabled in production
-NEXT_PUBLIC_DEBUG=false
+# Production environment configuration for BTP Xpress Frontend
+# This file is used during the Docker build process for production deployments
+
+# API Backend - Points to the backend via Ingress at api.lions.dev/btpxpress
+NEXT_PUBLIC_API_URL=https://api.lions.dev/btpxpress
+
+# Keycloak Configuration
+NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
+NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
+NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
+
+# Application
+NEXT_PUBLIC_APP_NAME=BTP Xpress
+NEXT_PUBLIC_APP_VERSION=1.0.0
+NEXT_PUBLIC_APP_DESCRIPTION=Plateforme de gestion BTP
+NEXT_PUBLIC_APP_ENV=production
+
+# Theme
+NEXT_PUBLIC_DEFAULT_THEME=blue
+NEXT_PUBLIC_DEFAULT_THEME_MODE=light
+
+# Security
+NEXT_PUBLIC_SESSION_TIMEOUT=1800
+NEXT_PUBLIC_SECURITY_STRICT_MODE=true
+NEXT_PUBLIC_ENABLE_CLIENT_VALIDATION=true
+
+# Debug - disabled in production
+NEXT_PUBLIC_DEBUG=false
diff --git a/Dockerfile b/Dockerfile
index 8a4d24a..ed391c5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,68 +1,68 @@
-####
-# This Dockerfile is used to build a production-ready Next.js application
-####
-
-## Stage 1: Dependencies
-FROM node:20-alpine AS deps
-WORKDIR /app
-
-# Install dependencies based on the preferred package manager
-COPY package.json package-lock.json* ./
-RUN npm ci --only=production
-
-## Stage 2: Build
-FROM node:20-alpine AS builder
-WORKDIR /app
-
-COPY package.json package-lock.json* ./
-RUN npm ci
-
-COPY . .
-
-# Build arguments for NEXT_PUBLIC variables (can be overridden at build time)
-ARG NEXT_PUBLIC_API_URL=https://api.lions.dev/btpxpress
-ARG NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
-ARG NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
-ARG NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
-ARG NEXT_PUBLIC_APP_ENV=production
-
-# Convert ARG to ENV for Next.js build process
-ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
-ENV NEXT_PUBLIC_KEYCLOAK_URL=${NEXT_PUBLIC_KEYCLOAK_URL}
-ENV NEXT_PUBLIC_KEYCLOAK_REALM=${NEXT_PUBLIC_KEYCLOAK_REALM}
-ENV NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=${NEXT_PUBLIC_KEYCLOAK_CLIENT_ID}
-ENV NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV}
-
-# Build Next.js application
-ENV NODE_ENV=production
-ENV NEXT_TELEMETRY_DISABLED=1
-RUN npm run build
-
-## Stage 3: Production image
-FROM node:20-alpine AS runner
-WORKDIR /app
-
-ENV NODE_ENV=production
-ENV NEXT_TELEMETRY_DISABLED=1
-
-RUN addgroup --system --gid 1001 nodejs
-RUN adduser --system --uid 1001 nextjs
-
-# Copy necessary files
-COPY --from=builder /app/public ./public
-COPY --from=builder /app/.next/standalone ./
-COPY --from=builder /app/.next/static ./.next/static
-
-USER nextjs
-
-EXPOSE 3000
-
-ENV PORT=3000
-ENV HOSTNAME="0.0.0.0"
-
-# Health check
-HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
- CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
-
-CMD ["node", "server.js"]
-
+####
+# This Dockerfile is used to build a production-ready Next.js application
+####
+
+## Stage 1: Dependencies
+FROM node:20-alpine AS deps
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY package.json package-lock.json* ./
+RUN npm ci --only=production
+
+## Stage 2: Build
+FROM node:20-alpine AS builder
+WORKDIR /app
+
+COPY package.json package-lock.json* ./
+RUN npm ci
+
+COPY . .
+
+# Build arguments for NEXT_PUBLIC variables (can be overridden at build time)
+ARG NEXT_PUBLIC_API_URL=https://api.lions.dev/btpxpress
+ARG NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
+ARG NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
+ARG NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
+ARG NEXT_PUBLIC_APP_ENV=production
+
+# Convert ARG to ENV for Next.js build process
+ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
+ENV NEXT_PUBLIC_KEYCLOAK_URL=${NEXT_PUBLIC_KEYCLOAK_URL}
+ENV NEXT_PUBLIC_KEYCLOAK_REALM=${NEXT_PUBLIC_KEYCLOAK_REALM}
+ENV NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=${NEXT_PUBLIC_KEYCLOAK_CLIENT_ID}
+ENV NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV}
+
+# Build Next.js application
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+RUN npm run build
+
+## Stage 3: Production image
+FROM node:20-alpine AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+# Copy necessary files
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/.next/standalone ./
+COPY --from=builder /app/.next/static ./.next/static
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+ENV HOSTNAME="0.0.0.0"
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
+ CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
+
+CMD ["node", "server.js"]
+
diff --git a/app/(main)/chantiers/[id]/budget/page.tsx b/app/(main)/chantiers/[id]/budget/page.tsx
index cb47353..f5732f6 100644
--- a/app/(main)/chantiers/[id]/budget/page.tsx
+++ b/app/(main)/chantiers/[id]/budget/page.tsx
@@ -9,7 +9,7 @@ import { Column } from 'primereact/column';
import { ProgressBar } from 'primereact/progressbar';
import { Chart } from 'primereact/chart';
import { Tag } from 'primereact/tag';
-import { apiService } from '@/services/api';
+import { budgetService } from '@/services/api';
interface BudgetChantier {
id: number;
@@ -46,7 +46,7 @@ export default function ChantierBudgetPage() {
const loadBudget = async () => {
try {
setLoading(true);
- const data = await apiService.budgets.getByChantier(Number(id));
+ const data = await budgetService.getByChantier(id);
setBudget(data);
} catch (error) {
console.error('Erreur lors du chargement du budget:', error);
diff --git a/app/(main)/chantiers/[id]/page.tsx b/app/(main)/chantiers/[id]/page.tsx
index ab803db..4964025 100644
--- a/app/(main)/chantiers/[id]/page.tsx
+++ b/app/(main)/chantiers/[id]/page.tsx
@@ -9,33 +9,8 @@ import { Tag } from 'primereact/tag';
import { ProgressBar } from 'primereact/progressbar';
import { Divider } from 'primereact/divider';
import { Skeleton } from 'primereact/skeleton';
-import { apiService } from '@/services/api';
-
-interface Chantier {
- id: number;
- nom: string;
- description: string;
- adresse: string;
- ville: string;
- codePostal: string;
- dateDebut: string;
- dateFin: string;
- dateLivraison: string;
- statut: string;
- budget: number;
- client: {
- id: number;
- nom: string;
- email: string;
- telephone: string;
- };
- responsable: {
- id: number;
- nom: string;
- prenom: string;
- };
- progression: number;
-}
+import { chantierService } from '@/services/api';
+import type { Chantier } from '@/types/btp';
export default function ChantierDetailsPage() {
const params = useParams();
@@ -55,7 +30,7 @@ export default function ChantierDetailsPage() {
const loadChantier = async () => {
try {
setLoading(true);
- const data = await apiService.chantiers.getById(Number(id));
+ const data = await chantierService.getById(id);
setChantier(data);
} catch (error) {
console.error('Erreur lors du chargement du chantier:', error);
@@ -191,8 +166,8 @@ export default function ChantierDetailsPage() {
-
- Responsable: {chantier.responsable?.prenom} {chantier.responsable?.nom}
+
+ Type: {chantier.typeChantier || 'N/A'}
@@ -200,7 +175,7 @@ export default function ChantierDetailsPage() {
@@ -210,12 +185,12 @@ export default function ChantierDetailsPage() {
Fin prévue
-
{formatDate(chantier.dateFin)}
+
{formatDate(chantier.dateFinPrevue || '')}
Budget
-
{formatMontant(chantier.budget)}
+
{formatMontant(chantier.montantPrevu || 0)}
@@ -234,7 +209,7 @@ export default function ChantierDetailsPage() {
Durée
- {Math.ceil((new Date(chantier.dateFin).getTime() - new Date(chantier.dateDebut).getTime()) / (1000 * 60 * 60 * 24))} jours
+ {chantier.dateFinPrevue ? Math.ceil((new Date(chantier.dateFinPrevue).getTime() - new Date(chantier.dateDebut).getTime()) / (1000 * 60 * 60 * 24)) : 0} jours
@@ -243,7 +218,7 @@ export default function ChantierDetailsPage() {
Avancement
- {chantier.progression || 0}%
+ N/A
@@ -251,7 +226,7 @@ export default function ChantierDetailsPage() {
Budget
- {formatMontant(chantier.budget)}
+ {formatMontant(chantier.montantPrevu || 0)}
diff --git a/app/(main)/chantiers/[id]/planning/page.tsx b/app/(main)/chantiers/[id]/planning/page.tsx
index 7273d7f..c4c89bf 100644
--- a/app/(main)/chantiers/[id]/planning/page.tsx
+++ b/app/(main)/chantiers/[id]/planning/page.tsx
@@ -11,31 +11,15 @@ import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Chart } from 'primereact/chart';
import { TabView, TabPanel } from 'primereact/tabview';
-import { apiService } from '@/services/api';
-
-interface TacheChantier {
- id: number;
- nom: string;
- description: string;
- dateDebut: string;
- dateFin: string;
- statut: string;
- responsable: {
- nom: string;
- prenom: string;
- };
- equipe: {
- nom: string;
- };
- progression: number;
-}
+import { planningService } from '@/services/api';
+import type { PlanningEvent } from '@/types/btp';
export default function ChantierPlanningPage() {
const params = useParams();
const router = useRouter();
const id = params.id as string;
- const [taches, setTaches] = useState([]);
+ const [taches, setTaches] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedDate, setSelectedDate] = useState(new Date());
const [viewMode, setViewMode] = useState<'timeline' | 'gantt'>('timeline');
@@ -49,7 +33,8 @@ export default function ChantierPlanningPage() {
const loadPlanning = async () => {
try {
setLoading(true);
- const data = await apiService.planning.getByChantier(Number(id));
+ // Récupérer les événements de planning pour ce chantier
+ const data = await planningService.getEvents({ chantierId: id });
setTaches(data || []);
} catch (error) {
console.error('Erreur lors du chargement du planning:', error);
@@ -90,7 +75,7 @@ export default function ChantierPlanningPage() {
return labels[statut] || statut;
};
- const statutBodyTemplate = (rowData: TacheChantier) => {
+ const statutBodyTemplate = (rowData: PlanningEvent) => {
return (
{
+ const dateBodyTemplate = (rowData: PlanningEvent) => {
return (
Début: {formatDate(rowData.dateDebut)}
@@ -108,31 +93,16 @@ export default function ChantierPlanningPage() {
);
};
- const responsableBodyTemplate = (rowData: TacheChantier) => {
- return `${rowData.responsable?.prenom} ${rowData.responsable?.nom}`;
- };
-
- const equipeBodyTemplate = (rowData: TacheChantier) => {
+ const equipeBodyTemplate = (rowData: PlanningEvent) => {
return rowData.equipe?.nom || 'Non assignée';
};
- const progressionBodyTemplate = (rowData: TacheChantier) => {
- return (
-
-
-
{rowData.progression}%
-
- );
+ const prioriteBodyTemplate = (rowData: PlanningEvent) => {
+ const severity = rowData.priorite === 'CRITIQUE' ? 'danger' : rowData.priorite === 'HAUTE' ? 'warning' : rowData.priorite === 'NORMALE' ? 'info' : 'success';
+ return
;
};
- const actionsBodyTemplate = (rowData: TacheChantier) => {
+ const actionsBodyTemplate = (rowData: PlanningEvent) => {
return (
-
Statistiques
-
-
-
- Chantiers
- {client.chantiers?.length || 0}
-
-
-
-
- Factures
- {client.factures?.length || 0}
-
-
-
+
Informations supplémentaires
+
SIRET: {client.siret || 'N/A'}
+
N° TVA: {client.numeroTVA || 'N/A'}
+
Date de création: {new Date(client.dateCreation).toLocaleDateString('fr-FR')}
-
-
-
-
-
-
-
- formatMontant(rowData.budget)}
- sortable
- />
- (
- router.push(`/chantiers/${rowData.id}`)}
- />
- )}
- />
-
-
-
-
-
-
- formatMontant(rowData.montant)}
- sortable
- />
-
-
- (
- router.push(`/factures/${rowData.id}`)}
- />
- )}
- />
-
-
-
-
- Documents du client
-
-
+
+
+ Les chantiers et factures associés à ce client seront affichés ici une fois la relation établie dans le backend.
+
+
+ router.push('/chantiers')} />
+ router.push('/factures')} />
+
diff --git a/app/(main)/materiels/[id]/page.tsx b/app/(main)/materiels/[id]/page.tsx
index b704864..ba7c19e 100644
--- a/app/(main)/materiels/[id]/page.tsx
+++ b/app/(main)/materiels/[id]/page.tsx
@@ -10,39 +10,8 @@ import { Calendar } from 'primereact/calendar';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Timeline } from 'primereact/timeline';
-import { apiService } from '@/services/api';
-
-interface Materiel {
- id: number;
- nom: string;
- reference: string;
- type: string;
- marque: string;
- modele: string;
- statut: string;
- dateAchat: string;
- prixAchat: number;
- tauxJournalier: number;
- disponibilite: string;
- reservations: Reservation[];
- maintenances: Maintenance[];
-}
-
-interface Reservation {
- id: number;
- chantier: string;
- dateDebut: string;
- dateFin: string;
- statut: string;
-}
-
-interface Maintenance {
- id: number;
- type: string;
- date: string;
- description: string;
- cout: number;
-}
+import { materielService } from '@/services/api';
+import type { Materiel } from '@/types/btp';
export default function MaterielDetailsPage() {
const params = useParams();
@@ -61,7 +30,7 @@ export default function MaterielDetailsPage() {
const loadMateriel = async () => {
try {
setLoading(true);
- const data = await apiService.materiels.getById(Number(id));
+ const data = await materielService.getById(id);
setMateriel(data);
} catch (error) {
console.error('Erreur lors du chargement du matériel:', error);
@@ -93,9 +62,6 @@ export default function MaterielDetailsPage() {
}
};
- const statutTemplate = (rowData: Reservation) => {
- return ;
- };
if (loading || !materiel) {
return Chargement...
;
@@ -115,7 +81,7 @@ export default function MaterielDetailsPage() {
/>
{materiel.nom}
-
{materiel.reference}
+
{materiel.numeroSerie || 'N/A'}
@@ -136,11 +102,11 @@ export default function MaterielDetailsPage() {
Informations
Type: {materiel.type}
-
Marque: {materiel.marque}
-
Modèle: {materiel.modele}
-
Date d'achat: {materiel.dateAchat}
-
Prix d'achat: {formatMontant(materiel.prixAchat)}
-
Tarif journalier: {formatMontant(materiel.tauxJournalier)}
+
Marque: {materiel.marque || 'N/A'}
+
Modèle: {materiel.modele || 'N/A'}
+
Date d'achat: {materiel.dateAchat || 'N/A'}
+
Valeur d'achat: {formatMontant(materiel.valeurAchat || 0)}
+
Coût d'utilisation: {formatMontant(materiel.coutUtilisation || 0)}
@@ -152,41 +118,14 @@ export default function MaterielDetailsPage() {
-
-
-
-
-
-
-
-
- (
-
- )}
- />
-
-
-
-
- (
-
- {item.type}
- {item.date}
- {item.description}
- Coût: {formatMontant(item.cout)}
-
- )}
- />
-
-
-
- Documents techniques et certificats
-
-
+
+
+ Les réservations et maintenances de ce matériel seront affichées ici une fois la relation établie dans le backend.
+
+
+ router.push('/planning')} />
+
+
diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts
index a88389a..ea480e5 100644
--- a/app/api/auth/logout/route.ts
+++ b/app/api/auth/logout/route.ts
@@ -9,7 +9,7 @@ const POST_LOGOUT_REDIRECT_URI = process.env.NEXT_PUBLIC_APP_URL || 'https://btp
export async function GET(request: NextRequest) {
console.log('🚪 Logout API called');
- const cookieStore = cookies();
+ const cookieStore = await cookies();
// Récupérer l'id_token avant de supprimer les cookies
const idToken = cookieStore.get('id_token')?.value;
diff --git a/app/api/auth/token/route.ts b/app/api/auth/token/route.ts
index 19ce7ff..e6a8c40 100644
--- a/app/api/auth/token/route.ts
+++ b/app/api/auth/token/route.ts
@@ -72,7 +72,7 @@ export async function POST(request: NextRequest) {
console.log('✅ Tokens received from Keycloak');
// Stocker les tokens dans des cookies HttpOnly sécurisés
- const cookieStore = cookies();
+ const cookieStore = await cookies();
const isProduction = process.env.NODE_ENV === 'production';
// Access token (durée: expires_in secondes)
diff --git a/app/api/auth/userinfo/route.ts b/app/api/auth/userinfo/route.ts
index 0b037ea..f096ebb 100644
--- a/app/api/auth/userinfo/route.ts
+++ b/app/api/auth/userinfo/route.ts
@@ -10,7 +10,7 @@ export async function GET(request: NextRequest) {
console.log('👤 Userinfo API called');
try {
- const cookieStore = cookies();
+ const cookieStore = await cookies();
const accessToken = cookieStore.get('access_token')?.value;
if (!accessToken) {
diff --git a/app/auth/login/page.tsx b/app/auth/login/page.tsx
index b9f3f26..08f8461 100644
--- a/app/auth/login/page.tsx
+++ b/app/auth/login/page.tsx
@@ -1,11 +1,11 @@
'use client';
-import React from 'react';
+import React, { Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { Button } from 'primereact/button';
import { Card } from 'primereact/card';
-export default function LoginPage() {
+function LoginContent() {
const searchParams = useSearchParams();
const returnUrl = searchParams.get('returnUrl') || '/dashboard';
@@ -68,3 +68,15 @@ export default function LoginPage() {
);
}
+
+export default function LoginPage() {
+ return (
+
+
+
+ }>
+
+
+ );
+}
diff --git a/env.example b/env.example
index 01dd2e4..af0c69a 100644
--- a/env.example
+++ b/env.example
@@ -1,16 +1,16 @@
-# Configuration d'environnement pour BTPXpress Frontend
-# Copiez ce fichier vers .env.local et remplissez les valeurs
-
-# API Backend
-NEXT_PUBLIC_API_URL=http://localhost:8080
-NEXT_PUBLIC_API_TIMEOUT=15000
-
-# Keycloak
-NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
-NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
-NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
-
-# Application
-NEXT_PUBLIC_APP_NAME=BTP Xpress
-NEXT_PUBLIC_APP_VERSION=1.0.0
-NEXT_PUBLIC_APP_ENV=development
+# Configuration d'environnement pour BTPXpress Frontend
+# Copiez ce fichier vers .env.local et remplissez les valeurs
+
+# API Backend
+NEXT_PUBLIC_API_URL=http://localhost:8080
+NEXT_PUBLIC_API_TIMEOUT=15000
+
+# Keycloak
+NEXT_PUBLIC_KEYCLOAK_URL=https://security.lions.dev
+NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
+NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend
+
+# Application
+NEXT_PUBLIC_APP_NAME=BTP Xpress
+NEXT_PUBLIC_APP_VERSION=1.0.0
+NEXT_PUBLIC_APP_ENV=development
diff --git a/next.config.js b/next.config.js
index b42d520..4579135 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,124 +1,124 @@
-/** @type {import('next').NextConfig} */
-const nextConfig = {
- reactStrictMode: false, // Disabled to prevent double OAuth code usage in dev
- output: 'standalone',
-
- // Optimisations pour la production
- compress: true,
- poweredByHeader: false,
- generateEtags: false,
-
- // Désactiver la génération statique pour éviter les erreurs useSearchParams
- skipTrailingSlashRedirect: true,
-
- // Configuration des images optimisées
- images: {
- domains: ['btpxpress.lions.dev', 'api.lions.dev', 'security.lions.dev'],
- formats: ['image/webp', 'image/avif'],
- minimumCacheTTL: 60,
- dangerouslyAllowSVG: true,
- contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
- },
-
- // Optimisations de performance
- experimental: {
- optimizeCss: true,
- optimizePackageImports: ['primereact', 'primeicons', 'chart.js', 'axios'],
- },
-
- // Packages externes pour les composants serveur
- serverExternalPackages: ['@prisma/client'],
-
- // Configuration Turbopack (stable)
- turbopack: {
- rules: {
- '*.svg': {
- loaders: ['@svgr/webpack'],
- as: '*.js',
- },
- },
- },
-
- // Configuration du bundler optimisée
- webpack: (config, { dev, isServer }) => {
- // Optimisations pour la production
- if (!dev && !isServer) {
- config.optimization.splitChunks = {
- chunks: 'all',
- cacheGroups: {
- vendor: {
- test: /[\\/]node_modules[\\/]/,
- name: 'vendors',
- chunks: 'all',
- },
- primereact: {
- test: /[\\/]node_modules[\\/]primereact[\\/]/,
- name: 'primereact',
- chunks: 'all',
- },
- charts: {
- test: /[\\/]node_modules[\\/](chart\.js|react-chartjs-2)[\\/]/,
- name: 'charts',
- chunks: 'all',
- },
- },
- };
- }
-
- // Alias pour optimiser les imports
- config.resolve.alias = {
- ...config.resolve.alias,
- '@': require('path').resolve(__dirname),
- '@components': require('path').resolve(__dirname, 'components'),
- '@services': require('path').resolve(__dirname, 'services'),
- '@types': require('path').resolve(__dirname, 'types'),
- '@utils': require('path').resolve(__dirname, 'utils'),
- };
-
- return config;
- },
-
- // Headers de sécurité
- async headers() {
- return [
- {
- source: '/(.*)',
- headers: [
- {
- key: 'X-Frame-Options',
- value: 'DENY'
- },
- {
- key: 'X-Content-Type-Options',
- value: 'nosniff'
- },
- {
- key: 'Referrer-Policy',
- value: 'strict-origin-when-cross-origin'
- }
- ]
- }
- ];
- },
-
- async redirects() {
- return [
- {
- source: '/apps/mail',
- destination: '/apps/mail/inbox',
- permanent: true
- }
- ];
- },
-
- // Configuration des variables d'environnement
- env: {
- NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
- NEXT_PUBLIC_KEYCLOAK_URL: process.env.NEXT_PUBLIC_KEYCLOAK_URL,
- NEXT_PUBLIC_KEYCLOAK_REALM: process.env.NEXT_PUBLIC_KEYCLOAK_REALM,
- NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
- NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV,
- }
-};
-
-module.exports = nextConfig;
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: false, // Disabled to prevent double OAuth code usage in dev
+ output: 'standalone',
+
+ // Optimisations pour la production
+ compress: true,
+ poweredByHeader: false,
+ generateEtags: false,
+
+ // Désactiver la génération statique pour éviter les erreurs useSearchParams
+ skipTrailingSlashRedirect: true,
+
+ // Configuration des images optimisées
+ images: {
+ domains: ['btpxpress.lions.dev', 'api.lions.dev', 'security.lions.dev'],
+ formats: ['image/webp', 'image/avif'],
+ minimumCacheTTL: 60,
+ dangerouslyAllowSVG: true,
+ contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
+ },
+
+ // Optimisations de performance
+ experimental: {
+ optimizeCss: true,
+ optimizePackageImports: ['primereact', 'primeicons', 'chart.js', 'axios'],
+ },
+
+ // Packages externes pour les composants serveur
+ serverExternalPackages: ['@prisma/client'],
+
+ // Configuration Turbopack (stable)
+ turbopack: {
+ rules: {
+ '*.svg': {
+ loaders: ['@svgr/webpack'],
+ as: '*.js',
+ },
+ },
+ },
+
+ // Configuration du bundler optimisée
+ webpack: (config, { dev, isServer }) => {
+ // Optimisations pour la production
+ if (!dev && !isServer) {
+ config.optimization.splitChunks = {
+ chunks: 'all',
+ cacheGroups: {
+ vendor: {
+ test: /[\\/]node_modules[\\/]/,
+ name: 'vendors',
+ chunks: 'all',
+ },
+ primereact: {
+ test: /[\\/]node_modules[\\/]primereact[\\/]/,
+ name: 'primereact',
+ chunks: 'all',
+ },
+ charts: {
+ test: /[\\/]node_modules[\\/](chart\.js|react-chartjs-2)[\\/]/,
+ name: 'charts',
+ chunks: 'all',
+ },
+ },
+ };
+ }
+
+ // Alias pour optimiser les imports
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ '@': require('path').resolve(__dirname),
+ '@components': require('path').resolve(__dirname, 'components'),
+ '@services': require('path').resolve(__dirname, 'services'),
+ '@types': require('path').resolve(__dirname, 'types'),
+ '@utils': require('path').resolve(__dirname, 'utils'),
+ };
+
+ return config;
+ },
+
+ // Headers de sécurité
+ async headers() {
+ return [
+ {
+ source: '/(.*)',
+ headers: [
+ {
+ key: 'X-Frame-Options',
+ value: 'DENY'
+ },
+ {
+ key: 'X-Content-Type-Options',
+ value: 'nosniff'
+ },
+ {
+ key: 'Referrer-Policy',
+ value: 'strict-origin-when-cross-origin'
+ }
+ ]
+ }
+ ];
+ },
+
+ async redirects() {
+ return [
+ {
+ source: '/apps/mail',
+ destination: '/apps/mail/inbox',
+ permanent: true
+ }
+ ];
+ },
+
+ // Configuration des variables d'environnement
+ env: {
+ NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
+ NEXT_PUBLIC_KEYCLOAK_URL: process.env.NEXT_PUBLIC_KEYCLOAK_URL,
+ NEXT_PUBLIC_KEYCLOAK_REALM: process.env.NEXT_PUBLIC_KEYCLOAK_REALM,
+ NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
+ NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV,
+ }
+};
+
+module.exports = nextConfig;
diff --git a/tsconfig.json b/tsconfig.json
index b400f20..33586d2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,41 +1,41 @@
-{
- "compilerOptions": {
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": false,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "incremental": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": [
- "./*"
- ]
- },
- "target": "ES2017"
- },
- "include": [
- "next-env.d.ts",
- ".next/types/**/*.ts",
- "**/*.ts",
- "**/*.tsx"
- ],
- "exclude": [
- "node_modules"
- ]
-}
+{
+ "compilerOptions": {
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "incremental": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ },
+ "target": "ES2017"
+ },
+ "include": [
+ "next-env.d.ts",
+ ".next/types/**/*.ts",
+ "**/*.ts",
+ "**/*.tsx"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}