'use client' import React, { useState } from 'react' import { ChecklistView } from './ChecklistView' interface CheckItem { id: string label: string passed: boolean severity: string matched_text: string level?: number parent?: string | null skipped?: boolean hint?: string } interface BannerResult { banner_detected: boolean banner_provider: string banner_checks?: { violations: { code: string; text: string; severity: string }[] has_impressum_link?: boolean has_dse_link?: boolean } structured_checks?: CheckItem[] completeness_pct?: number correctness_pct?: number phases?: { before_consent: { cookies: string[]; scripts: string[]; tracking_services: string[]; violations: any[] } after_reject: { cookies: string[]; scripts: string[]; new_tracking: string[]; violations: any[] } after_accept: { cookies: string[]; scripts: string[]; new_tracking: string[]; undocumented: string[] } } email_status?: string } const CATEGORIES = [ { id: 'all', label: 'Alle Kategorien' }, { id: 'necessary', label: 'Notwendig' }, { id: 'statistics', label: 'Statistik' }, { id: 'marketing', label: 'Marketing' }, { id: 'functional', label: 'Funktional' }, { id: 'preferences', label: 'Praeferenzen' }, ] export function BannerCheckTab() { const [url, setUrl] = useState(() => typeof window !== 'undefined' ? localStorage.getItem('banner-check-url') || '' : '' ) const [loading, setLoading] = useState(false) const [progress, setProgress] = useState('') const [error, setError] = useState(null) const [result, setResult] = useState(() => { if (typeof window === 'undefined') return null try { const s = localStorage.getItem('banner-check-result'); return s ? JSON.parse(s) : null } catch { return null } }) const [categories, setCategories] = useState(['all']) const [useAgent, setUseAgent] = useState(false) const [mcResults, setMcResults] = useState(null) const [history, setHistory] = useState<{ url: string; date: string; provider: string; violations: number; pct: number; resultKey: string }[]>(() => { if (typeof window === 'undefined') return [] try { return JSON.parse(localStorage.getItem('banner-check-history') || '[]') } catch { return [] } }) // Persist URL React.useEffect(() => { localStorage.setItem('banner-check-url', url) }, [url]) const toggleCategory = (id: string) => { if (id === 'all') { setCategories(['all']) return } setCategories(prev => { const without = prev.filter(c => c !== 'all' && c !== id) const next = prev.includes(id) ? without : [...without, id] return next.length === 0 ? ['all'] : next }) } const handleScan = async (e: React.FormEvent) => { e.preventDefault() if (!url.trim()) return setLoading(true) setError(null) setResult(null) setProgress('Cookie-Banner wird analysiert...') const selectedCategories = categories.includes('all') ? [] : categories try { const res = await fetch('/api/sdk/v1/agent/banner-check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: url.trim(), categories: selectedCategories }), }) if (!res.ok) throw new Error(`Fehler: ${res.status}`) const data = await res.json() setResult(data) localStorage.setItem('banner-check-result', JSON.stringify(data)) // If agent mode: also run cookie doc-check with 381 MCs if (useAgent) { setProgress('KI-Agent prueft Cookie-Richtlinie (381 MCs)...') try { const mcRes = await fetch('/api/sdk/v1/agent/doc-check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ entries: [{ doc_type: 'cookie', label: 'Cookie-Richtlinie', url: url.trim() }], recipient: 'dsb@breakpilot.local', use_agent: true, }), }) if (mcRes.ok) { const { check_id } = await mcRes.json() if (check_id) { for (let i = 0; i < 60; i++) { await new Promise(r => setTimeout(r, 3000)) const poll = await fetch(`/api/sdk/v1/agent/doc-check?check_id=${check_id}`) if (!poll.ok) continue const pd = await poll.json() if (pd.progress) setProgress(`KI-Agent: ${pd.progress}`) if (pd.status === 'completed' && pd.result) { setMcResults(pd.result); break } if (pd.status === 'failed') break } } } } catch { /* agent check is optional */ } } // Add to history with persistent result const violations = data.structured_checks?.filter((c: CheckItem) => !c.passed && !c.skipped).length || 0 const resultKey = `banner-check-result-${Date.now()}` try { localStorage.setItem(resultKey, JSON.stringify(data)) } catch { /* quota */ } const entry = { url: url.trim(), date: new Date().toISOString(), provider: data.banner_provider || 'Unbekannt', violations, pct: data.completeness_pct ?? 0, resultKey, } const updated = [entry, ...history].slice(0, 30) setHistory(updated) localStorage.setItem('banner-check-history', JSON.stringify(updated)) } catch (e) { setError(e instanceof Error ? e.message : 'Unbekannter Fehler') } finally { setLoading(false) setProgress('') } } const loadFromHistory = (entry: { url: string; resultKey?: string }) => { setUrl(entry.url) if (entry.resultKey) { try { const saved = localStorage.getItem(entry.resultKey) if (saved) { setResult(JSON.parse(saved)); return } } catch {} } // Fallback: load last result try { const last = localStorage.getItem('banner-check-result') if (last) setResult(JSON.parse(last)) } catch {} } const structuredChecks = result?.structured_checks || [] const hasStructured = structuredChecks.length > 0 const compPct = result?.completeness_pct ?? 0 const corrPct = result?.correctness_pct ?? 0 const checklistResults = hasStructured ? [{ label: `Cookie-Banner: ${result?.banner_provider || 'Unbekannt'}`, url: url, doc_type: 'banner', word_count: 0, completeness_pct: compPct, correctness_pct: corrPct, checks: structuredChecks, findings_count: structuredChecks.filter(c => !c.passed && !c.skipped).length, error: '', }] : [] return (

Cookie-Banner Compliance Check

Playwright-basierter 3-Phasen-Test: Vor Interaktion, nach Ablehnen, nach Akzeptieren. Prueft Dark Patterns, Pre-Consent-Cookies, Farbkontrast, Klick-Paritaet und 36 weitere Kriterien.

setUrl(e.target.value)} placeholder="https://www.example.com/" className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent text-sm" disabled={loading} required />
{CATEGORIES.map(cat => ( ))}
{progress && (
{progress}
)} {error && (
{error}
)} {result && (
{result.phases && (
{result.banner_detected ? '🛡️' : '⚠️'}

{result.banner_detected ? `Banner erkannt: ${result.banner_provider || 'Unbekannter Anbieter'}` : 'Kein Cookie-Banner erkannt'}

3-Phasen-Analyse: Cookies und Scripts vor/nach Interaktion

)} {hasStructured && (
)} {result.email_status && (
E-Mail: {result.email_status === 'sent' ? 'Gesendet' : result.email_status}
)} {/* MC Agent Results (Cookie-Richtlinie) */} {mcResults?.results && (

KI-Agent: Cookie-Richtlinie (381 MCs)

)} {!result.banner_detected && !hasStructured && (

Kein Cookie-Banner auf dieser Seite gefunden. Falls Cookies gesetzt werden, ist ein Banner nach §25 TDDDG Pflicht.

)}
)} {/* History */} {history.length > 0 && (

Letzte Banner-Checks

{history.map((h, i) => ( ))}
)}
) } function PhaseBox({ label, icon, cookies, scripts, violations }: { label: string; icon: string; cookies: number; scripts: number; violations: number }) { return (
{icon}
{label}
{cookies} Cookies, {scripts} Scripts
{violations > 0 &&
{violations} Verstoesse
}
) }