veza/apps/web/src/main.tsx
2026-01-22 17:23:11 +01:00

279 lines
9.8 KiB
TypeScript

// CRITICAL: React must be imported first before any Zustand/react imports
import React from 'react';
import ReactDOM from 'react-dom/client';
// Global error handlers (debug logs removed)
if (typeof window !== 'undefined') {
window.addEventListener('error', (event) => {
// Error handling without debug logs
}, true);
window.addEventListener('unhandledrejection', (event) => {
// Unhandled rejection handling without debug logs
});
}
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
// Action 4.6.1.5: Import QueryClient singleton setter
import { setQueryClient } from './utils/queryClientSingleton';
// CRITICAL FIX: Import lazy de react-hot-toast pour éviter les collisions de noms de variables après minification
import { LazyToaster } from './components/feedback/LazyToaster';
import { App } from './app/App';
import { logger } from './utils/logger';
import './index.css';
import './styles/design-system.css';
import './styles/global-effects.css';
import './styles/header.css';
// FIX URGENT: Charger en dernier pour override les classes focus: problématiques
import './styles/fix-input-focus.css';
// FIX DÉFINITIF: Styles forcés pour le formulaire de connexion
import './styles/fix-login-form.css';
// Initialize i18next before React renders
import './lib/i18n';
// FIX #20: Initialize Sentry for error tracking
import { initSentry } from './lib/sentry';
// FE-API-019: Initialize MSW for development if enabled
import { env } from './config/env';
// Action 11.2.1.4: Initialize grid overlay utility (dev only)
import { initGridOverlay } from './utils/gridOverlay';
// Fix display issues (dev only) - KEPT for diagnostic tools only
import './utils/fixDisplayIssues';
// Fix input focus (dev only) - Détecte clavier vs souris
import { fixInputFocus } from './utils/fixInputFocus';
// HMR Force Update: 1765126900
// FIX #20: Initialize Sentry before React renders
initSentry();
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
refetchOnWindowFocus: false, // Keep disabled (intentional - avoid unnecessary refetches)
// Edge 6.1: Handle stale data in cache - Ensure stale data refreshed appropriately
staleTime: 1 * 60 * 1000, // Default: Consider data fresh for 1 minute (individual hooks can override)
gcTime: 5 * 60 * 1000, // Default: Keep in cache for 5 minutes (formerly cacheTime)
refetchOnMount: true, // Refetch stale data when component mounts (ensures fresh data)
refetchOnReconnect: true, // Refetch stale data when network reconnects
},
},
});
// Action 4.6.1.5: Set QueryClient singleton for state invalidation
setQueryClient(queryClient);
// FE-API-019: Initialize MSW worker for development
async function enableMocking() {
// FIX: Désactiver MSW par défaut pour éviter les erreurs "module is not defined"
// MSW doit être explicitement activé avec VITE_USE_MSW=1
if (!env.USE_MSW) {
return;
}
if (import.meta.env.DEV) {
try {
const { worker } = await import('./mocks/browser');
// Start the worker
await worker.start({
onUnhandledRequest: 'bypass', // Don't warn about unhandled requests
serviceWorker: {
url: '/mockServiceWorker.js',
options: {
// FIX: Désactiver le service worker MSW si problème de module
// Le service worker peut causer des erreurs "module is not defined"
scope: '/',
},
},
});
// FIX #18: Utiliser logger structuré
const { logger } = await import('./utils/logger');
logger.info('[MSW] Mock Service Worker started', { component: 'MSW' });
} catch (error) {
// FIX: Ignorer les erreurs MSW pour ne pas bloquer l'app
console.warn('[MSW] Failed to start mock service worker:', error);
}
}
}
/**
* Wait for all stylesheets to be loaded before rendering
* This prevents "Layout was forced before the page was fully loaded" warning
* FIX: Version améliorée avec vérification plus robuste
*/
const waitForStylesheets = (): Promise<void> => {
return new Promise((resolve) => {
// Function to check if stylesheets are ready
const checkStylesheets = (): boolean => {
try {
// Check if document is ready
if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
return false;
}
// Check all stylesheets
const stylesheets = Array.from(document.styleSheets);
if (stylesheets.length === 0) {
// No stylesheets yet, wait a bit
return false;
}
// Check if all stylesheets are loaded
let loadedCount = 0;
for (const sheet of stylesheets) {
try {
// Try to access sheet.cssRules to check if it's loaded
// This will throw if the stylesheet is not loaded yet
if (sheet.cssRules !== null || sheet.href === null) {
loadedCount++;
}
} catch (e) {
// Cross-origin stylesheets will throw, but they're usually loaded
// Check if it's a cross-origin error (CORS) or a loading error
if (sheet.href !== null) {
// Has href but can't access rules - likely cross-origin, assume loaded
loadedCount++;
}
}
}
// All stylesheets must be loaded
return loadedCount === stylesheets.length;
} catch (e) {
return false;
}
};
// If already complete and stylesheets are ready, wait a bit more to ensure processing
if (document.readyState === 'complete' && checkStylesheets()) {
// Use multiple requestAnimationFrame calls to ensure stylesheets are processed
// Add extra delay to prevent "Layout was forced" warning
requestAnimationFrame(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
// Additional delay to ensure all CSS is applied and layout is calculated
// This prevents the "Layout was forced before the page was fully loaded" warning
setTimeout(() => {
// One more check to ensure stylesheets are still ready
if (checkStylesheets()) {
resolve();
} else {
// If not ready, wait a bit more
setTimeout(() => resolve(), 100);
}
}, 100); // Increased delay for better reliability
});
});
});
return;
}
// Wait for load event which fires after all resources (including stylesheets) are loaded
if (document.readyState === 'loading') {
window.addEventListener('load', () => {
// Wait a bit more to ensure all stylesheets are processed
let attempts = 0;
const maxAttempts = 20; // Increased attempts
const checkInterval = setInterval(() => {
attempts++;
if (checkStylesheets() || attempts >= maxAttempts) {
clearInterval(checkInterval);
// Use multiple requestAnimationFrame to ensure DOM is ready
requestAnimationFrame(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTimeout(() => {
resolve();
}, 50);
});
});
});
}
}, 50); // Check every 50ms
}, { once: true });
} else {
// Interactive state - check and wait
let attempts = 0;
const maxAttempts = 20; // Increased attempts
const checkInterval = setInterval(() => {
attempts++;
if (checkStylesheets() || attempts >= maxAttempts) {
clearInterval(checkInterval);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTimeout(() => {
resolve();
}, 50);
});
});
});
}
}, 50);
}
});
};
import { ThemeProvider } from './components/theme/ThemeProvider';
const renderApp = () => {
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<BrowserRouter
future={{
v7_startTransition: true,
v7_relativeSplatPath: true,
}}
>
<App />
<LazyToaster position="top-right" />
</BrowserRouter>
</ThemeProvider>
</QueryClientProvider>
</React.StrictMode>,
);
};
// CRITICAL FIX: Précharger react-hot-toast AVANT de rendre l'app
// pour éviter les collisions de noms de variables (ie) lors de la minification
// Le module sera chargé dans un chunk séparé, évitant les conflits
const preloadToast = import('react-hot-toast')
.then((mod) => {
return mod;
})
.catch((err) => {
// Ignorer les erreurs de préchargement
});
// Start MSW and preload toast before rendering the app
Promise.all([enableMocking(), preloadToast])
.then(() => {
// Initialization completed
})
.catch((error) => {
logger.error('[Init] Failed to initialize; continuing', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
})
.then(() => {
// Wait for stylesheets to be loaded before rendering
// This prevents "Layout was forced before the page was fully loaded" warning
return waitForStylesheets();
})
.finally(() => {
// Action 11.2.1.4: Initialize grid overlay utility (dev only)
initGridOverlay();
renderApp();
// Fix input focus après rendu
if (import.meta.env.DEV) {
setTimeout(() => {
fixInputFocus();
}, 500);
}
});