'use client' import { useEffect, useState } from 'react' import { useTranslations } from 'next-intl' import { Link } from '@/i18n/navigation' // --------------------------------------------------------------------------- // API de consentimiento de Cloudflare Zaraz (se inyecta cuando el dominio pasa // por el proxy de Cloudflare con Zaraz Consent activado). // --------------------------------------------------------------------------- interface ZarazConsent { modal?: boolean APIReady?: boolean setAll?: (status: boolean) => void getAll?: () => Record sendQueuedEvents?: () => void } declare global { interface Window { zaraz?: { consent?: ZarazConsent } UWCookies?: { showBanner: () => void revokeConsent: () => void acceptAll: () => void rejectAll: () => void } } } // Categorías de consentimiento (necesarias siempre activas). type Category = { key: string; label: string; always?: boolean; desc?: string } export const CONSENT_CATEGORIES: readonly Category[] = [ { key: 'necessary', label: 'Necesarias', always: true, desc: 'Esenciales para el funcionamiento del sitio. No se pueden desactivar.' }, { key: 'analytics', label: 'Analíticas', desc: 'Nos ayudan a entender cómo usas el sitio para mejorarlo.' }, { key: 'marketing', label: 'Marketing', desc: 'Se utilizan para mostrarte anuncios relevantes.' }, ] type Consent = Record const CONSENT_COOKIE = 'uw_cookie_consent' const CONSENT_EVENT = 'uw:consentchange' const SHOW_EVENT = 'uw:showbanner' function zaraz(): ZarazConsent | undefined { return typeof window !== 'undefined' ? window.zaraz?.consent : undefined } /** Lee la preferencia guardada en la cookie uw_cookie_consent. */ function readConsent(): Consent | null { if (typeof document === 'undefined') return null const m = document.cookie.match(/(?:^|; )uw_cookie_consent=([^;]*)/) if (!m) return null try { return JSON.parse(decodeURIComponent(m[1])) as Consent } catch { return null } } function writeConsent(c: Consent) { const maxAge = 60 * 60 * 24 * 365 // 1 año document.cookie = `${CONSENT_COOKIE}=${encodeURIComponent(JSON.stringify(c))}; path=/; max-age=${maxAge}; SameSite=Lax` // Sincroniza con Cloudflare Zaraz si está presente (control granular real lo // gestiona el panel de Zaraz; aquí aplicamos aceptar/rechazar todo). const nonNecessary = CONSENT_CATEGORIES.some((cat) => !cat.always && c[cat.key]) zaraz()?.setAll?.(nonNecessary) zaraz()?.sendQueuedEvents?.() window.dispatchEvent(new CustomEvent(CONSENT_EVENT)) } function allFlags(value: boolean): Consent { const c: Consent = {} for (const cat of CONSENT_CATEGORIES) c[cat.key] = cat.always ? true : value return c } function acceptAll() { writeConsent(allFlags(true)) } function rejectAll() { writeConsent(allFlags(false)) } function showBanner() { window.dispatchEvent(new CustomEvent(SHOW_EVENT)) } function revokeConsent() { // Borra la cookie, retira en Zaraz y reabre el banner. document.cookie = `${CONSENT_COOKIE}=; path=/; max-age=0; SameSite=Lax` zaraz()?.setAll?.(false) window.dispatchEvent(new CustomEvent(CONSENT_EVENT)) showBanner() } // --------------------------------------------------------------------------- // Banner de consentimiento (fijo abajo, en todo el sitio). // --------------------------------------------------------------------------- export function CookieBanner() { const t = useTranslations('UI') const [visible, setVisible] = useState(false) const [details, setDetails] = useState(false) // Estado de los checkboxes granulares (categorías no necesarias). const [prefs, setPrefs] = useState({ analytics: false, marketing: false }) useEffect(() => { window.UWCookies = { showBanner, revokeConsent, acceptAll, rejectAll } // Precarga los checkboxes con lo ya guardado (si lo hay). const saved = readConsent() if (saved) setPrefs({ analytics: !!saved.analytics, marketing: !!saved.marketing }) else setVisible(true) // Aparece si aún no hay decisión guardada. const onShow = () => { const cur = readConsent() if (cur) setPrefs({ analytics: !!cur.analytics, marketing: !!cur.marketing }) setDetails(false) setVisible(true) } const onChange = () => { if (readConsent()) setVisible(false) } window.addEventListener(SHOW_EVENT, onShow) window.addEventListener(CONSENT_EVENT, onChange) return () => { window.removeEventListener(SHOW_EVENT, onShow) window.removeEventListener(CONSENT_EVENT, onChange) } }, []) if (!visible) return null function savePrefs() { writeConsent({ necessary: true, analytics: !!prefs.analytics, marketing: !!prefs.marketing }) setVisible(false) } return (

{t('cookie.title')}

{t('cookie.description')}

{t('cookie.policyLink')} {details && (

{t('cookie.descNecessary')}

{t('cookie.descAnalytics')}

{t('cookie.descMarketing')}

)}
) } // --------------------------------------------------------------------------- // Botones «Cambiar / Retirar consentimiento» de la página /cookies. // --------------------------------------------------------------------------- export function CookieConsentButtons() { const t = useTranslations('UI') useEffect(() => { window.UWCookies = { showBanner, revokeConsent, acceptAll, rejectAll } }, []) return (
) } // --------------------------------------------------------------------------- // Panel «Tu estado de consentimiento»: muestra los datos de la API de Zaraz // (o, si Zaraz no está, la preferencia guardada en la cookie uw_cookie_consent). // --------------------------------------------------------------------------- export function ConsentStatus() { const t = useTranslations('UI') const [state, setState] = useState<{ source: string; values: Consent | null }>({ source: 'cookie', values: null, }) useEffect(() => { function refresh() { const c = zaraz() if (c?.APIReady && c.getAll) { setState({ source: 'zaraz', values: c.getAll() }) } else { setState({ source: 'cookie', values: readConsent() }) } } refresh() window.addEventListener(CONSENT_EVENT, refresh) window.addEventListener('zarazConsentAPIReady', refresh) window.addEventListener('zarazConsentChoicesUpdated', refresh) return () => { window.removeEventListener(CONSENT_EVENT, refresh) window.removeEventListener('zarazConsentAPIReady', refresh) window.removeEventListener('zarazConsentChoicesUpdated', refresh) } }, []) const zarazData = state.source === 'zaraz' && state.values const values = state.values const catLabel: Record = { necessary: t('cookie.catNecessary'), analytics: t('cookie.catAnalytics'), marketing: t('cookie.catMarketing'), } return (

{zarazData ? t('cookie.statusZaraz') : values ? t('cookie.statusSaved') : t('cookie.statusUnavailable')}

    {zarazData ? ( // Datos en vivo de la API de Zaraz: cada propósito y su estado. Object.entries(values as Consent).map(([purpose, granted]) => (
  • {purpose} {granted ? t('cookie.accepted') : t('cookie.rejected')}
  • )) ) : ( // Categorías locales (cookie uw_cookie_consent). CONSENT_CATEGORIES.map((cat) => { const on = cat.always || Boolean(values?.[cat.key]) return (
  • {catLabel[cat.key] ?? cat.label} {cat.always ? t('cookie.alwaysOn') : values ? (on ? t('cookie.accepted') : t('cookie.rejected')) : '—'}
  • ) }) )}
) }