Files
hope-events/frontend/src/components/shared/CookieConsent.tsx
T
joshuaandClaude Opus 5.5 cc62f52ab3 Add registration funnel stats
First-party, consent-based funnel tracking: a cookie notice asks once,
and only visitors who allow it get an anonymous visit id. Event page
views and registration starts are tracked from the browser; submit and
paid come from the registration. Adds the Registration funnel report
(conversion per stage, daily trend, by source/device/ticket option), a
conversion tile on the supervisor/admin dashboards, short-link source
tagging, and a daily rollup of raw rows older than 90 days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CP62L9JxHomA36aYP1ekCe
2026-10-19 18:14:18 +02:00

33 lines
1.6 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { Cookie } from "lucide-react";
import { getConsent, setConsent } from "@/lib/analytics";
/**
* Asks once whether we may use a cookie-like identifier to count visits (our own
* registration-funnel stats). Declining changes nothing else about the site.
*/
export function CookieConsent() {
const [show, setShow] = useState(false);
useEffect(() => { setShow(getConsent() === null); }, []);
if (!show) return null;
const choose = (v: "accepted" | "declined") => { setConsent(v); setShow(false); };
return (
<div className="fixed bottom-0 inset-x-0 z-40 p-3 sm:p-4 pointer-events-none" role="region" aria-label="Cookie consent">
<div className="pointer-events-auto max-w-3xl mx-auto bg-white border shadow-lg rounded-xl p-4 flex flex-col sm:flex-row sm:items-center gap-3">
<Cookie className="w-6 h-6 text-brand-600 shrink-0 hidden sm:block" />
<p className="text-sm text-gray-700 flex-1">
May we count your visit? We use our own anonymous statistics (no third-party analytics) to see how people find and register for events.{" "}
<Link href="/legal/privacy#cookies" className="text-brand-700 underline">Privacy policy</Link>
</p>
<div className="flex gap-2 shrink-0">
<button onClick={() => choose("declined")} className="px-3 py-1.5 text-sm rounded-lg border hover:bg-gray-50">No thanks</button>
<button onClick={() => choose("accepted")} className="px-3 py-1.5 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700">Allow</button>
</div>
</div>
</div>
);
}