70ee518e1d
CookieBanner.tsx (equally-weighted Akzeptieren/Ablehnen, TTDSG) +
useConsent() cookie hook + TrackingScripts.tsx render active
tracking-codes rows only once their consentCategory is actually
accepted ('necessary' always renders). Wired into layout.tsx via the
new getTrackingCodes() fetcher. This is what makes the Phase 1
backend collection (tracking-codes) actually usable end to end.
Also reworks NotifyMeForm back to always-visible input+button (better
UX than a collapse-to-reveal step) — the resulting taller CTA is now
reserved on every card via NotifyMeFormReservedSpace, an invisible
twin rendered behind the real button, so an in-stock card's row
height matches an out-of-stock sibling's without the grid's
row-stretch pushing buttons out of alignment.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { AnimatePresence, motion } from "motion/react";
|
|
import { useConsent } from "../lib/useConsent";
|
|
|
|
/**
|
|
* Bottom banner, shown whenever useConsent()'s cookie hasn't recorded a
|
|
* decision yet — the hard prerequisite for TrackingScripts.tsx to ever
|
|
* render an analytics/marketing script (see that file's own comment).
|
|
* Two equally-sized buttons, not a prominent "Akzeptieren" next to a
|
|
* de-emphasized reject link — TTDSG requires an equally easy way to
|
|
* decline, not just technically present a way to.
|
|
*/
|
|
export function CookieBanner() {
|
|
const { consent, loaded, setConsent } = useConsent();
|
|
const visible = loaded && consent === null;
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{visible && (
|
|
<motion.div
|
|
initial={{ y: 80, opacity: 0 }}
|
|
animate={{ y: 0, opacity: 1 }}
|
|
exit={{ y: 80, opacity: 0 }}
|
|
transition={{ duration: 0.25, ease: "easeOut" }}
|
|
role="dialog"
|
|
aria-label="Cookie-Einstellungen"
|
|
className="fixed inset-x-0 bottom-0 z-50 border-t border-border bg-bg-base px-4 py-5 shadow-[0_-4px_24px_rgba(0,0,0,0.06)] sm:px-6"
|
|
>
|
|
<div className="mx-auto flex max-w-5xl flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
<p className="text-body-sm text-text-primary">
|
|
Wir nutzen Cookies, um diese Seite zu betreiben und ihre Nutzung zu verstehen. Mehr dazu in unserer{" "}
|
|
<Link href="/datenschutz" className="underline hover:text-brand">
|
|
Datenschutzerklärung
|
|
</Link>
|
|
.
|
|
</p>
|
|
<div className="flex shrink-0 gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={() => setConsent({ analytics: false })}
|
|
className="flex-1 rounded-sm border border-border px-5 py-2.5 text-body-sm font-semibold text-text-primary transition-colors hover:border-brand sm:flex-none"
|
|
>
|
|
Ablehnen
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setConsent({ analytics: true })}
|
|
className="flex-1 rounded-sm bg-brand px-5 py-2.5 text-body-sm font-bold text-text-primary transition-colors hover:bg-brand-hover sm:flex-none"
|
|
>
|
|
Akzeptieren
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
}
|