// Auteur : Simon-Pierre Boucher — contact@spboucher.ai /** * Gestionnaire de consentement aux témoins (Loi 25) : * bandeau au premier passage + panneau de préférences réouvrable partout * (événement window « vp-open-cookie-prefs » ou bouton du pied de page). * Choix conservé 12 mois dans localStorage (vp-cookie-consent). */ "use client"; import { useEffect, useState } from "react"; import { useLang } from "./LangContext"; export interface ConsentState { essential: true; preferences: boolean; analytics: boolean; savedAt: string; } const KEY = "vp-cookie-consent"; const MAX_AGE_DAYS = 365; export function readConsent(): ConsentState | null { if (typeof window === "undefined") return null; try { const raw = window.localStorage.getItem(KEY); if (!raw) return null; const c = JSON.parse(raw) as ConsentState; const age = (Date.now() - new Date(c.savedAt).getTime()) / 86400000; return age > MAX_AGE_DAYS ? null : c; } catch { return null; } } export function openCookiePrefs() { window.dispatchEvent(new Event("vp-open-cookie-prefs")); } const TXT = { fr: { banner: "Vrai-Prix utilise un minimum de témoins : des essentiels (fonctionnement du site) et, si vous l'acceptez, un témoin de préférences pour retenir votre langue. Aucun témoin publicitaire, aucun pistage tiers.", acceptAll: "Tout accepter", essentialOnly: "Essentiels seulement", customize: "Personnaliser", prefsTitle: "Préférences de témoins", essential: "Essentiels", essentialDesc: "Nécessaires au fonctionnement (sécurité, session). Toujours actifs.", prefs: "Préférences", prefsDesc: "Retient votre langue d'affichage (FR/EN) sur cet appareil, 12 mois.", analytics: "Mesure d'audience", analyticsDesc: "Aucun outil de mesure n'est actuellement utilisé. Ce réglage s'appliquera si un jour un outil respectueux est ajouté.", always: "Toujours actifs", save: "Enregistrer mes choix", more: "Politique de confidentialité", }, en: { banner: "Vrai-Prix uses a minimum of cookies: essential ones (site operation) and, with your consent, a preference cookie to remember your language. No advertising cookies, no third-party tracking.", acceptAll: "Accept all", essentialOnly: "Essential only", customize: "Customize", prefsTitle: "Cookie preferences", essential: "Essential", essentialDesc: "Required for operation (security, session). Always on.", prefs: "Preferences", prefsDesc: "Remembers your display language (FR/EN) on this device for 12 months.", analytics: "Analytics", analyticsDesc: "No analytics tool is currently used. This setting will apply if a privacy-respecting tool is ever added.", always: "Always on", save: "Save my choices", more: "Privacy policy", }, }; function Toggle({ on, disabled, onChange, }: { on: boolean; disabled?: boolean; onChange?: (v: boolean) => void; }) { return ( ); } export default function CookieConsent() { const { lang } = useLang(); const t = TXT[lang]; const [ready, setReady] = useState(false); const [consent, setConsent] = useState(null); const [panel, setPanel] = useState(false); const [prefs, setPrefs] = useState(true); const [analytics, setAnalytics] = useState(false); useEffect(() => { const id = setTimeout(() => { const c = readConsent(); setConsent(c); if (c) { setPrefs(c.preferences); setAnalytics(c.analytics); } setReady(true); }, 0); const open = () => setPanel(true); window.addEventListener("vp-open-cookie-prefs", open); return () => { clearTimeout(id); window.removeEventListener("vp-open-cookie-prefs", open); }; }, []); const persist = (preferences: boolean, analyticsOn: boolean) => { const c: ConsentState = { essential: true, preferences, analytics: analyticsOn, savedAt: new Date().toISOString(), }; window.localStorage.setItem(KEY, JSON.stringify(c)); if (!preferences) window.localStorage.removeItem("vp-lang"); setConsent(c); setPanel(false); }; if (!ready) return null; const showBanner = consent === null && !panel; return ( <> {showBanner && (

🍪 {t.prefsTitle}

{t.banner}{" "} {t.more}

)} {panel && (
setPanel(false)} >
e.stopPropagation()} role="dialog" aria-modal="true" aria-label={t.prefsTitle} >

{t.prefsTitle}

{[ { k: t.essential, d: t.essentialDesc, on: true, lock: true, set: undefined }, { k: t.prefs, d: t.prefsDesc, on: prefs, lock: false, set: setPrefs }, { k: t.analytics, d: t.analyticsDesc, on: analytics, lock: false, set: setAnalytics }, ].map((row) => (

{row.k} {row.lock && ( {t.always} )}

{row.d}

))}
)} ); }