// ───────────────────────────────────────────────────────────────────────── // cookies.jsx — Cookie consent banner (RODO/ePrivacy) + Google Consent Mode v2 // Wymóg prawny EU. Blokuje GA/AdSense PRZED zgodą. // ───────────────────────────────────────────────────────────────────────── const { useState: useStateC2, useEffect: useEffectC2 } = React; const CONSENT_LS_KEY = "kr_cookie_consent_v1"; const CONSENT_VERSION = 1; const CONSENT_LIFETIME_DAYS = 365; /** * Domyślnie BLOKUJEMY analytics, ads, personalization — zgodnie z Consent Mode v2. * Google Tag Manager sprawdza window.dataLayer i te ustawienia ZANIM cokolwiek wyśle. */ function setDefaultConsent() { window.dataLayer = window.dataLayer || []; function gtag() { window.dataLayer.push(arguments); } window.gtag = window.gtag || gtag; gtag('consent', 'default', { 'ad_storage': 'denied', 'ad_user_data': 'denied', 'ad_personalization': 'denied', 'analytics_storage': 'denied', 'functionality_storage': 'granted', // niezbędne do działania 'personalization_storage': 'denied', 'security_storage': 'granted', // antifraud — zawsze 'wait_for_update': 500, }); } function updateConsent(prefs) { if (!window.gtag) return; window.gtag('consent', 'update', { 'ad_storage': prefs.ads ? 'granted' : 'denied', 'ad_user_data': prefs.ads ? 'granted' : 'denied', 'ad_personalization': prefs.ads ? 'granted' : 'denied', 'analytics_storage': prefs.analytics ? 'granted' : 'denied', 'personalization_storage': prefs.personalization ? 'granted' : 'denied', }); } function readConsent() { try { const raw = localStorage.getItem(CONSENT_LS_KEY); if (!raw) return null; const parsed = JSON.parse(raw); if (parsed.version !== CONSENT_VERSION) return null; // Wygasła zgoda? const ageDays = (Date.now() - parsed.timestamp) / (1000 * 60 * 60 * 24); if (ageDays > CONSENT_LIFETIME_DAYS) return null; return parsed.preferences; } catch { return null; } } function saveConsent(preferences) { const payload = { version: CONSENT_VERSION, timestamp: Date.now(), preferences, }; try { localStorage.setItem(CONSENT_LS_KEY, JSON.stringify(payload)); } catch {} // Wyślij do backendu (audit log RODO) try { fetch('/api/consent.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ preferences, language: document.documentElement.lang || 'pl' }), keepalive: true, }).catch(() => {}); } catch {} updateConsent(preferences); } // Inicjalizuj Consent Mode jak najwcześniej (przed jakimkolwiek GA/AdSense) setDefaultConsent(); // Po starcie, jeśli już była zgoda — zastosuj const existing = readConsent(); if (existing) { updateConsent(existing); } /* ─────────── React component: banner i panel preferencji ─────────── */ function CookieBanner({ lang = 'pl' }) { const [consent, setConsent] = useStateC2(() => readConsent()); const [showSettings, setShowSettings] = useStateC2(false); const [prefs, setPrefs] = useStateC2({ necessary: true, // zawsze on, nie do wyboru functional: true, analytics: false, ads: false, personalization: false, }); // Odsłuchuj globalnego eventu "openCookieSettings" (np. z linku w stopce) useEffectC2(() => { const open = () => { const c = readConsent(); if (c) setPrefs(prev => ({ ...prev, ...c })); setShowSettings(true); }; window.addEventListener('openCookieSettings', open); return () => window.removeEventListener('openCookieSettings', open); }, []); const t = COOKIE_I18N[lang] || COOKIE_I18N.pl; // Już udzielił zgody — banner ukryty, tylko link w stopce otwiera ustawienia if (consent && !showSettings) return null; const acceptAll = () => { const all = { necessary: true, functional: true, analytics: true, ads: true, personalization: true }; saveConsent(all); setConsent(all); setShowSettings(false); }; const acceptNecessary = () => { const min = { necessary: true, functional: false, analytics: false, ads: false, personalization: false }; saveConsent(min); setConsent(min); setShowSettings(false); }; const saveCustom = () => { saveConsent(prefs); setConsent(prefs); setShowSettings(false); }; return ( <> {/* Modalne ustawienia */} {showSettings && (
{ if (e.target === e.currentTarget) setShowSettings(false); }} >

{t.settingsDesc}

