import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { fetchConfig } from './api'; import { useCustomerAuth } from './customer/CustomerAuthContext'; import { brevoPage, startBrevoTracking, stopBrevoTracking } from './brevo'; /** * Drives the Brevo tracker from the two things that decide whether it may run * at all: the environment's key and the signed-in customer's consent (#56). * * Renders nothing. It exists as a component rather than a hook because it needs * both the router and the auth context, and mounting it inside `AppRoutes` is * what guarantees it sits under both — a hook called from the wrong place would * fail at runtime instead of being impossible to misplace. * * ## Why the gate is a server-computed field * * `analytics_consent` is not `marketing_consent`. The consent sentence was * widened to mention analytics, and the server reports this flag by comparing * the wording each customer actually agreed to against the current text. A * customer who consented to the older, email-only wording is not tracked. Never * substitute `marketing_consent` here — that is the exact retroactive widening * the field exists to prevent. * * A signed-out visitor has no consent record, so no key is ever used and the * script is never injected. Anonymous browsing is not reported at all. */ export default function BrevoTracking() { const { customer } = useCustomerAuth(); const location = useLocation(); const [trackerKey, setTrackerKey] = useState(null); useEffect(() => { let cancelled = false; // Swallowed for the same reason main.tsx swallows its warm-up call: a // config that cannot be fetched is a broken deployment every other request // will report, and losing analytics is not worth surfacing to a customer. void fetchConfig() .then(config => { if (!cancelled) setTrackerKey(config.brevoTrackerKey); }) .catch(() => {}); return () => { cancelled = true; }; }, []); const consented = customer?.analytics_consent === true; const email = customer?.email; // Start and stop are driven by the same effect so there is no state in which // consent has gone away and nothing has acted on it. Withdrawing consent, // signing out, and deleting the account all arrive here as `consented` // turning false, because each one ends with the customer no longer being a // consenting signed-in customer. useEffect(() => { if (consented && trackerKey && email) { startBrevoTracking(trackerKey, email); } else { stopBrevoTracking(); } }, [consented, trackerKey, email]); // Declared after the effect above so that on the render where consent or the // key first arrives, tracking is started before this reports the route. // // The storefront is a single-page app: without this, the tracker's own // initial call would be the only page view it ever saw, and every customer // would look like they viewed one page and left. useEffect(() => { brevoPage(location.pathname); }, [location.pathname, consented, trackerKey]); return null; }