Fix Cookie Banner.

This commit is contained in:
2025-04-18 16:16:07 +02:00
parent 867168ae99
commit 7aa0543001
3 changed files with 179 additions and 93 deletions
+21 -3
View File
@@ -9,23 +9,41 @@ import CookieBanner from './components/CookieBanner';
import PageTransition from './components/PageTransition'; import PageTransition from './components/PageTransition';
import { ScrollProvider } from './components/ScrollContext'; import { ScrollProvider } from './components/ScrollContext';
import DebugOverlay from './components/DebugOverlay'; import DebugOverlay from './components/DebugOverlay';
import { logPageView } from './utils/analytics'; import { logPageView, isCookieCategoryEnabled } from './utils/analytics';
import './styles/scrollbar.css'; import './styles/scrollbar.css';
// RouteTracker Component für präzises Seiten-Tracking // RouteTracker Component für präzises Seiten-Tracking - aber nur mit Zustimmung
const RouteTracker = () => { const RouteTracker = () => {
const location = useLocation(); const location = useLocation();
useEffect(() => { useEffect(() => {
// Nur tracken, wenn die Analytics-Cookies akzeptiert wurden
if (isCookieCategoryEnabled('analytics')) {
logPageView(); logPageView();
}
}, [location]); }, [location]);
return null; return null;
}; };
// Funktion zum Blockieren von externen Google Fonts
const blockExternalGoogleFonts = () => {
// Suche nach allen Google Fonts Link-Elementen und entferne sie
const linkElements = document.querySelectorAll('link[href*="fonts.googleapis.com"]');
linkElements.forEach(link => {
link.parentNode?.removeChild(link);
});
};
function App() { function App() {
useEffect(() => { useEffect(() => {
// Benutzerdefiniertes Scrollbar-Styling aktivieren
document.documentElement.classList.add('custom-scrollbar'); document.documentElement.classList.add('custom-scrollbar');
// Blockiere Google Fonts beim ersten Laden, sofern nicht explizit zugestimmt wurde
if (!isCookieCategoryEnabled('functional')) {
blockExternalGoogleFonts();
}
}, []); }, []);
return ( return (
@@ -37,7 +55,7 @@ function App() {
<PageTransition> <PageTransition>
<Layout> <Layout>
<ErrorBoundary> <ErrorBoundary>
<RouteTracker /> {/* Tracking bei Routenwechsel */} <RouteTracker /> {/* Tracking nur mit Zustimmung */}
<AppRoutes /> <AppRoutes />
</ErrorBoundary> </ErrorBoundary>
<CookieBanner /> <CookieBanner />
+73 -46
View File
@@ -1,12 +1,12 @@
import { useEffect, useState } from 'react'; import { useEffect, useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { initGA, logPageView } from '../utils/analytics'; import { initGA, logPageView, initFBPixel, initFunctionalCookies } from '../utils/analytics';
// Feste Übersetzungen direkt in der Komponente // Feste Übersetzungen direkt in der Komponente
const translations = { const translations = {
de: { de: {
title: "Cookie-Einstellungen", title: "Cookie-Einstellungen",
message: "Diese Website verwendet Cookies, um Ihr Erlebnis zu verbessern. Daten werden nicht an Dritte weitergegeben.", message: "Diese Website verwendet Cookies, um Ihr Erlebnis zu verbessern. Daten werden ohne Ihre Zustimmung nicht an Dritte weitergegeben.",
acceptAll: "Alle akzeptieren", acceptAll: "Alle akzeptieren",
save: "Einstellungen speichern", save: "Einstellungen speichern",
decline: "Ablehnen", decline: "Ablehnen",
@@ -23,7 +23,7 @@ const translations = {
}, },
en: { en: {
title: "Cookie Settings", title: "Cookie Settings",
message: "This website uses cookies to enhance your browsing experience. The data will not be shared with third parties.", message: "This website uses cookies to enhance your browsing experience. Data will not be shared with third parties without your consent.",
acceptAll: "Accept All", acceptAll: "Accept All",
save: "Save Settings", save: "Save Settings",
decline: "Decline All", decline: "Decline All",
@@ -40,7 +40,7 @@ const translations = {
}, },
sr: { sr: {
title: "Podešavanja kolačića", title: "Podešavanja kolačića",
message: "Ovaj sajt koristi kolačiće za bolje iskustvo pretraživanja. Podaci neće biti deljeni sa trećim licima.", message: "Ovaj sajt koristi kolačiće za bolje iskustvo pretraživanja. Podaci neće biti deljeni sa trećim licima bez vaše saglasnosti.",
acceptAll: "Prihvati sve", acceptAll: "Prihvati sve",
save: "Sačuvaj podešavanja", save: "Sačuvaj podešavanja",
decline: "Odbij sve", decline: "Odbij sve",
@@ -76,6 +76,11 @@ const cookieServices = {
name: 'Spracheinstellungen', name: 'Spracheinstellungen',
provider: 'Eigentümer der Website', provider: 'Eigentümer der Website',
purpose: 'Speichert Ihre bevorzugte Sprache' purpose: 'Speichert Ihre bevorzugte Sprache'
},
{
name: 'Google Fonts',
provider: 'Google LLC',
purpose: 'Anzeige von Webschriften'
} }
] ]
}, },
@@ -113,6 +118,31 @@ interface CookieSettings {
functional: boolean; functional: boolean;
} }
// Entfernt externe Google Fonts, falls keine Zustimmung vorliegt
const removeExternalGoogleFonts = () => {
// Suche nach allen Google Fonts Link-Elementen und entferne sie
const linkElements = document.querySelectorAll('link[href*="fonts.googleapis.com"]');
linkElements.forEach(link => {
link.parentNode?.removeChild(link);
});
// Suche nach allen Google Fonts Script-Elementen und entferne sie
const scriptElements = document.querySelectorAll('script[src*="fonts.googleapis.com"]');
scriptElements.forEach(script => {
script.parentNode?.removeChild(script);
});
};
// Funktion zum Blockieren von Google Analytics falls noch keine Zustimmung vorliegt
const blockGoogleAnalytics = () => {
// Entferne alle bestehenden GA Cookies
document.cookie.split(';').forEach(function(c) {
if (c.trim().indexOf('_ga') === 0) {
document.cookie = c.trim().split('=')[0] + '=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;';
}
});
};
const CookieBanner = () => { const CookieBanner = () => {
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const [currentLang, setCurrentLang] = useState<'de' | 'en' | 'sr'>('de'); const [currentLang, setCurrentLang] = useState<'de' | 'en' | 'sr'>('de');
@@ -124,6 +154,32 @@ const CookieBanner = () => {
functional: false functional: false
}); });
// Initialisiere Dienste basierend auf den Cookie-Einstellungen
const initializeServices = useCallback((settings: CookieSettings) => {
// Blockieren von Diensten, wenn nicht zugestimmt wurde
if (!settings.functional) {
removeExternalGoogleFonts();
}
if (!settings.analytics) {
blockGoogleAnalytics();
} else {
// Initialisiere Google Analytics nur wenn ausdrücklich zugestimmt wurde
initGA();
logPageView();
}
// Marketing-Dienste nur mit Zustimmung
if (settings.marketing) {
initFBPixel();
}
// Funktionale Dienste nur mit Zustimmung
if (settings.functional) {
initFunctionalCookies();
}
}, []);
// Aktuelle Sprache erkennen // Aktuelle Sprache erkennen
useEffect(() => { useEffect(() => {
const lang = i18n.language.substring(0, 2); const lang = i18n.language.substring(0, 2);
@@ -139,6 +195,13 @@ const CookieBanner = () => {
return translations[currentLang][key]; return translations[currentLang][key];
}; };
// DSGVO: Unmittelbar beim ersten Laden blockiere alle nicht-essentiellen Dienste
useEffect(() => {
// Blockiere externe Dienste bis Zustimmung vorliegt
removeExternalGoogleFonts();
blockGoogleAnalytics();
}, []);
// Prüfe beim Laden, ob bereits Zustimmung vorhanden ist // Prüfe beim Laden, ob bereits Zustimmung vorhanden ist
useEffect(() => { useEffect(() => {
try { try {
@@ -153,50 +216,11 @@ const CookieBanner = () => {
} catch (error) { } catch (error) {
console.error('Error loading cookie settings:', error); console.error('Error loading cookie settings:', error);
} }
}, []); }, [initializeServices]);
// Initialisiere Dienste basierend auf den Cookie-Einstellungen
const initializeServices = (settings: CookieSettings) => {
// Initialisiere Google Analytics, wenn Analytics-Cookies akzeptiert wurden
if (settings.analytics) {
initGA();
logPageView();
}
// Hier könnten weitere Dienste initialisiert werden
if (settings.marketing) {
// Marketing-Dienste initialisieren (z.B. Facebook Pixel)
initializeMarketingServices();
}
if (settings.functional) {
// Funktionale Dienste initialisieren
initializeFunctionalServices();
}
};
// Initialisiere Marketing-Dienste
const initializeMarketingServices = () => {
// Facebook Pixel (Beispiel)
try {
if (typeof window !== 'undefined' && !window.fbq) {
// Facebook Pixel Code hier einfügen
console.log('Facebook Pixel würde jetzt initialisiert');
}
} catch (e) {
console.error('Error initializing marketing services:', e);
}
};
// Initialisiere funktionale Dienste
const initializeFunctionalServices = () => {
// Beispiel für funktionale Dienste
console.log('Funktionale Dienste würden jetzt initialisiert');
};
const handleSaveSettings = () => { const handleSaveSettings = () => {
localStorage.setItem('cookieSettings', JSON.stringify(cookieSettings)); localStorage.setItem('cookieSettings', JSON.stringify(cookieSettings));
localStorage.setItem('cookieConsent', 'true'); localStorage.setItem('cookieConsent', cookieSettings.analytics || cookieSettings.marketing || cookieSettings.functional ? 'true' : 'false');
// Initialisiere Dienste basierend auf den ausgewählten Einstellungen // Initialisiere Dienste basierend auf den ausgewählten Einstellungen
initializeServices(cookieSettings); initializeServices(cookieSettings);
@@ -234,6 +258,9 @@ const CookieBanner = () => {
localStorage.setItem('cookieConsent', 'false'); localStorage.setItem('cookieConsent', 'false');
setCookieSettings(minimalSettings); setCookieSettings(minimalSettings);
// Block all non-essential services
initializeServices(minimalSettings);
setShowSettings(false); setShowSettings(false);
}; };
+76 -35
View File
@@ -1,12 +1,10 @@
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
const TRACKING_ID = 'G-N0PZBL7X18'; const TRACKING_ID = 'G-N0PZBL7X18';
const FB_PIXEL_ID = 'XXXXXXXXXXXXXXXXX'; // Ersetze mit deiner Facebook Pixel ID
// Facebook Pixel ID - ersetze dies durch deine echte Pixel-ID
const FB_PIXEL_ID = 'XXXXXXXXXXXXXXXXX';
// Prüft, ob eine bestimmte Cookie-Kategorie aktiviert ist // Prüft, ob eine bestimmte Cookie-Kategorie aktiviert ist
const isCookieCategoryEnabled = (category: string): boolean => { export const isCookieCategoryEnabled = (category: string): boolean => {
try { try {
const cookieSettings = localStorage.getItem('cookieSettings'); const cookieSettings = localStorage.getItem('cookieSettings');
if (!cookieSettings) return false; if (!cookieSettings) return false;
@@ -19,10 +17,28 @@ const isCookieCategoryEnabled = (category: string): boolean => {
} }
}; };
// Google Analytics Initialisierung // Entfernt alle Google Analytics Cookies
export const initGA = () => { const removeGACookies = (): void => {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
const cookieName = cookie.split('=')[0];
// Entferne alle GA-Cookies
if (cookieName.startsWith('_ga') ||
cookieName.startsWith('_gid') ||
cookieName.startsWith('_gat')) {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
}
};
// Google Analytics Initialisierung - mit strikter Zustimmungsprüfung
export const initGA = (): void => {
if (!isCookieCategoryEnabled('analytics')) { if (!isCookieCategoryEnabled('analytics')) {
return; // Keine Analytics-Initialisierung ohne Zustimmung // Entferne bestehende GA-Cookies, falls keine Zustimmung
removeGACookies();
return;
} }
ReactGA.initialize(TRACKING_ID, { ReactGA.initialize(TRACKING_ID, {
@@ -33,9 +49,9 @@ export const initGA = () => {
}); });
}; };
export const logPageView = () => { export const logPageView = (): void => {
if (!isCookieCategoryEnabled('analytics')) { if (!isCookieCategoryEnabled('analytics')) {
return; // Kein Tracking ohne Zustimmung return;
} }
ReactGA.send({ ReactGA.send({
@@ -45,9 +61,9 @@ export const logPageView = () => {
}); });
}; };
export const trackEvent = (category: string, action: string, label?: string) => { export const trackEvent = (category: string, action: string, label?: string): void => {
if (!isCookieCategoryEnabled('analytics')) { if (!isCookieCategoryEnabled('analytics')) {
return; // Kein Tracking ohne Zustimmung return;
} }
ReactGA.event({ ReactGA.event({
@@ -57,9 +73,9 @@ export const trackEvent = (category: string, action: string, label?: string) =>
}); });
}; };
export const trackConversion = (value?: number) => { export const trackConversion = (value?: number): void => {
if (!isCookieCategoryEnabled('analytics')) { if (!isCookieCategoryEnabled('analytics')) {
return; // Kein Tracking ohne Zustimmung return;
} }
ReactGA.event({ ReactGA.event({
@@ -69,9 +85,9 @@ export const trackConversion = (value?: number) => {
}); });
}; };
export const trackTiming = (category: string, variable: string, value: number) => { export const trackTiming = (category: string, variable: string, value: number): void => {
if (!isCookieCategoryEnabled('analytics')) { if (!isCookieCategoryEnabled('analytics')) {
return; // Kein Tracking ohne Zustimmung return;
} }
ReactGA.event({ ReactGA.event({
@@ -82,19 +98,17 @@ export const trackTiming = (category: string, variable: string, value: number) =
}); });
}; };
// Facebook Pixel Initialisierung // Facebook Pixel Initialisierung - nur mit Zustimmung
export const initFBPixel = () => { export const initFBPixel = (): void => {
if (!isCookieCategoryEnabled('marketing')) { if (!isCookieCategoryEnabled('marketing')) {
return; // Keine Marketing-Cookie-Initialisierung ohne Zustimmung return;
} }
if (typeof window !== 'undefined' && !window.fbq) { if (typeof window !== 'undefined' && !window.fbq) {
// @ts-ignore - fbq wird dynamisch hinzugefügt // fbq ist nicht Teil des Window-Interfaces und wird zur Laufzeit hinzugefügt
window.fbq = function() { window.fbq = function(...args: unknown[]): void {
// @ts-ignore
window._fbq = window._fbq || []; window._fbq = window._fbq || [];
// @ts-ignore window._fbq.push(args);
window._fbq.push(arguments);
}; };
// Facebook Pixel Script laden // Facebook Pixel Script laden
@@ -109,7 +123,7 @@ export const initFBPixel = () => {
}; };
// Facebook Event tracking // Facebook Event tracking
export const trackFBEvent = (event: string, params?: any) => { export const trackFBEvent = (event: string, params?: Record<string, unknown>): void => {
if (!isCookieCategoryEnabled('marketing') || typeof window === 'undefined' || !window.fbq) { if (!isCookieCategoryEnabled('marketing') || typeof window === 'undefined' || !window.fbq) {
return; return;
} }
@@ -117,32 +131,58 @@ export const trackFBEvent = (event: string, params?: any) => {
window.fbq('track', event, params); window.fbq('track', event, params);
}; };
// Funktionale Cookies initialisieren (Beispiel) // Google Fonts Handling - nur mit funktionaler Cookie-Zustimmung
export const initFunctionalCookies = () => { export const initGoogleFonts = (): void => {
if (!isCookieCategoryEnabled('functional')) {
// Entferne bestehende Google Fonts
const linkElements = document.querySelectorAll('link[href*="fonts.googleapis.com"]');
linkElements.forEach(link => {
link.parentNode?.removeChild(link);
});
return;
}
// Vermeide Duplikate beim Laden
if (document.querySelector('link[href*="fonts.googleapis.com"]')) {
return;
}
// Lokales Laden von Google Fonts als Alternative
// Code hier zur lokalen Einbindung der Schriftarten
};
// Funktionale Cookies initialisieren
export const initFunctionalCookies = (): void => {
if (!isCookieCategoryEnabled('functional')) { if (!isCookieCategoryEnabled('functional')) {
return; return;
} }
// Hier könnten Funktionen für funktionale Cookies implementiert werden // Initialisiere Google Fonts wenn zugestimmt wurde
console.log('Funktionale Cookies initialisiert'); initGoogleFonts();
// Weitere funktionale Dienste könnten hier initialisiert werden
}; };
// Typdefinitionen für globale Objekte // Typdefinitionen für globale Objekte
declare global { declare global {
interface Window { interface Window {
initializeAnalytics: () => void; initializeAnalytics: () => void;
fbq: (...args: any[]) => void; fbq: (...args: unknown[]) => void;
_fbq: any[]; _fbq: unknown[];
} }
} }
// Analytics-Funktionen global verfügbar machen (für den Cookie-Banner) // Analytics-Funktionen global verfügbar machen (für den Cookie-Banner)
window.initializeAnalytics = () => { if (typeof window !== 'undefined') {
window.initializeAnalytics = (): void => {
// Prüfe die einzelnen Cookie-Kategorien und initialisiere entsprechend
if (isCookieCategoryEnabled('analytics')) {
initGA(); initGA();
logPageView(); logPageView();
} else {
removeGACookies();
}
// Wenn die entsprechenden Cookie-Kategorien aktiviert sind,
// initialisiere auch andere Dienste
if (isCookieCategoryEnabled('marketing')) { if (isCookieCategoryEnabled('marketing')) {
initFBPixel(); initFBPixel();
} }
@@ -150,7 +190,8 @@ window.initializeAnalytics = () => {
if (isCookieCategoryEnabled('functional')) { if (isCookieCategoryEnabled('functional')) {
initFunctionalCookies(); initFunctionalCookies();
} }
}; };
}
// Export der Namespace-Erweiterung // Export für TypeScript
export {}; export {};