Files
Hotel-Booking/Frontend/src/components/common/AnalyticsLoader.tsx
Iliyan Angelov 48353cde9c update
2025-11-16 20:05:08 +02:00

118 lines
3.5 KiB
TypeScript

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<PublicPrivacyConfig | null>(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;