From bb2ebd03cd6af20c16ea51abc855dc9eb251a5fb Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Sun, 3 May 2026 08:44:00 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20CMP=20Dashboard=20=E2=80=94=20aggregate?= =?UTF-8?q?d=20consent,=20DSR,=20and=20compliance=20overview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New route /sdk/cmp with full CMP dashboard - 4 KPI cards: total consents, active consents, open DSR requests, configured sites - Cookie category acceptance bars (necessary/statistics/marketing/functional) - DSR breakdown: by status, by type (Art. 15-21), avg processing time, overdue count - 9-point compliance checklist (banner, DSE, impressum, Art.7 proof, DSR, loeschfristen, vendor AVV, email templates, EWR-only mode) — each links to relevant module - 8 module cards with icons linking to all CMP sub-modules - Real API integration: /banner/admin/stats, /einwilligungen/consents/stats, /dsr/stats - Dashboard link added as first entry in CMP sidebar section Co-Authored-By: Claude Opus 4.6 (1M context) --- admin-compliance/app/sdk/cmp/page.tsx | 263 ++++++++++++++++++ .../sdk/Sidebar/SidebarModuleList.tsx | 1 + 2 files changed, 264 insertions(+) create mode 100644 admin-compliance/app/sdk/cmp/page.tsx diff --git a/admin-compliance/app/sdk/cmp/page.tsx b/admin-compliance/app/sdk/cmp/page.tsx new file mode 100644 index 0000000..0dfe429 --- /dev/null +++ b/admin-compliance/app/sdk/cmp/page.tsx @@ -0,0 +1,263 @@ +'use client' + +import { useState, useEffect } from 'react' +import Link from 'next/link' + +/** + * CMP Dashboard — Consent Management Platform overview. + * + * Aggregates data from: Banner API, Einwilligungen, DSR, Vendors. + * State-of-the-art layout inspired by OneTrust/Cookiebot dashboards + * but with EWR-Only as unique differentiator. + */ + +const API_BASE = typeof window !== 'undefined' + ? (process.env.NEXT_PUBLIC_SDK_URL || `${window.location.protocol}//${window.location.hostname}:8093`) + : '' +const TENANT_ID = '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e' +const HEADERS = { 'X-Tenant-ID': TENANT_ID } + +interface BannerStats { total_consents: number; category_acceptance: Record } +interface ConsentStats { total_consents: number; active_consents: number; revoked_consents: number; unique_users: number; conversion_rate: number } +interface DSRStats { total: number; by_status: Record; by_type: Record; overdue: number; due_this_week: number; average_processing_days: number; completed_this_month: number } + +const MODULES = [ + { href: '/sdk/cookie-banner', label: 'Cookie-Banner', desc: 'Banner konfigurieren und Code exportieren', icon: 'shield', color: 'purple' }, + { href: '/sdk/cookie-banner/preview', label: 'Live-Vorschau', desc: 'Banner auf simulierter Website testen', icon: 'eye', color: 'blue' }, + { href: '/sdk/einwilligungen', label: 'Consent-Records', desc: 'Einwilligungen einsehen und verwalten', icon: 'clipboard', color: 'green' }, + { href: '/sdk/consent-management', label: 'Consent-Verwaltung', desc: 'Dokument-Lifecycle und DSGVO-Prozesse', icon: 'folder', color: 'indigo' }, + { href: '/sdk/vendor-compliance', label: 'Vendor-Compliance', desc: 'Dienstleister und Auftragsverarbeitung', icon: 'users', color: 'amber' }, + { href: '/sdk/dsr', label: 'DSR Portal', desc: 'Betroffenenrechte Art. 15-21 DSGVO', icon: 'user', color: 'rose' }, + { href: '/sdk/loeschfristen', label: 'Loeschfristen', desc: 'Aufbewahrungsrichtlinien verwalten', icon: 'clock', color: 'teal' }, + { href: '/sdk/email-templates', label: 'E-Mail-Templates', desc: 'Benachrichtigungsvorlagen', icon: 'mail', color: 'slate' }, +] + +const ICON_MAP: Record = { + shield: , + eye: <>, + clipboard: , + folder: , + users: , + user: , + clock: , + mail: , +} + +const COLOR_MAP: Record = { + purple: 'bg-purple-100 text-purple-600', blue: 'bg-blue-100 text-blue-600', + green: 'bg-green-100 text-green-600', indigo: 'bg-indigo-100 text-indigo-600', + amber: 'bg-amber-100 text-amber-600', rose: 'bg-rose-100 text-rose-600', + teal: 'bg-teal-100 text-teal-600', slate: 'bg-slate-100 text-slate-600', +} + +export default function CMPDashboardPage() { + const [bannerStats, setBannerStats] = useState(null) + const [consentStats, setConsentStats] = useState(null) + const [dsrStats, setDSRStats] = useState(null) + const [sites, setSites] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + async function load() { + const f = (url: string) => fetch(`${API_BASE}${url}`, { headers: HEADERS }).then(r => r.ok ? r.json() : null).catch(() => null) + const [banner, consent, dsr, siteList] = await Promise.all([ + f('/banner/admin/stats/preview-test-site'), + f('/einwilligungen/consents/stats'), + f('/dsr/stats'), + f('/banner/admin/sites'), + ]) + setBannerStats(banner) + setConsentStats(consent) + setDSRStats(dsr) + setSites(siteList || []) + setLoading(false) + } + load() + }, []) + + const totalConsents = (bannerStats?.total_consents || 0) + (consentStats?.total_consents || 0) + const dsrOpen = dsrStats ? (dsrStats.by_status?.intake || 0) + (dsrStats.by_status?.processing || 0) + (dsrStats.by_status?.identity_verification || 0) : 0 + const dsrOverdue = dsrStats?.overdue || 0 + const catAcceptance = bannerStats?.category_acceptance || {} + + return ( +
+ {/* Header */} +
+
+

Consent Management Platform

+

Ueberblick ueber Einwilligungen, Betroffenenrechte und Vendor-Compliance

+
+ + Banner testen + +
+ + {/* KPI Cards */} +
+ + + 0 ? `${dsrOverdue} ueberfaellig` : null} trendColor={dsrOverdue > 0 ? 'red' : 'green'} /> + +
+ + {/* Category Acceptance + DSR Breakdown */} +
+ {/* Cookie Category Acceptance */} +
+

