This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -38,12 +38,51 @@ const AnalyticsLoader: React.FC = () => {
};
}, []);
// Listen for consent updates to disable analytics when consent is withdrawn
useEffect(() => {
const handleConsentUpdate = () => {
if (consent && config) {
const analyticsAllowed =
config.policy.analytics_enabled && consent.categories.analytics;
// SECURITY/COMPLIANCE: Disable Google Analytics when consent is withdrawn
if (gaLoadedRef.current && !analyticsAllowed && window.gtag) {
window.gtag('consent', 'update', {
'analytics_storage': 'denied'
});
}
const marketingAllowed =
config.policy.marketing_enabled && consent.categories.marketing;
// SECURITY/COMPLIANCE: Disable Facebook Pixel when consent is withdrawn
if (fbLoadedRef.current && !marketingAllowed && window.fbq) {
window.fbq('consent', 'revoke');
}
}
};
window.addEventListener('cookie-consent-updated', handleConsentUpdate);
return () => {
window.removeEventListener('cookie-consent-updated', handleConsentUpdate);
};
}, [consent, config]);
// Google Analytics - only load if analytics consent is given
// SECURITY/COMPLIANCE: Respects user consent - only loads if explicitly allowed
useEffect(() => {
if (!config || !consent) return;
const measurementId = config.integrations.ga_measurement_id;
const analyticsAllowed =
config.policy.analytics_enabled && consent.categories.analytics;
// If consent withdrawn, disable analytics
if (gaLoadedRef.current && !analyticsAllowed) {
if (window.gtag) {
window.gtag('consent', 'update', {
'analytics_storage': 'denied'
});
}
return;
}
if (!measurementId || !analyticsAllowed || gaLoadedRef.current) return;
@@ -58,7 +97,15 @@ const AnalyticsLoader: React.FC = () => {
}
window.gtag = gtag;
gtag('js', new Date());
gtag('config', measurementId, { anonymize_ip: true });
// SECURITY/COMPLIANCE: Set consent mode and anonymize IP for privacy
gtag('consent', 'default', {
'analytics_storage': 'granted',
'ad_storage': 'denied', // Don't allow ad storage by default
});
gtag('config', measurementId, {
anonymize_ip: true,
allow_google_signals: false, // Disable Google signals for privacy
});
gaLoadedRef.current = true;
@@ -77,11 +124,22 @@ const AnalyticsLoader: React.FC = () => {
}, [location, config]);
// Facebook Pixel - only load if marketing consent is given
// SECURITY/COMPLIANCE: Respects user consent - only loads if explicitly allowed
useEffect(() => {
if (!config || !consent) return;
const pixelId = config.integrations.fb_pixel_id;
const marketingAllowed =
config.policy.marketing_enabled && consent.categories.marketing;
// If consent withdrawn, disable Facebook Pixel
if (fbLoadedRef.current && !marketingAllowed) {
if (window.fbq) {
window.fbq('consent', 'revoke');
}
return;
}
if (!pixelId || !marketingAllowed || fbLoadedRef.current) return;
@@ -104,6 +162,8 @@ const AnalyticsLoader: React.FC = () => {
}
})(window, document, 'script');
// SECURITY/COMPLIANCE: Set consent before initializing
window.fbq('consent', 'grant');
window.fbq('init', pixelId);
window.fbq('track', 'PageView');
fbLoadedRef.current = true;

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useCookieConsent } from '../contexts/CookieConsentContext';
import { Link } from 'react-router-dom';
const CookieConsentBanner: React.FC = () => {
const { consent, isLoading, hasDecided, updateConsent } = useCookieConsent();
@@ -86,13 +87,21 @@ const CookieConsentBanner: React.FC = () => {
</p>
</div>
<button
type="button"
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#d4af37] underline underline-offset-4 hover:text-[#f6e7b4]"
onClick={() => setShowDetails((prev) => !prev)}
>
{showDetails ? 'Hide detailed preferences' : 'Fine-tune preferences'}
</button>
<div className="flex flex-wrap items-center gap-3">
<button
type="button"
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#d4af37] underline underline-offset-4 hover:text-[#f6e7b4]"
onClick={() => setShowDetails((prev) => !prev)}
>
{showDetails ? 'Hide detailed preferences' : 'Fine-tune preferences'}
</button>
<Link
to="/gdpr"
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#d4af37] underline underline-offset-4 hover:text-[#f6e7b4]"
>
Data Privacy (GDPR)
</Link>
</div>
{showDetails && (
<div className="mt-1.5 space-y-3 rounded-xl bg-black/40 p-3 ring-1 ring-zinc-800/80 backdrop-blur-md sm:p-4">