web-next: «Personalizar» abre panel granular de cookies (uw-cookie-details)

El botón Personalizar del banner ahora despliega un panel con checkboxes por
categoría (Necesarias fija+desactivada, Analíticas, Marketing) y «Guardar
preferencias», tal como el markup del usuario. Al guardar, escribe la cookie
uw_cookie_consent con la selección granular y sincroniza con Zaraz. Categorías
alineadas a las del panel (Necesarias/Analíticas/Marketing); ConsentStatus y
acceptAll/rejectAll usan las mismas. Estilos del panel en globals.css.

Verificado: Personalizar abre el panel, marcar Analíticas + Guardar deja la
cookie {necessary,analytics:true,marketing:false} y /cookies lo refleja.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 12:02:49 +00:00
parent 4ebe3c34d3
commit 57e91480d1
2 changed files with 98 additions and 15 deletions
+46
View File
@@ -262,6 +262,52 @@ textarea:focus {
border-color: #5c2b2b;
color: #d98c8c;
}
.uw-cookie-btn-save {
border-color: #b17c02;
color: #f0b93f;
}
.uw-cookie-btn-save:hover {
background: hsla(40, 90%, 45%, 0.15);
color: #fff;
}
/* Panel «Personalizar» del banner (checkboxes por categoría). */
.uw-cookie-details {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid #352e2b;
}
.uw-cookie-category {
margin-bottom: 10px;
}
.uw-cookie-category label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.uw-cookie-category input[type='checkbox'] {
width: 16px;
height: 16px;
accent-color: #d79602;
cursor: pointer;
}
.uw-cookie-category input[type='checkbox']:disabled {
cursor: default;
}
.uw-cookie-cat-name {
color: #ebdec2;
font-size: 15px;
}
.uw-cookie-category p {
color: #b1997f;
font-size: 13px;
margin: 2px 0 0 24px;
line-height: 18px;
}
.uw-cookie-actions-detail {
margin-top: 8px;
}
/* Panel «Tu estado de consentimiento» en /cookies (datos de la API de Zaraz). */
.uw-consent-status {
+52 -15
View File
@@ -27,12 +27,11 @@ declare global {
}
// Categorías de consentimiento (necesarias siempre activas).
type Category = { key: string; label: string; always?: boolean }
type Category = { key: string; label: string; always?: boolean; desc?: string }
export const CONSENT_CATEGORIES: readonly Category[] = [
{ key: 'necessary', label: 'Necesarias', always: true },
{ key: 'preferences', label: 'Preferencias' },
{ key: 'analytics', label: 'Analíticas' },
{ key: 'marketing', label: 'Marketing' },
{ 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<string, boolean>
@@ -79,12 +78,6 @@ function acceptAll() {
function rejectAll() {
writeConsent(allFlags(false))
}
/** Abre el modal granular de Zaraz; si no está, lleva a la página de cookies. */
function customize() {
const c = zaraz()
if (c) c.modal = true
else if (typeof window !== 'undefined') window.location.href = '/cookies'
}
function showBanner() {
window.dispatchEvent(new CustomEvent(SHOW_EVENT))
}
@@ -101,12 +94,22 @@ function revokeConsent() {
// ---------------------------------------------------------------------------
export function CookieBanner() {
const [visible, setVisible] = useState(false)
const [details, setDetails] = useState(false)
// Estado de los checkboxes granulares (categorías no necesarias).
const [prefs, setPrefs] = useState<Consent>({ analytics: false, marketing: false })
useEffect(() => {
window.UWCookies = { showBanner, revokeConsent, acceptAll, rejectAll }
// Aparece si aún no hay decisión guardada.
if (!readConsent()) setVisible(true)
const onShow = () => setVisible(true)
// 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)
}
@@ -120,16 +123,50 @@ export function CookieBanner() {
if (!visible) return null
function savePrefs() {
writeConsent({ necessary: true, analytics: !!prefs.analytics, marketing: !!prefs.marketing })
setVisible(false)
}
return (
<div className="uw-cookie-content" role="dialog" aria-label="Consentimiento de cookies">
<div className="uw-cookie-text">
<h3>Utilizamos cookies</h3>
<p>Este sitio utiliza cookies propias y de terceros para garantizar el funcionamiento del sitio, analizar el tráfico y ofrecerte publicidad personalizada. Consulta nuestra política de cookies para más información.</p>
<Link href="/cookies" className="uw-cookie-link">Política de Cookies</Link>
{details && (
<div className="uw-cookie-details" style={{ display: 'block' }}>
<div className="uw-cookie-category">
<label>
<input type="checkbox" checked disabled readOnly />
<span className="uw-cookie-cat-name">Necesarias</span>
</label>
<p>Esenciales para el funcionamiento del sitio. No se pueden desactivar.</p>
</div>
<div className="uw-cookie-category">
<label>
<input type="checkbox" id="uw-cookie-analytics" checked={!!prefs.analytics} onChange={(e) => setPrefs((s) => ({ ...s, analytics: e.target.checked }))} />
<span className="uw-cookie-cat-name">Analíticas</span>
</label>
<p>Nos ayudan a entender cómo usas el sitio para mejorarlo.</p>
</div>
<div className="uw-cookie-category">
<label>
<input type="checkbox" id="uw-cookie-marketing" checked={!!prefs.marketing} onChange={(e) => setPrefs((s) => ({ ...s, marketing: e.target.checked }))} />
<span className="uw-cookie-cat-name">Marketing</span>
</label>
<p>Se utilizan para mostrarte anuncios relevantes.</p>
</div>
<div className="uw-cookie-actions-detail">
<button type="button" className="uw-cookie-btn uw-cookie-btn-save" data-action="save" onClick={savePrefs}>Guardar preferencias</button>
</div>
</div>
)}
</div>
<div className="uw-cookie-actions">
<button type="button" className="uw-cookie-btn uw-cookie-btn-reject" data-action="reject" onClick={() => { rejectAll(); setVisible(false) }}>Rechazar</button>
<button type="button" className="uw-cookie-btn uw-cookie-btn-customize" data-action="customize" onClick={customize}>Personalizar</button>
<button type="button" className="uw-cookie-btn uw-cookie-btn-customize" data-action="customize" onClick={() => setDetails((v) => !v)}>Personalizar</button>
<button type="button" className="uw-cookie-btn uw-cookie-btn-accept" data-action="accept" onClick={() => { acceptAll(); setVisible(false) }}>Aceptar todas</button>
</div>
</div>