From d3c8811fdb037dd7b67c60f88c19847e7a1d6a2d Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Mon, 4 May 2026 07:01:37 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20IAB=20TCF=202.2=20=E2=80=94=20TC=20Stri?= =?UTF-8?q?ng=20encoder=20+=20purpose=20mapping=20+=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TCFEncoderService: generates base64url-encoded TC Strings per IAB spec with 12 purposes, vendor consent bitfield, CMP metadata - Category-to-purpose mapping (necessary→none, statistics→1,7,8,9,10, marketing→1,2,3,4,5,6,7,12, functional→1,11) - tcf_routes: 5 endpoints (purposes, features, mapping, encode, encode-categories) - banner_consent_service: auto-generates TC String when tcf_enabled=true - TCFSettings.tsx: enable/disable toggle, purpose grid with category mapping, TC String test generator, CMP registration info - New "TCF/IAB" tab in cookie-banner page (7 tabs total) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cookie-banner/_components/TCFSettings.tsx | 161 ++++++++++++++ .../app/sdk/cookie-banner/page.tsx | 18 +- backend-compliance/compliance/api/__init__.py | 1 + .../compliance/api/tcf_routes.py | 95 ++++++++ .../services/banner_consent_service.py | 22 ++ .../services/tcf_encoder_service.py | 209 ++++++++++++++++++ 6 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 admin-compliance/app/sdk/cookie-banner/_components/TCFSettings.tsx create mode 100644 backend-compliance/compliance/api/tcf_routes.py create mode 100644 backend-compliance/compliance/services/tcf_encoder_service.py diff --git a/admin-compliance/app/sdk/cookie-banner/_components/TCFSettings.tsx b/admin-compliance/app/sdk/cookie-banner/_components/TCFSettings.tsx new file mode 100644 index 0000000..6448309 --- /dev/null +++ b/admin-compliance/app/sdk/cookie-banner/_components/TCFSettings.tsx @@ -0,0 +1,161 @@ +'use client' + +import { useState, useEffect } from 'react' + +interface IABPurpose { + id: number + name: string + name_de: string +} + +const API = '/api/sdk/v1/compliance/tcf' + +export function TCFSettings({ siteId, tcfEnabled, onToggle }: { + siteId?: string + tcfEnabled: boolean + onToggle: (enabled: boolean) => void +}) { + const [purposes, setPurposes] = useState([]) + const [categoryMap, setCategoryMap] = useState>({}) + const [testResult, setTestResult] = useState(null) + const [testing, setTesting] = useState(false) + + useEffect(() => { + Promise.all([ + fetch(`${API}/purposes`).then(r => r.ok ? r.json() : []), + fetch(`${API}/category-mapping`).then(r => r.ok ? r.json() : {}), + ]).then(([p, m]) => { + setPurposes(p) + setCategoryMap(m) + }).catch(() => {}) + }, []) + + const handleTestEncode = async () => { + setTesting(true) + setTestResult(null) + try { + const res = await fetch(`${API}/encode-categories`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ categories: ['necessary', 'statistics', 'marketing'] }), + }) + if (res.ok) { + const data = await res.json() + setTestResult(`TC String: ${data.tc_string}\nPurposes: ${data.purposes_consented.join(', ')}`) + } + } catch { setTestResult('Fehler beim Generieren') } + setTesting(false) + } + + return ( +
+ {/* Enable/Disable */} +
+
+
+

IAB TCF 2.2

+

+ Transparency & Consent Framework — Standardisierte Einwilligungssignale fuer programmatische Werbung +

+
+ +
+ {!tcfEnabled && ( +

+ TCF ist nur erforderlich wenn Sie programmatische Werbung (AdTech) einsetzen. + Fuer die meisten Websites reicht das Standard-Cookie-Banner. +

+ )} +
+ + {tcfEnabled && ( + <> + {/* IAB Purposes */} +
+

12 IAB-Zwecke (Purposes)

+

+ Diese Zwecke werden automatisch aus Ihren Cookie-Kategorien abgeleitet. +

+
+ {purposes.map(p => { + const activeCats = Object.entries(categoryMap) + .filter(([, pids]) => pids.includes(p.id)) + .map(([cat]) => cat) + return ( +
0 ? 'bg-green-50' : 'bg-gray-50'}`}> + 0 ? 'bg-green-500 text-white' : 'bg-gray-300 text-white'}`}> + {p.id} + +
+
{p.name_de}
+ {activeCats.length > 0 && ( +
via: {activeCats.join(', ')}
+ )} +
+
+ ) + })} +
+
+ + {/* Category Mapping */} +
+

Kategorie → Purpose Zuordnung

+
+ {Object.entries(categoryMap).map(([cat, pids]) => ( +
+ {cat} +
+ {pids.length === 0 ? ( + Keine Einwilligung noetig + ) : ( + pids.map(pid => ( + + Purpose {pid} + + )) + )} +
+
+ ))} +
+
+ + {/* TC String Test */} +
+

TC String testen

+ + {testResult && ( +
+                {testResult}
+              
+ )} +

+ Simuliert: necessary + statistics + marketing → generiert base64url-codierten TC String +

+
+ + {/* CMP Registration Info */} +
+

CMP-Registrierung

+

+ Fuer den produktiven Einsatz muss Ihr CMP bei der IAB Europe registriert werden. + Sie erhalten eine eindeutige CMP-ID die im TC String codiert wird. +

+

+ Registrierung: iabeurope.eu/tcf-for-cmps +

+
+ + )} +
+ ) +} diff --git a/admin-compliance/app/sdk/cookie-banner/page.tsx b/admin-compliance/app/sdk/cookie-banner/page.tsx index c2196c5..cfcf14e 100644 --- a/admin-compliance/app/sdk/cookie-banner/page.tsx +++ b/admin-compliance/app/sdk/cookie-banner/page.tsx @@ -11,8 +11,9 @@ import { EmbeddableVendorHTML } from './_components/EmbeddableVendorHTML' import { SiteSelector } from './_components/SiteSelector' import { AnalyticsDashboard } from './_components/AnalyticsDashboard' import { ABTestPanel } from './_components/ABTestPanel' +import { TCFSettings } from './_components/TCFSettings' -type BannerTab = 'config' | 'vendors' | 'embed' | 'analytics' | 'abtest' +type BannerTab = 'config' | 'vendors' | 'embed' | 'analytics' | 'abtest' | 'tcf' export default function CookieBannerPage() { const { state } = useSDK() @@ -79,6 +80,7 @@ export default function CookieBannerPage() { { id: 'embed' as const, label: 'Einbettung' }, { id: 'analytics' as const, label: 'Analytik' }, { id: 'abtest' as const, label: 'A/B-Test' }, + { id: 'tcf' as const, label: 'TCF/IAB' }, ]).map(tab => (