'use client' /** * CookieLibraryPanel — Pro-Cookie-Abgleich gegen die Knowledge-Library: * findet als „notwendig" deklarierte Tracker + fehlende Zwecke und zeigt je * Befund die Abstellmaßnahme. Lädt aus dem Snapshot (kein Re-Crawl). */ import React, { useEffect, useState } from 'react' export interface CookieFinding { vendor: string cookie: string type: string severity: string declared: string library_purpose: string remediation: string control?: { control_id?: string | null; regulation?: string; article?: string } } interface CheckData { summary?: { checked?: number; in_library?: number; findings?: number } findings?: CookieFinding[] storage_inventory?: { total?: number by_type?: Record real_cookies?: number other_storage?: number } drift?: { declared_count?: number browser_count?: number high_findings?: number low_findings?: number } } const SEV_COLOR: Record = { HIGH: 'bg-red-100 text-red-700', MEDIUM: 'bg-amber-100 text-amber-700', LOW: 'bg-blue-100 text-blue-700', } const TYPE_LABEL: Record = { tracker_as_necessary: 'Tracker als „notwendig" deklariert', missing_purpose: 'Zweck fehlt', excessive_lifetime: 'Speicherdauer zu lang', vague_duration: 'Speicherdauer nicht konkret', missing_retention: 'Keine Speicherdauer/Löschfrist', third_country: 'Drittland-Transfer', eu_alternative: 'EU-Alternative verfügbar', storage_transparency: 'Speichertyp nicht transparent', } const STORAGE_LABEL: Record = { cookie: 'Cookies', local_storage: 'Local Storage', session_storage: 'Session Storage', indexeddb: 'IndexedDB', framework_storage: 'Framework-Storage', } // Pure, testbar. export function CookieFindingList({ data }: { data: CheckData }) { const findings = data.findings || [] const s = data.summary || {} const inv = data.storage_inventory const drift = data.drift const driftShown = !!drift && ((drift.declared_count ?? 0) + (drift.browser_count ?? 0)) > 0 return (
{driftShown && (
Richtlinie ↔ Realität:{' '} {drift!.declared_count ?? 0} in der Cookie-Richtlinie dokumentiert · {drift!.browser_count ?? 0} im Browser geladen {(drift!.high_findings ?? 0) > 0 && ( <> · {drift!.high_findings} undokumentiert geladen )} {(drift!.low_findings ?? 0) > 0 && ( <> · {drift!.low_findings} dokumentiert, aber nicht geladen )}
)} {inv && (inv.total ?? 0) > 0 && (
Storage-Inventar:{' '} {inv.total} als „Cookies" gelistet →{' '} {inv.real_cookies} echte Cookies {(inv.other_storage ?? 0) > 0 && ( <> + {inv.other_storage} andere Endgeräte-Speicher )} {inv.by_type && ( ({Object.entries(inv.by_type) .map(([k, n]) => `${n} ${STORAGE_LABEL[k] || k}`) .join(' · ')}) )}
)}
Library-Abgleich — {findings.length} Befund{findings.length !== 1 ? 'e' : ''} {s.in_library ?? 0}/{s.checked ?? 0} Cookies in der Library erkannt
{findings.length === 0 ? (
Keine Abweichungen gegen die Library.
) : (
{findings.map((f, i) => (
{f.severity} {f.cookie} · {f.vendor} {TYPE_LABEL[f.type] || f.type}
{f.library_purpose && (
Library-Zweck: {f.library_purpose}
)}
{f.remediation}
{f.control?.regulation && f.control.regulation !== '—' && (
Rechtsgrundlage: {f.control.regulation} {f.control.article} {f.control.control_id && ` · Control ${f.control.control_id}`}
)}
))}
)}
) } export function CookieLibraryPanel( { snapshotId, data: provided }: { snapshotId: string; data?: CheckData }, ) { const [data, setData] = useState(provided ?? null) const [loading, setLoading] = useState(!provided) useEffect(() => { if (provided) { setData(provided); setLoading(false); return } let cancelled = false fetch(`/api/sdk/v1/agent/snapshots/${snapshotId}/cookie-check`) .then(r => r.json()) .then(d => { if (!cancelled) setData(d) }) .catch(() => { if (!cancelled) setData({ findings: [] }) }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [snapshotId, provided]) if (loading) return
Library-Abgleich läuft…
return }