/** * Cookie Consent Context * * Provides cookie consent state and methods to the application * Syncs with backend for authenticated users */ 'use client'; import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; import { getConsentPreferences, updateConsentPreferences, type CookiePreferences, } from '../api/gdpr'; import { hasSession } from '../api/client'; const STORAGE_KEY = 'cookieConsent'; const STORAGE_DATE_KEY = 'cookieConsentDate'; interface CookieContextType { preferences: CookiePreferences; showBanner: boolean; showSettings: boolean; isLoading: boolean; setShowBanner: (show: boolean) => void; setShowSettings: (show: boolean) => void; acceptAll: () => Promise; acceptEssentialOnly: () => Promise; savePreferences: (prefs: CookiePreferences) => Promise; openPreferences: () => void; } const defaultPreferences: CookiePreferences = { essential: true, functional: false, analytics: false, marketing: false, }; const CookieContext = createContext(undefined); export function CookieProvider({ children }: { children: React.ReactNode }) { const [preferences, setPreferences] = useState(defaultPreferences); const [showBanner, setShowBanner] = useState(false); const [showSettings, setShowSettings] = useState(false); const [isLoading, setIsLoading] = useState(true); const [hasInitialized, setHasInitialized] = useState(false); // Check if user is authenticated const isAuthenticated = useCallback(() => { return hasSession(); }, []); // Load preferences from localStorage const loadFromLocalStorage = useCallback((): CookiePreferences | null => { if (typeof window === 'undefined') return null; const stored = localStorage.getItem(STORAGE_KEY); if (stored) { try { return JSON.parse(stored); } catch { return null; } } return null; }, []); // Save preferences to localStorage const saveToLocalStorage = useCallback((prefs: CookiePreferences) => { if (typeof window === 'undefined') return; localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs)); localStorage.setItem(STORAGE_DATE_KEY, new Date().toISOString()); }, []); // Apply preferences (gtag, etc.) const applyPreferences = useCallback((prefs: CookiePreferences) => { if (typeof window === 'undefined') return; // Google Analytics consent const gtag = (window as any).gtag; if (gtag) { gtag('consent', 'update', { analytics_storage: prefs.analytics ? 'granted' : 'denied', ad_storage: prefs.marketing ? 'granted' : 'denied', functionality_storage: prefs.functional ? 'granted' : 'denied', }); } // Sentry (if analytics enabled) const Sentry = (window as any).Sentry; if (Sentry) { if (prefs.analytics) { Sentry.init && Sentry.getCurrentHub && Sentry.getCurrentHub().getClient()?.getOptions(); } } }, []); // Initialize preferences useEffect(() => { const initializePreferences = async () => { setIsLoading(true); // First, check localStorage const localPrefs = loadFromLocalStorage(); if (localPrefs) { setPreferences(localPrefs); applyPreferences(localPrefs); setShowBanner(false); } else { // No local consent - show banner setShowBanner(true); } // If authenticated, try to sync with backend if (isAuthenticated() && localPrefs) { try { const backendPrefs = await getConsentPreferences(); if (backendPrefs) { // Backend has preferences - use them const prefs: CookiePreferences = { essential: backendPrefs.essential, functional: backendPrefs.functional, analytics: backendPrefs.analytics, marketing: backendPrefs.marketing, }; setPreferences(prefs); saveToLocalStorage(prefs); applyPreferences(prefs); } else if (localPrefs) { // No backend preferences but we have local - sync to backend try { await updateConsentPreferences({ ...localPrefs, userAgent: navigator.userAgent, }); } catch (syncError) { console.warn('Failed to sync consent to backend:', syncError); } } } catch (error) { console.warn('Failed to fetch consent from backend:', error); // Continue with local preferences } } setIsLoading(false); setHasInitialized(true); }; initializePreferences(); }, [isAuthenticated, loadFromLocalStorage, saveToLocalStorage, applyPreferences]); // Save preferences (to localStorage and optionally backend) const savePreferences = useCallback( async (prefs: CookiePreferences) => { // Ensure essential is always true const safePrefs = { ...prefs, essential: true }; setPreferences(safePrefs); saveToLocalStorage(safePrefs); applyPreferences(safePrefs); setShowBanner(false); setShowSettings(false); // Sync to backend if authenticated if (isAuthenticated()) { try { await updateConsentPreferences({ ...safePrefs, userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined, }); } catch (error) { console.warn('Failed to sync consent to backend:', error); // Local save succeeded, backend sync failed - that's okay } } }, [isAuthenticated, saveToLocalStorage, applyPreferences] ); const acceptAll = useCallback(async () => { await savePreferences({ essential: true, functional: true, analytics: true, marketing: true, }); }, [savePreferences]); const acceptEssentialOnly = useCallback(async () => { await savePreferences({ essential: true, functional: false, analytics: false, marketing: false, }); }, [savePreferences]); const openPreferences = useCallback(() => { setShowBanner(true); setShowSettings(true); }, []); const value: CookieContextType = { preferences, showBanner, showSettings, isLoading, setShowBanner, setShowSettings, acceptAll, acceptEssentialOnly, savePreferences, openPreferences, }; return {children}; } export function useCookieConsent() { const context = useContext(CookieContext); if (context === undefined) { throw new Error('useCookieConsent must be used within a CookieProvider'); } return context; }