setPrefs(p => ({ ...p, functional: v }))} /> setPrefs(p => ({ ...p, analytics: v }))} /> setPrefs(p => ({ ...p, ads: v }))} /> setPrefs(p => ({ ...p, personalization: v }))} />
{t.linkPrivacy} {' · '} {t.linkCookies}
)} {/* Banner stały (jak nie ma jeszcze zgody) */} {!consent && !showSettings && (
🍪
{t.title}

{t.body}{' '} {t.linkCookies}{' · '} {t.linkPrivacy}

)} ); } function CookieCategory({ label, desc, checked, onChange, locked = false }) { return ( ); } /* ─────────── Tłumaczenia bannera ─────────── */ const COOKIE_I18N = { pl: { title: "Używamy plików cookie", body: "Stosujemy pliki cookie aby zapewnić działanie strony, mierzyć ruch (Google Analytics), wyświetlać reklamy (Google AdSense) i poprawiać Twoje doświadczenie. Możesz wybrać które kategorie akceptujesz.", bannerAria: "Powiadomienie o plikach cookie", btnAcceptAll: "Akceptuj wszystkie", btnNecessary: "Tylko niezbędne", btnSettings: "Dostosuj", btnSave: "Zapisz wybór", linkCookies: "Polityka cookies", linkPrivacy: "Polityka prywatności", settingsTitle: "Ustawienia plików cookie", settingsDesc: "Wybierz które kategorie cookies akceptujesz. Wybór możesz zmienić w dowolnym momencie klikając ikonę w stopce.", necessary: "Niezbędne", necessaryDesc: "Wymagane do działania strony — sesja, bezpieczeństwo, preferencje wyboru języka. Tych nie można wyłączyć.", functional: "Funkcjonalne", functionalDesc: "Zapamiętują Twoje preferencje (np. układ kafelków, ostatnio wybrany kraj) między wizytami.", analytics: "Analityczne (Google Analytics)", analyticsDesc: "Pomagają zrozumieć jak użytkownicy korzystają ze strony. Dane są anonimowe i zagregowane.", ads: "Reklamowe (Google AdSense)", adsDesc: "Pozwalają wyświetlać reklamy dopasowane do Twoich zainteresowań. Bez tego widzisz reklamy losowe.", personalization: "Personalizacja", personalizationDesc: "Dostosowują treści (np. polecane artykuły) do Twojej historii przeglądania na naszej stronie.", }, en: { title: "We use cookies", body: "We use cookies to ensure the site works, measure traffic (Google Analytics), display ads (Google AdSense), and improve your experience. You can choose which categories you accept.", bannerAria: "Cookie notice", btnAcceptAll: "Accept all", btnNecessary: "Necessary only", btnSettings: "Customize", btnSave: "Save choice", linkCookies: "Cookie Policy", linkPrivacy: "Privacy Policy", settingsTitle: "Cookie settings", settingsDesc: "Choose which cookie categories you accept. You can change your choice at any time via the footer link.", necessary: "Necessary", necessaryDesc: "Required for the site to work — session, security, language preference. Cannot be disabled.", functional: "Functional", functionalDesc: "Remember your preferences (e.g. tile layout, last selected country) between visits.", analytics: "Analytics (Google Analytics)", analyticsDesc: "Help us understand how users use the site. Data is anonymous and aggregated.", ads: "Advertising (Google AdSense)", adsDesc: "Allow us to display ads matched to your interests. Without these you see random ads.", personalization: "Personalization", personalizationDesc: "Adapt content (e.g. recommended articles) to your browsing history on our site.", }, de: { title: "Wir verwenden Cookies", body: "Wir verwenden Cookies, um den Betrieb der Website sicherzustellen, den Datenverkehr zu messen (Google Analytics), Werbung anzuzeigen (Google AdSense) und Ihre Erfahrung zu verbessern.", bannerAria: "Cookie-Hinweis", btnAcceptAll: "Alle akzeptieren", btnNecessary: "Nur notwendige", btnSettings: "Anpassen", btnSave: "Auswahl speichern", linkCookies: "Cookie-Richtlinie", linkPrivacy: "Datenschutzerklärung", settingsTitle: "Cookie-Einstellungen", settingsDesc: "Wählen Sie, welche Cookie-Kategorien Sie akzeptieren. Sie können Ihre Wahl jederzeit über den Link im Footer ändern.", necessary: "Notwendig", necessaryDesc: "Erforderlich für den Betrieb — Sitzung, Sicherheit, Spracheinstellung. Kann nicht deaktiviert werden.", functional: "Funktional", functionalDesc: "Speichern Ihre Einstellungen zwischen Besuchen.", analytics: "Analyse (Google Analytics)", analyticsDesc: "Helfen zu verstehen, wie Nutzer die Website verwenden. Daten sind anonym und aggregiert.", ads: "Werbung (Google AdSense)", adsDesc: "Ermöglichen die Anzeige interessenbasierter Werbung.", personalization: "Personalisierung", personalizationDesc: "Passen Inhalte an Ihre Browsing-Historie auf unserer Website an.", }, fr: { title: "Nous utilisons des cookies", body: "Nous utilisons des cookies pour assurer le fonctionnement du site, mesurer le trafic (Google Analytics), afficher des publicités (Google AdSense) et améliorer votre expérience.", bannerAria: "Avis sur les cookies", btnAcceptAll: "Tout accepter", btnNecessary: "Nécessaires uniquement", btnSettings: "Personnaliser", btnSave: "Enregistrer", linkCookies: "Politique cookies", linkPrivacy: "Politique de confidentialité", settingsTitle: "Paramètres des cookies", settingsDesc: "Choisissez les catégories de cookies que vous acceptez. Vous pouvez modifier votre choix à tout moment via le lien dans le pied de page.", necessary: "Nécessaires", necessaryDesc: "Requis pour le fonctionnement du site. Ne peuvent être désactivés.", functional: "Fonctionnels", functionalDesc: "Mémorisent vos préférences entre les visites.", analytics: "Analyse (Google Analytics)", analyticsDesc: "Aident à comprendre comment les utilisateurs utilisent le site.", ads: "Publicité (Google AdSense)", adsDesc: "Permettent l'affichage de publicités personnalisées.", personalization: "Personnalisation", personalizationDesc: "Adaptent le contenu à votre historique de navigation.", }, cs: { title: "Používáme soubory cookie", body: "Používáme cookies pro zajištění funkcí webu, měření návštěvnosti (Google Analytics), zobrazování reklam (Google AdSense) a zlepšení vašich zkušeností.", bannerAria: "Upozornění o cookies", btnAcceptAll: "Přijmout vše", btnNecessary: "Pouze nezbytné", btnSettings: "Přizpůsobit", btnSave: "Uložit volbu", linkCookies: "Zásady cookies", linkPrivacy: "Zásady ochrany", settingsTitle: "Nastavení cookies", settingsDesc: "Zvolte, které kategorie cookies přijímáte. Volbu lze kdykoli změnit přes odkaz v zápatí.", necessary: "Nezbytné", necessaryDesc: "Vyžadováno pro fungování webu. Nelze deaktivovat.", functional: "Funkční", functionalDesc: "Pamatují vaše preference mezi návštěvami.", analytics: "Analytické (Google Analytics)", analyticsDesc: "Pomáhají pochopit, jak uživatelé používají web.", ads: "Reklamní (Google AdSense)", adsDesc: "Umožňují zobrazovat reklamy podle vašich zájmů.", personalization: "Personalizace", personalizationDesc: "Přizpůsobují obsah vaší historii prohlížení.", }, hu: { title: "Sütiket használunk", body: "Sütiket használunk a működés, forgalom mérése (Google Analytics), hirdetések (AdSense) és élmény javítása érdekében.", bannerAria: "Süti értesítés", btnAcceptAll: "Összes elfogadása", btnNecessary: "Csak szükséges", btnSettings: "Testreszabás", btnSave: "Mentés", linkCookies: "Süti szabályzat", linkPrivacy: "Adatvédelem", settingsTitle: "Süti beállítások", settingsDesc: "Válassza ki, mely kategóriákat fogadja el. Bármikor módosíthatja a láblécben.", necessary: "Szükséges", necessaryDesc: "A weboldal működéséhez kötelező. Nem kapcsolható ki.", functional: "Funkcionális", functionalDesc: "Megjegyzi a beállításait.", analytics: "Analitika (Google Analytics)", analyticsDesc: "Segít megérteni, hogyan használják a weboldalt.", ads: "Hirdetések (AdSense)", adsDesc: "Lehetővé teszik a személyre szabott hirdetéseket.", personalization: "Személyre szabás", personalizationDesc: "Tartalom a böngészési előzményei alapján.", }, nl: { title: "Wij gebruiken cookies", body: "Wij gebruiken cookies voor de werking van de site, verkeersmeting (Google Analytics), advertenties (Google AdSense) en betere ervaring.", bannerAria: "Cookie-melding", btnAcceptAll: "Alles accepteren", btnNecessary: "Alleen noodzakelijk", btnSettings: "Aanpassen", btnSave: "Opslaan", linkCookies: "Cookiebeleid", linkPrivacy: "Privacybeleid", settingsTitle: "Cookie-instellingen", settingsDesc: "Kies welke categorieën u accepteert. U kunt uw keuze altijd wijzigen via de link in de voettekst.", necessary: "Noodzakelijk", necessaryDesc: "Vereist voor de werking van de site. Kan niet worden uitgeschakeld.", functional: "Functioneel", functionalDesc: "Onthouden uw voorkeuren tussen bezoeken.", analytics: "Analyse (Google Analytics)", analyticsDesc: "Helpen begrijpen hoe gebruikers de site gebruiken.", ads: "Advertenties (Google AdSense)", adsDesc: "Maken gepersonaliseerde advertenties mogelijk.", personalization: "Personalisatie", personalizationDesc: "Inhoud op basis van uw browsegeschiedenis.", }, }; // Eksportuj do globalnego scope window.CookieBanner = CookieBanner; window.openCookieSettings = () => window.dispatchEvent(new CustomEvent('openCookieSettings')); window.readConsent = readConsent;