Files
pragma-stack/frontend/src/app/[locale]/layout.tsx
Felipe Cardoso 487c8a3863 Add demo mode support with MSW integration and documentation
- Integrated Mock Service Worker (MSW) for frontend-only demo mode, allowing API call interception without requiring a backend.
- Added `DemoModeBanner` component to indicate active demo mode and display demo credentials.
- Enhanced configuration with `DEMO_MODE` flag and demo credentials for user and admin access.
- Updated ESLint configuration to exclude MSW-related files from linting and coverage.
- Created comprehensive `DEMO_MODE.md` documentation for setup and usage guidelines, including deployment instructions and troubleshooting.
- Updated package dependencies to include MSW and related libraries.
2025-11-24 18:42:05 +01:00

99 lines
3.0 KiB
TypeScript

import type { Metadata } from 'next';
import { Geist, Geist_Mono } from 'next/font/google';
import { notFound } from 'next/navigation';
import { routing } from '@/lib/i18n/routing';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { generateLocalizedMetadata, type Locale } from '@/lib/i18n/metadata';
import '../globals.css';
import { Providers } from '../providers';
import { AuthProvider } from '@/lib/auth/AuthContext';
import { AuthInitializer } from '@/components/auth';
import { MSWProvider } from '@/components/providers/MSWProvider';
import { DemoModeBanner } from '@/components/demo';
const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],
display: 'swap', // Prevent font from blocking render
preload: true,
});
const geistMono = Geist_Mono({
variable: '--font-geist-mono',
subsets: ['latin'],
display: 'swap', // Prevent font from blocking render
preload: false, // Only preload primary font
});
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
return generateLocalizedMetadata(locale as Locale);
}
export default async function LocaleLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: Promise<{ locale: string }>;
}>) {
// Await params in Next.js 15
const { locale } = await params;
// Ensure that the incoming `locale` is valid
if (!routing.locales.includes(locale as 'en' | 'it')) {
notFound();
}
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale} suppressHydrationWarning>
<head>
{/* Theme initialization script - runs before React hydrates to prevent FOUC */}
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
try {
const theme = localStorage.getItem('theme') || 'system';
let resolved;
if (theme === 'system') {
resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} else {
resolved = theme;
}
document.documentElement.classList.remove('light', 'dark');
document.documentElement.classList.add(resolved);
} catch (e) {
// Silently fail - theme will be set by ThemeProvider
}
})();
`,
}}
/>
</head>
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<NextIntlClientProvider messages={messages}>
<MSWProvider>
<DemoModeBanner />
<AuthProvider>
<AuthInitializer />
<Providers>{children}</Providers>
</AuthProvider>
</MSWProvider>
</NextIntlClientProvider>
</body>
</html>
);
}