Cookie-Kategorie Akzeptanz

+ {Object.keys(catAcceptance).length > 0 ? ( +
+ {Object.entries(catAcceptance).map(([cat, data]) => ( +
+ {cat} +
+
+
+ {data.rate}% + {data.count}x +
+ ))} +
+ ) : ( +
+

Noch keine Consent-Daten vorhanden

+ + Jetzt Banner testen + +
+ )} +
+ + {/* DSR Breakdown */} +
+
+

Betroffenenrechte (DSR)

+ Alle anzeigen +
+ {dsrStats && dsrStats.total > 0 ? ( +
+
+ + + 0 ? 'red' : 'gray'} /> +
+
+
Nach Typ
+
+ {Object.entries(dsrStats.by_type || {}).filter(([, v]) => v > 0).map(([type, count]) => ( +
+ {DSR_TYPE_LABELS[type] || type} + {count} +
+ ))} +
+
+ {dsrStats.average_processing_days > 0 && ( +
+ Durchschnittl. Bearbeitungszeit + {dsrStats.average_processing_days.toFixed(1)} Tage +
+ )} +
+ ) : ( +
+ Keine DSR-Anfragen vorhanden +
+ )} +
+
+ + {/* Compliance Status */} +
+

Compliance-Status

+

Pruefung der wichtigsten DSGVO-Anforderungen

+
+ 0} href="/sdk/cookie-banner" /> + + + 0} href="/sdk/einwilligungen" /> + + + + + +
+
+ + {/* Module Grid */} +
+

CMP Module

+
+ {MODULES.map(m => ( + +
+ + {ICON_MAP[m.icon]} + +
+
{m.label}
+
{m.desc}
+ + ))} +
+
+
+ ) +} + +const DSR_TYPE_LABELS: Record = { + access: 'Auskunft (Art. 15)', rectification: 'Berichtigung (Art. 16)', + erasure: 'Loeschung (Art. 17)', restriction: 'Einschraenkung (Art. 18)', + portability: 'Portabilitaet (Art. 20)', objection: 'Widerspruch (Art. 21)', +} + +function KPICard({ label, value, icon, trend, trendColor }: { + label: string; value: number | string; icon: string; trend: string | null; trendColor?: string +}) { + return ( +
+
{label}
+
{value}
+ {trend && ( +
+ {trend} +
+ )} +
+ ) +} + +function MiniStat({ label, value, color }: { label: string; value: number; color?: string }) { + const c = color === 'red' ? 'text-red-600' : color === 'green' ? 'text-green-600' : 'text-gray-900' + return ( +
+
{value}
+
{label}
+
+ ) +} + +function ComplianceCheck({ label, ok, href }: { label: string; ok: boolean; href: string }) { + return ( + + {ok ? ( + + + + ) : ( + + + + )} + {label} + + ) +} diff --git a/admin-compliance/components/sdk/Sidebar/SidebarModuleList.tsx b/admin-compliance/components/sdk/Sidebar/SidebarModuleList.tsx index a3aedfa..5c5b19f 100644 --- a/admin-compliance/components/sdk/Sidebar/SidebarModuleList.tsx +++ b/admin-compliance/components/sdk/Sidebar/SidebarModuleList.tsx @@ -27,6 +27,7 @@ export function SidebarModuleList({ collapsed, projectId, pendingCRCount }: Side CMP
)} + } label="Dashboard" isActive={pathname === '/sdk/cmp'} collapsed={collapsed} projectId={projectId} /> } label="Cookie-Banner" isActive={pathname?.startsWith('/sdk/cookie-banner') ?? false} collapsed={collapsed} projectId={projectId} /> } label="Live-Vorschau" isActive={pathname === '/sdk/cookie-banner/preview'} collapsed={collapsed} projectId={projectId} /> } label="Consent-Records" isActive={pathname?.startsWith('/sdk/einwilligungen') ?? false} collapsed={collapsed} projectId={projectId} />