2026-01-15 18:26:53 +00:00
|
|
|
// CRITICAL: React must be imported first before any Zustand/react imports
|
2025-12-08 18:57:54 +00:00
|
|
|
import React from 'react';
|
|
|
|
|
import ReactDOM from 'react-dom/client';
|
2026-01-15 18:26:53 +00:00
|
|
|
|
2026-01-18 12:55:28 +00:00
|
|
|
// Global error handlers (debug logs removed)
|
2026-01-15 18:26:53 +00:00
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
window.addEventListener('error', (event) => {
|
2026-01-18 12:55:28 +00:00
|
|
|
// Error handling without debug logs
|
2026-01-15 18:26:53 +00:00
|
|
|
}, true);
|
|
|
|
|
|
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
2026-01-18 12:55:28 +00:00
|
|
|
// Unhandled rejection handling without debug logs
|
2026-01-15 18:26:53 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 18:57:54 +00:00
|
|
|
import { BrowserRouter } from 'react-router-dom';
|
|
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
state-utilities: update stateInvalidation to work with React Query
- Created QueryClient singleton (queryClientSingleton.ts):
- Provides global access to QueryClient for state invalidation
- Set in main.tsx after QueryClient creation
- Updated invalidateQueries() to use QueryClient directly:
- Replaced custom event system with direct QueryClient.invalidateQueries()
- Added query key mapping for all resource types
- Event system kept as fallback if QueryClient not available
- Updated invalidateStore() for Library Store:
- Removed clearItems() call (method doesn't exist, domain data migrated to React Query)
- Library Store now only contains UI state (filters)
- React Query cache invalidation handles refetching
- Query keys mapped:
- tracks: ['tracks'], ['track'], ['library']
- playlists: ['playlists'], ['playlist']
- users: ['users'], ['user'], ['auth'], ['userProfile']
- conversations: ['conversations'], ['conversation'], ['chat'], ['chatConversations']
- roles: ['roles'], ['role']
- library: ['library'], ['tracks'], ['favorites'], ['libraryItems']
- auth: ['auth'], ['user']
- Action 4.6.1.5 complete
2026-01-15 18:38:47 +00:00
|
|
|
// Action 4.6.1.5: Import QueryClient singleton setter
|
|
|
|
|
import { setQueryClient } from './utils/queryClientSingleton';
|
2026-01-15 18:26:53 +00:00
|
|
|
// 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';
|
2025-12-08 18:57:54 +00:00
|
|
|
import { App } from './app/App';
|
2026-01-16 11:15:20 +00:00
|
|
|
import { logger } from './utils/logger';
|
2025-12-08 18:57:54 +00:00
|
|
|
import './index.css';
|
feat: add fusion design system foundation
- Created design-system.css with tokens from KŌDŌ, BOTANICAL, and Spectre Astral
- Added color palette (cyber neons + nature tones + space gradients)
- Implemented typography system (Orbitron, Rajdhani, Inter, Source Serif, JetBrains Mono)
- Added spacing scale, border radius, shadows, and glows
- Created keyframe animations (logo-spin, graffiti-shake, gentle-pulse, etc.)
- Added utility classes for gradients, glows, clip-paths, and hover effects
- Imported Google Fonts in index.html
- Integrated design system CSS in main.tsx
2026-01-03 22:35:02 +00:00
|
|
|
import './styles/design-system.css';
|
feat: add layout, navigation, and utility components
Phase 3: Layout & Navigation
- Created header.css with animated hexagonal logo
- Gradient navigation links with hover effects
- Responsive mobile menu with toggle animation
- Full mobile/tablet support
Global Effects:
- Created global-effects.css with texture overlay (graffiti wall)
- Scanline effect (gaming CRT)
- Custom scrollbar with gradient
- Focus styles and selection colors
- Page layout, container, section utilities
- Grid system (2, 3, 4 columns + auto-fit)
- Flex utilities and spacing helpers
Additional Components:
- Created Badge component (8 variants: default, cyber, neon, nature, gaming, success, warning, error)
- Created Tag component with closable option
- Created Avatar component (4 variants, 6 sizes, status indicators)
- Created AvatarGroup for stacked avatars
- All components fully typed with TypeScript
Imported all new CSS files in main.tsx
2026-01-03 23:22:06 +00:00
|
|
|
import './styles/global-effects.css';
|
|
|
|
|
import './styles/header.css';
|
2026-01-18 12:55:28 +00:00
|
|
|
// 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';
|
2025-12-17 13:07:35 +00:00
|
|
|
// Initialize i18next before React renders
|
|
|
|
|
import './lib/i18n';
|
2025-12-27 00:58:49 +00:00
|
|
|
// FIX #20: Initialize Sentry for error tracking
|
|
|
|
|
import { initSentry } from './lib/sentry';
|
2025-12-25 12:37:10 +00:00
|
|
|
// FE-API-019: Initialize MSW for development if enabled
|
|
|
|
|
import { env } from './config/env';
|
2026-01-16 10:53:29 +00:00
|
|
|
// Action 11.2.1.4: Initialize grid overlay utility (dev only)
|
|
|
|
|
import { initGridOverlay } from './utils/gridOverlay';
|
2026-01-18 12:55:28 +00:00
|
|
|
// 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';
|
2025-12-03 21:56:50 +00:00
|
|
|
|
2025-12-08 18:57:54 +00:00
|
|
|
// HMR Force Update: 1765126900
|
2025-12-03 21:56:50 +00:00
|
|
|
|
2025-12-27 00:58:49 +00:00
|
|
|
// FIX #20: Initialize Sentry before React renders
|
|
|
|
|
initSentry();
|
|
|
|
|
|
2025-12-08 18:57:54 +00:00
|
|
|
const queryClient = new QueryClient({
|
|
|
|
|
defaultOptions: {
|
|
|
|
|
queries: {
|
|
|
|
|
retry: false,
|
2026-01-16 13:35:42 +00:00
|
|
|
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
|
2025-12-08 18:57:54 +00:00
|
|
|
},
|
|
|
|
|
},
|
2025-12-03 21:56:50 +00:00
|
|
|
});
|
2025-12-08 18:57:54 +00:00
|
|
|
|
state-utilities: update stateInvalidation to work with React Query
- Created QueryClient singleton (queryClientSingleton.ts):
- Provides global access to QueryClient for state invalidation
- Set in main.tsx after QueryClient creation
- Updated invalidateQueries() to use QueryClient directly:
- Replaced custom event system with direct QueryClient.invalidateQueries()
- Added query key mapping for all resource types
- Event system kept as fallback if QueryClient not available
- Updated invalidateStore() for Library Store:
- Removed clearItems() call (method doesn't exist, domain data migrated to React Query)
- Library Store now only contains UI state (filters)
- React Query cache invalidation handles refetching
- Query keys mapped:
- tracks: ['tracks'], ['track'], ['library']
- playlists: ['playlists'], ['playlist']
- users: ['users'], ['user'], ['auth'], ['userProfile']
- conversations: ['conversations'], ['conversation'], ['chat'], ['chatConversations']
- roles: ['roles'], ['role']
- library: ['library'], ['tracks'], ['favorites'], ['libraryItems']
- auth: ['auth'], ['user']
- Action 4.6.1.5 complete
2026-01-15 18:38:47 +00:00
|
|
|
// Action 4.6.1.5: Set QueryClient singleton for state invalidation
|
|
|
|
|
setQueryClient(queryClient);
|
|
|
|
|
|
2025-12-25 12:37:10 +00:00
|
|
|
// FE-API-019: Initialize MSW worker for development
|
|
|
|
|
async function enableMocking() {
|
2026-01-18 12:55:28 +00:00
|
|
|
// 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
|
2025-12-25 12:37:10 +00:00
|
|
|
if (!env.USE_MSW) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (import.meta.env.DEV) {
|
2026-01-18 12:55:28 +00:00
|
|
|
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: '/',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-12-25 12:37:10 +00:00
|
|
|
|
2026-01-18 12:55:28 +00:00
|
|
|
// 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);
|
|
|
|
|
}
|
2025-12-25 12:37:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 12:55:28 +00:00
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-22 16:23:11 +00:00
|
|
|
import { ThemeProvider } from './components/theme/ThemeProvider';
|
|
|
|
|
|
2026-01-15 18:26:53 +00:00
|
|
|
const renderApp = () => {
|
2025-12-25 12:37:10 +00:00
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
|
|
<React.StrictMode>
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
2026-01-22 16:23:11 +00:00
|
|
|
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
|
|
|
|
<BrowserRouter
|
|
|
|
|
future={{
|
|
|
|
|
v7_startTransition: true,
|
|
|
|
|
v7_relativeSplatPath: true,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<App />
|
|
|
|
|
<LazyToaster position="top-right" />
|
|
|
|
|
</BrowserRouter>
|
|
|
|
|
</ThemeProvider>
|
2025-12-25 12:37:10 +00:00
|
|
|
</QueryClientProvider>
|
|
|
|
|
</React.StrictMode>,
|
|
|
|
|
);
|
2026-01-15 18:26:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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])
|
2026-01-18 12:55:28 +00:00
|
|
|
.then(() => {
|
|
|
|
|
// Initialization completed
|
|
|
|
|
})
|
2026-01-15 18:26:53 +00:00
|
|
|
.catch((error) => {
|
2026-01-16 11:15:20 +00:00
|
|
|
logger.error('[Init] Failed to initialize; continuing', {
|
|
|
|
|
error: error instanceof Error ? error.message : String(error),
|
|
|
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
|
|
|
});
|
2026-01-15 18:26:53 +00:00
|
|
|
})
|
2026-01-18 12:55:28 +00:00
|
|
|
.then(() => {
|
|
|
|
|
// Wait for stylesheets to be loaded before rendering
|
|
|
|
|
// This prevents "Layout was forced before the page was fully loaded" warning
|
|
|
|
|
return waitForStylesheets();
|
|
|
|
|
})
|
2026-01-15 18:26:53 +00:00
|
|
|
.finally(() => {
|
2026-01-16 10:53:29 +00:00
|
|
|
// Action 11.2.1.4: Initialize grid overlay utility (dev only)
|
|
|
|
|
initGridOverlay();
|
2026-01-15 18:26:53 +00:00
|
|
|
renderApp();
|
2026-01-18 12:55:28 +00:00
|
|
|
// Fix input focus après rendu
|
|
|
|
|
if (import.meta.env.DEV) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
fixInputFocus();
|
|
|
|
|
}, 500);
|
|
|
|
|
}
|
2026-01-15 18:26:53 +00:00
|
|
|
});
|