import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import './styles/global-enhancements.css'; import './styles/admin-enhancements.css'; import './styles/home-style-pack.css'; import './styles/sparta-styles.css'; // Quill editor styles (MUST be imported globally) - CRITICAL for rich text editor import 'quill/dist/quill.snow.css'; import 'react-image-crop/dist/ReactCrop.css'; // Custom editor styles AFTER quill base styles to ensure proper override import './styles/custom-editor.css'; import { theme } from './App'; import AppLazy from './App.lazy'; import { ColorModeScript } from '@chakra-ui/react'; import { HelmetProvider } from 'react-helmet-async'; import reportWebVitals from './reportWebVitals'; import * as serviceWorkerRegistration from './serviceWorkerRegistration'; import { promptUserToUpdate } from './serviceWorkerRegistration'; import { installGlobalErrorHandlers, reportError } from './services/errorReporter'; // Cookie consent utilities type Consent = { analytics?: boolean }; const getConsent = (): Consent | null => { try { const raw = localStorage.getItem('cookie_consent'); return raw ? JSON.parse(raw) : null; } catch { return null; } }; const onConsentChange = (cb: (c: Consent) => void) => { window.addEventListener('cookie-consent-change', (e: Event) => { const detail = (e as CustomEvent).detail as Consent; cb(detail || {}); }); }; // Example analytics initializer (placeholder) const initAnalytics = () => { // Place your analytics loader here (e.g., GA/Matomo), gated by consent.analytics // This function will be called only when analytics consent is granted. }; // Error Boundary component class ErrorBoundary extends React.Component<{children: React.ReactNode}, {hasError: boolean}> { constructor(props: {children: React.ReactNode}) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { reportError({ message: error.message, stack: error.stack, component: 'ErrorBoundary', context: { react: errorInfo.componentStack } }); console.error('Error caught by ErrorBoundary:', error, errorInfo); } render() { if (this.state.hasError) { return (

Something went wrong.

Please refresh the page or try again later.

); } return this.props.children; } } installGlobalErrorHandlers(); window.addEventListener('unhandledrejection', (event) => { const reason: any = (event as any).reason; const message = typeof reason === 'string' ? reason : (reason?.message || 'Unhandled rejection'); const stack = typeof reason === 'object' ? (reason?.stack || '') : ''; reportError({ message, stack }); }); const rootElement = document.getElementById('root'); if (!rootElement) { // Failed to find the root element } else { try { // Initialize CSS variables immediately to prevent flash of fallback colors const r = document.documentElement; // Try to load cached colors from localStorage for instant display let cachedColors: any = null; try { const cached = localStorage.getItem('club_theme_cache'); if (cached) { cachedColors = JSON.parse(cached); // Use cache if it's less than 24 hours old const age = Date.now() - (cachedColors.timestamp || 0); if (age > 24 * 60 * 60 * 1000) { cachedColors = null; // Cache expired } } } catch (e) { // Ignore localStorage errors } // Set CSS variables - use cached values if available, otherwise defaults r.style.setProperty('--club-primary', cachedColors?.primary || '#0b5cff'); r.style.setProperty('--club-secondary', cachedColors?.secondary || '#ffd200'); r.style.setProperty('--club-accent', cachedColors?.accent || '#141414'); r.style.setProperty('--club-text-on-primary', cachedColors?.textOnPrimary || '#ffffff'); if (cachedColors?.textOnSecondary) r.style.setProperty('--club-text-on-secondary', cachedColors.textOnSecondary); if (cachedColors?.textOnAccent) r.style.setProperty('--club-text-on-accent', cachedColors.textOnAccent); r.style.setProperty('--club-bg-light', cachedColors?.background || '#ffffff'); r.style.setProperty('--club-text-light', cachedColors?.text || '#000000'); // Load fonts const link = document.createElement('link'); link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@600;700;800&display=swap'; link.rel = 'stylesheet'; document.head.appendChild(link); const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( {/* Ensure color mode (light/dark) persists and matches Chakra config before UI renders */} ); // App rendered // Initialize analytics if consent allows const current = getConsent(); if (current?.analytics) { initAnalytics(); } // React to future consent changes onConsentChange((c) => { if (c?.analytics) { initAnalytics(); } }); } catch (error) { // Optionally report to monitoring service // console.error('Error rendering application:', error); rootElement.innerHTML = `

Application Error

${String(error)}

Please check the console for more details and refresh the page.

`; } } // Report web vitals (disabled logging by default). Hook up your analytics here if needed. reportWebVitals(); // Enable PWA service worker with user prompt on updates serviceWorkerRegistration.register({ onUpdate: promptUserToUpdate });