import type { TrackingCode } from "./payload"; // Klaro's own config shape (kiprotect/klaro, see node_modules/klaro's // dist/config.js for the fully-annotated reference) — only the fields we // actually set are typed here, not Klaro's full surface. export type KlaroConfig = { version: number; elementID: string; styling: Record; storageMethod: "cookie"; cookieName: string; cookieExpiresAfterDays: number; default: boolean; mustConsent: boolean; acceptAll: boolean; hideDeclineAll: boolean; noticeAsModal: boolean; translations: Record>; services: { name: string; title: string; purposes: string[]; required: boolean; default: boolean; onlyOnce: boolean; callback: (consent: boolean) => void; }[]; }; export function serviceName(code: TrackingCode): string { return `tracking-code-${code.id}`; } function providerTitle(code: TrackingCode): string { switch (code.provider) { case "google-analytics": return "Google Analytics"; case "facebook-pixel": return "Facebook Pixel"; case "google-tag-manager": return "Google Tag Manager"; default: return "Sonstiges Tracking-Skript"; } } // `onAccept` is threaded in rather than importing loadTrackingCode.ts // directly here — this module only builds a plain config object (no DOM // access), kept separate from the actual script-injection side effect so // it stays trivially testable/reusable if the loading mechanism ever // changes. export function buildKlaroConfig(codes: TrackingCode[], onAccept: (code: TrackingCode) => void): KlaroConfig { const serviceTranslations: Record = {}; for (const code of codes) serviceTranslations[serviceName(code)] = { title: providerTitle(code) }; return { version: 1, elementID: "klaro", // 'light' + 'bottom' + 'left' — a compact corner notice (Klaro's own // default max-width, ~400px), not a full-width bar. 'wide' (tried // first) stretched it across nearly the whole viewport, which read as // too dominant compared to a typical bottom-left corner CMP. // Extra keys beyond `theme` override individual CSS custom properties // (Klaro's injectStyles() applies any non-'theme' `styling` key as a // `--` var, see node_modules/klaro's own utils/styling.js) — this // is the actual brand-color pass: dark1/light1 are (confusingly // named) the notice's background/text colors, not a dark-mode toggle; // green1 colors the accept button, red1 the decline button (set // neutral gray, not alarming red — this site's own secondary buttons // are plain-bordered, never red, see NotifyMeForm.tsx). styling: { theme: ["light", "bottom", "left"], dark1: "#fffdf8", // --color-bg-paper — notice/modal background dark2: "#e5e0d8", // --color-border dark3: "#f3efe9", light1: "#1a1a18", // --color-text-primary — notice/modal text light2: "#6b6b69", // --color-text-muted light3: "#1a1a18", green1: "#f6a701", // --color-brand — accept button green2: "#d99000", red1: "#e5e0d8", // decline button — neutral bordered look, not red red2: "#d1cabf", "button-text-color": "#1a1a18", "border-radius": "10px", "border-width": "1px", "font-family": "inherit", "notice-max-width": "380px", }, storageMethod: "cookie", cookieName: "klaro-consent", cookieExpiresAfterDays: 180, default: false, mustConsent: false, acceptAll: true, hideDeclineAll: false, noticeAsModal: false, translations: { de: { consentModal: { title: "Cookie-Einstellungen", description: "Hier kannst du einsehen und anpassen, welche Cookies/Skripte diese Seite verwendet.", }, consentNotice: { description: "Wir nutzen Cookies, um diese Seite zu betreiben und ihre Nutzung zu verstehen.", learnMore: "Einstellungen", }, purposes: { necessary: "Notwendig", analytics: "Analyse", marketing: "Marketing", }, ...serviceTranslations, }, }, services: codes.map((code) => ({ name: serviceName(code), title: providerTitle(code), purposes: [code.consentCategory], required: code.consentCategory === "necessary", default: code.consentCategory === "necessary", // A restock/page-reload shouldn't re-fire the loader for a service // the visitor already accepted in an earlier session within this // same page load — Klaro still calls `callback` once per accepted // service per page load either way, `onlyOnce` just governs repeat // toggling within one session. onlyOnce: true, callback: (consent: boolean) => { if (consent) onAccept(code); }, })), }; }