From 78009ff3b05d703a878eb3baec10458cc092aec1 Mon Sep 17 00:00:00 2001 From: jessy-david-dev Date: Tue, 27 Jan 2026 17:59:13 +0100 Subject: [PATCH] feat(cookies): add GDPR-compliant cookie consent banner --- component/ui/CookieBanner.tsx | 294 ++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 component/ui/CookieBanner.tsx diff --git a/component/ui/CookieBanner.tsx b/component/ui/CookieBanner.tsx new file mode 100644 index 0000000..b338e13 --- /dev/null +++ b/component/ui/CookieBanner.tsx @@ -0,0 +1,294 @@ +"use client"; + +import { motion, AnimatePresence } from "framer-motion"; +import { Cookie, X, Check, Settings } from "lucide-react"; +import Link from "next/link"; +import { useState, useEffect } from "react"; + +type CookiePreferences = { + necessary: boolean; + analytics: boolean; +}; + +const COOKIE_CONSENT_KEY = "cookie-consent"; +const COOKIE_PREFERENCES_KEY = "cookie-preferences"; + +function getInitialPreferences(): CookiePreferences { + if (typeof window === "undefined") { + return { necessary: true, analytics: false }; + } + const savedPreferences = localStorage.getItem(COOKIE_PREFERENCES_KEY); + if (savedPreferences) { + try { + return JSON.parse(savedPreferences); + } catch { + return { necessary: true, analytics: false }; + } + } + return { necessary: true, analytics: false }; +} + +export default function CookieBanner() { + const [showBanner, setShowBanner] = useState(false); + const [showSettings, setShowSettings] = useState(false); + const [preferences, setPreferences] = useState( + getInitialPreferences, + ); + + useEffect(() => { + // Vérifier si l'utilisateur a déjà donné son consentement + const consent = localStorage.getItem(COOKIE_CONSENT_KEY); + if (!consent) { + // Délai pour ne pas afficher immédiatement au chargement + const timer = setTimeout(() => setShowBanner(true), 1000); + return () => clearTimeout(timer); + } else { + // Appliquer les préférences sauvegardées au chargement + const savedPreferences = localStorage.getItem(COOKIE_PREFERENCES_KEY); + if (savedPreferences) { + try { + const prefs = JSON.parse(savedPreferences); + if (prefs.analytics) { + // @ts-expect-error - gtag n'est pas typé + window.gtag?.("consent", "update", { + analytics_storage: "granted", + }); + } + } catch { + // Ignorer les erreurs de parsing + } + } + } + }, []); + + const handleAcceptAll = () => { + const newPreferences = { necessary: true, analytics: true }; + saveConsent(newPreferences); + }; + + const handleRejectAll = () => { + const newPreferences = { necessary: true, analytics: false }; + saveConsent(newPreferences); + }; + + const handleSavePreferences = () => { + saveConsent(preferences); + }; + + const saveConsent = (prefs: CookiePreferences) => { + localStorage.setItem(COOKIE_CONSENT_KEY, "true"); + localStorage.setItem(COOKIE_PREFERENCES_KEY, JSON.stringify(prefs)); + setPreferences(prefs); + setShowBanner(false); + setShowSettings(false); + + // Activer/désactiver Google Analytics selon le consentement + if (prefs.analytics) { + enableAnalytics(); + } else { + disableAnalytics(); + } + }; + + const enableAnalytics = () => { + // Active Google Analytics via GTM Consent Mode + if (typeof window !== "undefined") { + // @ts-expect-error - gtag n'est pas typé + window.gtag?.("consent", "update", { + analytics_storage: "granted", + }); + // Envoyer un événement pour GTM + // @ts-expect-error - dataLayer n'est pas typé + window.dataLayer?.push({ event: "cookie_consent_analytics" }); + } + }; + + const disableAnalytics = () => { + // Désactive Google Analytics via GTM Consent Mode + if (typeof window !== "undefined") { + // @ts-expect-error - gtag n'est pas typé + window.gtag?.("consent", "update", { + analytics_storage: "denied", + }); + } + }; + + // Fonction pour rouvrir la bannière (appelable depuis le footer) + useEffect(() => { + const handleOpenCookieSettings = () => { + setShowBanner(true); + setShowSettings(true); + }; + + window.addEventListener("openCookieSettings", handleOpenCookieSettings); + return () => + window.removeEventListener( + "openCookieSettings", + handleOpenCookieSettings, + ); + }, []); + + return ( + + {showBanner && ( + +
+
+ {/* Header */} +
+
+
+ +
+

+ Gestion des cookies +

+
+ +
+ + {/* Content */} +
+ {!showSettings ? ( + <> +

+ Ce site utilise des cookies pour améliorer votre + expérience. Les cookies nécessaires sont essentiels au bon + fonctionnement du site. Vous pouvez choisir + d'accepter ou de refuser les cookies analytiques.{" "} + + En savoir plus + +

+ + {/* Boutons */} +
+ + + +
+ + ) : ( + <> +

+ Personnalisez vos préférences de cookies ci-dessous. +

+ + {/* Options de cookies */} +
+ {/* Cookies nécessaires */} +
+
+

+ Cookies nécessaires +

+

+ Essentiels au fonctionnement du site (session, + sécurité, préférences). +

+
+
+
+
+ +
+
+
+
+ + {/* Cookies analytiques */} +
+
+

+ Cookies analytiques +

+

+ Google Analytics pour comprendre comment vous + utilisez ce site. +

+
+
+ +
+
+
+ + {/* Boutons */} +
+ + +
+ + )} +
+
+
+
+ )} +
+ ); +}