mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import './styles/global-enhancements.css';
|
||||
import './styles/admin-enhancements.css';
|
||||
import App, { theme } from './App';
|
||||
import { ColorModeScript } from '@chakra-ui/react';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
// 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) {
|
||||
console.error('Error caught by ErrorBoundary:', error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
||||
<h2>Something went wrong.</h2>
|
||||
<p>Please refresh the page or try again later.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
// Log unhandled promise rejections
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
// Optionally report to monitoring service here
|
||||
});
|
||||
|
||||
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(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<HelmetProvider>
|
||||
{/* Ensure color mode (light/dark) persists and matches Chakra config before UI renders */}
|
||||
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||
<App />
|
||||
</HelmetProvider>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
);
|
||||
// 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 = `
|
||||
<div style="padding: 20px; font-family: Arial, sans-serif; color: #721c24; background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
|
||||
<h2>Application Error</h2>
|
||||
<p>${String(error)}</p>
|
||||
<p>Please check the console for more details and refresh the page.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Report web vitals (disabled logging by default). Hook up your analytics here if needed.
|
||||
reportWebVitals();
|
||||
Reference in New Issue
Block a user