import React, { useEffect, useRef, useState } from 'react'; import { useLocation } from 'react-router-dom'; import privacyService, { PublicPrivacyConfig, } from '../../services/api/privacyService'; import { useCookieConsent } from '../../contexts/CookieConsentContext'; declare global { interface Window { dataLayer: any[]; gtag: (...args: any[]) => void; fbq: (...args: any[]) => void; } } const AnalyticsLoader: React.FC = () => { const location = useLocation(); const { consent } = useCookieConsent(); const [config, setConfig] = useState(null); const gaLoadedRef = useRef(false); const fbLoadedRef = useRef(false); // Load public privacy config once useEffect(() => { let mounted = true; const loadConfig = async () => { try { const cfg = await privacyService.getPublicConfig(); if (!mounted) return; setConfig(cfg); } catch { // Fail silently in production; analytics are non-critical } }; void loadConfig(); return () => { mounted = false; }; }, []); // Load Google Analytics when allowed useEffect(() => { if (!config || !consent) return; const measurementId = config.integrations.ga_measurement_id; const analyticsAllowed = config.policy.analytics_enabled && consent.categories.analytics; if (!measurementId || !analyticsAllowed || gaLoadedRef.current) return; // Inject GA script const script = document.createElement('script'); script.async = true; script.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent( measurementId )}`; document.head.appendChild(script); window.dataLayer = window.dataLayer || []; function gtag(...args: any[]) { window.dataLayer.push(args); } window.gtag = gtag; gtag('js', new Date()); gtag('config', measurementId, { anonymize_ip: true }); gaLoadedRef.current = true; return () => { // We don't remove GA script on unmount; typical SPA behaviour is to keep it. }; }, [config, consent]); // Track GA page views on route change useEffect(() => { if (!gaLoadedRef.current || !config?.integrations.ga_measurement_id) return; if (typeof window.gtag !== 'function') return; window.gtag('config', config.integrations.ga_measurement_id, { page_path: location.pathname + location.search, }); }, [location, config]); // Load Meta Pixel when allowed useEffect(() => { if (!config || !consent) return; const pixelId = config.integrations.fb_pixel_id; const marketingAllowed = config.policy.marketing_enabled && consent.categories.marketing; if (!pixelId || !marketingAllowed || fbLoadedRef.current) return; // Meta Pixel base code !(function (f: any, b, e, v, n?, t?, s?) { if (f.fbq) return; n = f.fbq = function () { (n.callMethod ? n.callMethod : n.queue.push).apply(n, arguments); }; if (!f._fbq) f._fbq = n; (n as any).push = n; (n as any).loaded = true; (n as any).version = '2.0'; (n as any).queue = []; t = b.createElement(e); t.async = true; t.src = 'https://connect.facebook.net/en_US/fbevents.js'; s = b.getElementsByTagName(e)[0]; s.parentNode?.insertBefore(t, s); })(window, document, 'script'); window.fbq('init', pixelId); window.fbq('track', 'PageView'); fbLoadedRef.current = true; }, [config, consent]); return null; }; export default AnalyticsLoader;