Initial commit: breakpilot-compliance - Compliance SDK Platform

Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Boenisch
2026-02-11 23:47:28 +01:00
commit 4435e7ea0a
734 changed files with 251369 additions and 0 deletions

View File

@@ -0,0 +1,596 @@
'use client'
/**
* UCCA System Documentation Page (SDK Version)
*
* Displays architecture documentation, auditor information,
* and transparency data for the UCCA compliance system.
*/
import { useState, useEffect } from 'react'
import Link from 'next/link'
// ============================================================================
// Types
// ============================================================================
type DocTab = 'overview' | 'architecture' | 'auditor' | 'rules' | 'legal-corpus'
interface Rule {
code: string
category: string
title: string
description: string
severity: string
gdpr_ref: string
rationale?: string
risk_add?: number
}
interface Pattern {
id: string
title: string
description: string
benefit?: string
effort?: string
risk_reduction?: number
}
interface Control {
id: string
title: string
description: string
gdpr_ref?: string
effort?: string
}
// ============================================================================
// API Configuration
// ============================================================================
const API_BASE = process.env.NEXT_PUBLIC_SDK_API_URL || 'https://macmini:8090'
// ============================================================================
// Main Component
// ============================================================================
export default function DocumentationPage() {
const [activeTab, setActiveTab] = useState<DocTab>('overview')
const [rules, setRules] = useState<Rule[]>([])
const [patterns, setPatterns] = useState<Pattern[]>([])
const [controls, setControls] = useState<Control[]>([])
const [policyVersion, setPolicyVersion] = useState<string>('')
const [loading, setLoading] = useState(false)
useEffect(() => {
const fetchData = async () => {
setLoading(true)
try {
const rulesRes = await fetch(`${API_BASE}/sdk/v1/ucca/rules`, {
headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' }
})
if (rulesRes.ok) {
const rulesData = await rulesRes.json()
setRules(rulesData.rules || [])
setPolicyVersion(rulesData.policy_version || '')
}
const patternsRes = await fetch(`${API_BASE}/sdk/v1/ucca/patterns`, {
headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' }
})
if (patternsRes.ok) {
const patternsData = await patternsRes.json()
setPatterns(patternsData.patterns || [])
}
const controlsRes = await fetch(`${API_BASE}/sdk/v1/ucca/controls`, {
headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' }
})
if (controlsRes.ok) {
const controlsData = await controlsRes.json()
setControls(controlsData.controls || [])
}
} catch (error) {
console.error('Failed to fetch documentation data:', error)
} finally {
setLoading(false)
}
}
fetchData()
}, [])
// ============================================================================
// Tab Content Renderers
// ============================================================================
const renderOverview = () => (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-white rounded-xl border border-slate-200 p-6 shadow-sm">
<h3 className="font-semibold text-slate-800 mb-2">Deterministische Regeln</h3>
<div className="text-3xl font-bold text-purple-600">{rules.length}</div>
<p className="text-sm text-slate-500 mt-2">
Alle Entscheidungen basieren auf transparenten, nachvollziehbaren Regeln.
</p>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6 shadow-sm">
<h3 className="font-semibold text-slate-800 mb-2">Architektur-Patterns</h3>
<div className="text-3xl font-bold text-green-600">{patterns.length}</div>
<p className="text-sm text-slate-500 mt-2">
Best-Practice-Loesungen fuer datenschutzkonforme KI-Systeme.
</p>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6 shadow-sm">
<h3 className="font-semibold text-slate-800 mb-2">Compliance-Kontrollen</h3>
<div className="text-3xl font-bold text-blue-600">{controls.length}</div>
<p className="text-sm text-slate-500 mt-2">
Technische und organisatorische Massnahmen.
</p>
</div>
</div>
<div className="bg-gradient-to-br from-purple-50 to-blue-50 rounded-xl border border-purple-200 p-6">
<h3 className="font-semibold text-purple-800 text-lg mb-4">Was ist UCCA?</h3>
<div className="prose prose-sm max-w-none text-slate-700">
<p>
<strong>UCCA (Use-Case Compliance & Feasibility Advisor)</strong> ist ein deterministisches
Compliance-Pruefwerkzeug, das Organisationen bei der Bewertung geplanter KI-Anwendungsfaelle
hinsichtlich ihrer datenschutzrechtlichen Zulaessigkeit unterstuetzt.
</p>
<h4 className="text-purple-700 mt-4">Kernprinzipien</h4>
<ul className="space-y-2">
<li>
<strong>Determinismus:</strong> Alle Entscheidungen basieren auf transparenten Regeln.
Die KI trifft KEINE autonomen Entscheidungen.
</li>
<li>
<strong>Transparenz:</strong> Alle Regeln, Kontrollen und Patterns sind einsehbar.
</li>
<li>
<strong>Human-in-the-Loop:</strong> Kritische Entscheidungen erfordern immer
menschliche Pruefung durch DSB oder Legal.
</li>
<li>
<strong>Rechtsgrundlage:</strong> Jede Regel referenziert konkrete DSGVO-Artikel.
</li>
</ul>
</div>
</div>
<div className="bg-amber-50 border border-amber-200 rounded-xl p-6">
<h3 className="font-semibold text-amber-800 mb-3">
Wichtiger Hinweis zur KI-Nutzung
</h3>
<p className="text-amber-700">
Das System verwendet KI (LLM) <strong>ausschliesslich zur Erklaerung</strong> bereits
getroffener Regelentscheidungen. Die eigentliche Compliance-Bewertung erfolgt
<strong> rein deterministisch</strong> durch die Policy Engine. BLOCK-Entscheidungen
koennen NICHT durch KI ueberschrieben werden.
</p>
</div>
</div>
)
const renderArchitecture = () => (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="font-semibold text-slate-800 text-lg mb-4">Systemarchitektur</h3>
<div className="bg-slate-900 text-green-400 p-6 rounded-lg font-mono text-sm overflow-x-auto">
<pre>{`
┌─────────────────────────────────────────────────────────────────────┐
│ Frontend (Next.js) │
│ admin-v2:3000/sdk/advisory-board │
└───────────────────────────────────┬─────────────────────────────────┘
│ HTTPS
┌─────────────────────────────────────────────────────────────────────┐
│ AI Compliance SDK (Go) │
│ Port 8090 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Policy Engine │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ YAML-basierte Regeln (ucca_policy_v1.yaml) │ │ │
│ │ │ ~45 Regeln in 7 Kategorien │ │ │
│ │ │ Deterministisch - Kein LLM in Entscheidungslogik │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │
│ │ │ Controls │ │ Patterns │ │ Examples │ │ │
│ │ │ Library │ │ Library │ │ Library │ │ │
│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ LLM Integration │ │ Legal RAG │──────┐ │
│ │ (nur Explain) │ │ Client │ │ │
│ └──────────────────┘ └──────────────────┘ │ │
└─────────────────────────────┬────────────────────┼──────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ Datenschicht │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ PostgreSQL │ │ Qdrant │ │
│ │ (Assessments, │ │ (Legal Corpus, │ │
│ │ Escalations) │ │ 2,274 Chunks) │ │
│ └────────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
`}</pre>
</div>
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-blue-50 rounded-lg border border-blue-200">
<h4 className="font-medium text-blue-800 mb-2">Datenfluss</h4>
<ol className="text-sm text-blue-700 list-decimal list-inside space-y-1">
<li>Benutzer beschreibt Use Case im Frontend</li>
<li>Policy Engine evaluiert gegen alle Regeln</li>
<li>Ergebnis mit Controls + Patterns zurueck</li>
<li>Optional: LLM erklaert das Ergebnis</li>
<li>Bei Risiko: Automatische Eskalation</li>
</ol>
</div>
<div className="p-4 bg-green-50 rounded-lg border border-green-200">
<h4 className="font-medium text-green-800 mb-2">Sicherheitsmerkmale</h4>
<ul className="text-sm text-green-700 list-disc list-inside space-y-1">
<li>TLS 1.3 Verschluesselung</li>
<li>RBAC mit Tenant-Isolation</li>
<li>JWT-basierte Authentifizierung</li>
<li>Audit-Trail aller Aktionen</li>
<li>Keine Rohtext-Speicherung (nur Hash)</li>
</ul>
</div>
</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="font-semibold text-slate-800 text-lg mb-4">Eskalations-Workflow</h3>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-slate-200">
<th className="text-left py-2 px-3 font-medium text-slate-600">Level</th>
<th className="text-left py-2 px-3 font-medium text-slate-600">Ausloeser</th>
<th className="text-left py-2 px-3 font-medium text-slate-600">Pruefer</th>
<th className="text-left py-2 px-3 font-medium text-slate-600">SLA</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-slate-100 bg-green-50">
<td className="py-2 px-3 font-medium text-green-700">E0</td>
<td className="py-2 px-3 text-slate-600">Nur INFO-Regeln, Risiko &lt; 20</td>
<td className="py-2 px-3 text-slate-600">Automatisch</td>
<td className="py-2 px-3 text-slate-600">-</td>
</tr>
<tr className="border-b border-slate-100 bg-yellow-50">
<td className="py-2 px-3 font-medium text-yellow-700">E1</td>
<td className="py-2 px-3 text-slate-600">WARN-Regeln, Risiko 20-40</td>
<td className="py-2 px-3 text-slate-600">Team-Lead</td>
<td className="py-2 px-3 text-slate-600">24h / 72h</td>
</tr>
<tr className="border-b border-slate-100 bg-orange-50">
<td className="py-2 px-3 font-medium text-orange-700">E2</td>
<td className="py-2 px-3 text-slate-600">Art. 9 Daten, DSFA empfohlen, Risiko 40-60</td>
<td className="py-2 px-3 text-slate-600">DSB</td>
<td className="py-2 px-3 text-slate-600">8h / 48h</td>
</tr>
<tr className="bg-red-50">
<td className="py-2 px-3 font-medium text-red-700">E3</td>
<td className="py-2 px-3 text-slate-600">BLOCK-Regeln, Art. 22, Risiko &gt; 60</td>
<td className="py-2 px-3 text-slate-600">DSB + Legal</td>
<td className="py-2 px-3 text-slate-600">4h / 24h</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
)
const renderAuditorInfo = () => (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="font-semibold text-slate-800 text-lg mb-4">
Dokumentation fuer externe Auditoren
</h3>
<p className="text-slate-600 mb-4">
Diese Dokumentation erfuellt die Anforderungen nach Art. 30 DSGVO (Verzeichnis von
Verarbeitungstaetigkeiten) und dient als Grundlage fuer Audits nach Art. 32 DSGVO.
</p>
<div className="space-y-4">
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<h4 className="font-medium text-slate-800 mb-2">1. Zweck des Systems</h4>
<p className="text-sm text-slate-600">
UCCA ist ein Compliance-Pruefwerkzeug zur Bewertung geplanter KI-Anwendungsfaelle
hinsichtlich ihrer datenschutzrechtlichen Zulaessigkeit.
</p>
</div>
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<h4 className="font-medium text-slate-800 mb-2">2. Rechtsgrundlage</h4>
<ul className="text-sm text-slate-600 list-disc list-inside space-y-1">
<li><strong>Art. 6 Abs. 1 lit. c DSGVO</strong> - Erfuellung rechtlicher Verpflichtungen</li>
<li><strong>Art. 6 Abs. 1 lit. f DSGVO</strong> - Berechtigte Interessen (Compliance-Management)</li>
</ul>
</div>
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<h4 className="font-medium text-slate-800 mb-2">3. Verarbeitete Datenkategorien</h4>
<div className="overflow-x-auto">
<table className="w-full text-sm mt-2">
<thead>
<tr className="border-b border-slate-200">
<th className="text-left py-2 px-2 font-medium text-slate-600">Kategorie</th>
<th className="text-left py-2 px-2 font-medium text-slate-600">Speicherung</th>
<th className="text-left py-2 px-2 font-medium text-slate-600">Aufbewahrung</th>
</tr>
</thead>
<tbody className="text-slate-600">
<tr className="border-b border-slate-100">
<td className="py-2 px-2">Use-Case-Beschreibung</td>
<td className="py-2 px-2">Nur Hash (SHA-256)</td>
<td className="py-2 px-2">10 Jahre</td>
</tr>
<tr className="border-b border-slate-100">
<td className="py-2 px-2">Bewertungsergebnis</td>
<td className="py-2 px-2">Vollstaendig</td>
<td className="py-2 px-2">10 Jahre</td>
</tr>
<tr className="border-b border-slate-100">
<td className="py-2 px-2">Audit-Trail</td>
<td className="py-2 px-2">Vollstaendig</td>
<td className="py-2 px-2">10 Jahre</td>
</tr>
<tr>
<td className="py-2 px-2">Eskalations-Historie</td>
<td className="py-2 px-2">Vollstaendig</td>
<td className="py-2 px-2">10 Jahre</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<h4 className="font-medium text-slate-800 mb-2">4. Keine autonomen KI-Entscheidungen</h4>
<p className="text-sm text-slate-600">
Das System trifft <strong>KEINE automatisierten Einzelentscheidungen</strong> im Sinne
von Art. 22 DSGVO, da:
</p>
<ul className="text-sm text-slate-600 list-disc list-inside mt-2 space-y-1">
<li>Regelauswertung ist keine rechtlich bindende Entscheidung</li>
<li>Alle kritischen Faelle werden menschlich geprueft (E1-E3)</li>
<li>BLOCK-Entscheidungen erfordern immer menschliche Freigabe</li>
<li>Betroffene haben Anfechtungsmoeglichkeit ueber Eskalation</li>
</ul>
</div>
<div className="p-4 bg-green-50 rounded-lg border border-green-200">
<h4 className="font-medium text-green-800 mb-2">5. Technische und Organisatorische Massnahmen</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<strong className="text-green-700">Vertraulichkeit</strong>
<ul className="text-green-700 list-disc list-inside mt-1">
<li>RBAC mit Tenant-Isolation</li>
<li>TLS 1.3 Verschluesselung</li>
<li>AES-256 at rest</li>
</ul>
</div>
<div>
<strong className="text-green-700">Integritaet</strong>
<ul className="text-green-700 list-disc list-inside mt-1">
<li>Unveraenderlicher Audit-Trail</li>
<li>Policy-Versionierung</li>
<li>Input-Validierung</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
)
const renderRulesTab = () => (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h3 className="font-semibold text-slate-800 text-lg">Regel-Katalog</h3>
<p className="text-sm text-slate-500">Policy Version: {policyVersion}</p>
</div>
<div className="text-sm text-slate-500">
{rules.length} Regeln insgesamt
</div>
</div>
{loading ? (
<div className="text-center py-8 text-slate-500">Lade Regeln...</div>
) : (
<div className="space-y-4">
{Array.from(new Set(rules.map(r => r.category))).map(category => (
<div key={category} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
<div className="px-4 py-3 bg-slate-50 border-b border-slate-200">
<h4 className="font-medium text-slate-800">{category}</h4>
<p className="text-xs text-slate-500">
{rules.filter(r => r.category === category).length} Regeln
</p>
</div>
<div className="divide-y divide-slate-100">
{rules.filter(r => r.category === category).map(rule => (
<div key={rule.code} className="p-4">
<div className="flex items-start justify-between gap-4">
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-mono text-sm text-slate-500">{rule.code}</span>
<span className={`text-xs px-2 py-0.5 rounded ${
rule.severity === 'BLOCK' ? 'bg-red-100 text-red-700' :
rule.severity === 'WARN' ? 'bg-yellow-100 text-yellow-700' :
'bg-blue-100 text-blue-700'
}`}>
{rule.severity}
</span>
</div>
<div className="font-medium text-slate-800 mt-1">{rule.title}</div>
<div className="text-sm text-slate-600 mt-1">{rule.description}</div>
{rule.gdpr_ref && (
<div className="text-xs text-slate-500 mt-2">{rule.gdpr_ref}</div>
)}
</div>
{rule.risk_add && (
<div className="text-sm font-medium text-red-600">
+{rule.risk_add}
</div>
)}
</div>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
)
const renderLegalCorpus = () => (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="font-semibold text-slate-800 text-lg mb-4">Legal RAG Corpus</h3>
<p className="text-slate-600 mb-4">
Das System verwendet einen semantischen Suchindex mit 2.274 Chunks aus 19 EU-Regulierungen
fuer rechtsgrundlagenbasierte Erklaerungen.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-blue-50 rounded-lg border border-blue-200">
<h4 className="font-medium text-blue-800 mb-2">Indexierte Regulierungen</h4>
<ul className="text-sm text-blue-700 space-y-1">
<li>DSGVO - Datenschutz-Grundverordnung</li>
<li>AI Act - EU KI-Verordnung</li>
<li>NIS2 - Cybersicherheits-Richtlinie</li>
<li>CRA - Cyber Resilience Act</li>
<li>Data Act - Datengesetz</li>
<li>DSA/DMA - Digital Services/Markets Act</li>
<li>DPF - EU-US Data Privacy Framework</li>
<li>BSI-TR-03161 - Digitale Identitaeten</li>
</ul>
</div>
<div className="p-4 bg-green-50 rounded-lg border border-green-200">
<h4 className="font-medium text-green-800 mb-2">RAG-Funktionalitaet</h4>
<ul className="text-sm text-green-700 space-y-1">
<li>Hybride Suche (Dense + BM25)</li>
<li>Semantisches Chunking</li>
<li>Cross-Encoder Reranking</li>
<li>Artikel-Referenz-Extraktion</li>
<li>Mehrsprachig (DE/EN)</li>
</ul>
</div>
</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="font-semibold text-slate-800 mb-4">Verwendung im System</h3>
<div className="space-y-3">
<div className="flex items-start gap-3">
<div className="w-8 h-8 bg-purple-100 text-purple-700 rounded-full flex items-center justify-center flex-shrink-0">
1
</div>
<div>
<div className="font-medium text-slate-800">Benutzer fordert Erklaerung an</div>
<div className="text-sm text-slate-600">
Nach der Bewertung kann eine LLM-basierte Erklaerung generiert werden.
</div>
</div>
</div>
<div className="flex items-start gap-3">
<div className="w-8 h-8 bg-purple-100 text-purple-700 rounded-full flex items-center justify-center flex-shrink-0">
2
</div>
<div>
<div className="font-medium text-slate-800">Legal RAG Client sucht relevante Artikel</div>
<div className="text-sm text-slate-600">
Basierend auf den ausgeloesten Regeln werden passende Gesetzestexte gefunden.
</div>
</div>
</div>
<div className="flex items-start gap-3">
<div className="w-8 h-8 bg-purple-100 text-purple-700 rounded-full flex items-center justify-center flex-shrink-0">
3
</div>
<div>
<div className="font-medium text-slate-800">LLM generiert Erklaerung mit Rechtsgrundlage</div>
<div className="text-sm text-slate-600">
Die Erklaerung referenziert konkrete Artikel aus DSGVO, AI Act etc.
</div>
</div>
</div>
</div>
</div>
</div>
)
// ============================================================================
// Tabs Configuration
// ============================================================================
const tabs: { id: DocTab; label: string }[] = [
{ id: 'overview', label: 'Uebersicht' },
{ id: 'architecture', label: 'Architektur' },
{ id: 'auditor', label: 'Fuer Auditoren' },
{ id: 'rules', label: 'Regel-Katalog' },
{ id: 'legal-corpus', label: 'Legal RAG' },
]
// ============================================================================
// Main Render
// ============================================================================
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div className="bg-white rounded-xl shadow-sm border p-6 flex-1">
<h1 className="text-2xl font-bold text-slate-900">UCCA System-Dokumentation</h1>
<p className="text-slate-500 mt-1">
Transparente Dokumentation des UCCA-Systems fuer Entwickler, Auditoren und Datenschutzbeauftragte.
</p>
</div>
<Link
href="/sdk/advisory-board"
className="ml-4 px-4 py-2 text-sm text-slate-600 hover:text-slate-800 border border-slate-200 rounded-lg hover:bg-slate-50"
>
Zurueck zum Advisory Board
</Link>
</div>
{/* Tab Navigation */}
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
<div className="flex border-b border-slate-200 overflow-x-auto">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex items-center gap-2 px-4 py-3 text-sm font-medium whitespace-nowrap transition-colors ${
activeTab === tab.id
? 'text-purple-600 border-b-2 border-purple-600 bg-purple-50'
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50'
}`}
>
{tab.label}
</button>
))}
</div>
<div className="p-6">
{activeTab === 'overview' && renderOverview()}
{activeTab === 'architecture' && renderArchitecture()}
{activeTab === 'auditor' && renderAuditorInfo()}
{activeTab === 'rules' && renderRulesTab()}
{activeTab === 'legal-corpus' && renderLegalCorpus()}
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,667 @@
'use client'
import React, { useState } from 'react'
import Link from 'next/link'
import { useSDK, UseCaseAssessment } from '@/lib/sdk'
// =============================================================================
// WIZARD STEPS
// =============================================================================
const WIZARD_STEPS = [
{ id: 1, name: 'Grunddaten', description: 'Name und Beschreibung des Use Cases' },
{ id: 2, name: 'Datenkategorien', description: 'Welche Daten werden verarbeitet?' },
{ id: 3, name: 'Technologie', description: 'Eingesetzte KI-Technologien' },
{ id: 4, name: 'Risikobewertung', description: 'Erste Risikoeinschätzung' },
{ id: 5, name: 'Zusammenfassung', description: 'Überprüfung und Abschluss' },
]
// =============================================================================
// USE CASE CARD
// =============================================================================
function UseCaseCard({
useCase,
isActive,
onSelect,
onDelete,
}: {
useCase: UseCaseAssessment
isActive: boolean
onSelect: () => void
onDelete: () => void
}) {
const completionPercent = Math.round((useCase.stepsCompleted / 5) * 100)
return (
<div
className={`relative bg-white rounded-xl border-2 p-6 transition-all cursor-pointer ${
isActive ? 'border-purple-500 shadow-lg' : 'border-gray-200 hover:border-purple-300'
}`}
onClick={onSelect}
>
{/* Delete Button */}
<button
onClick={e => {
e.stopPropagation()
onDelete()
}}
className="absolute top-4 right-4 p-1 text-gray-400 hover:text-red-500 transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<div className="flex items-start gap-4">
<div
className={`w-12 h-12 rounded-xl flex items-center justify-center ${
completionPercent === 100
? 'bg-green-100 text-green-600'
: 'bg-purple-100 text-purple-600'
}`}
>
{completionPercent === 100 ? (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
) : (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
)}
</div>
<div className="flex-1 min-w-0">
<h3 className="text-lg font-semibold text-gray-900 truncate">{useCase.name}</h3>
<p className="text-sm text-gray-500 line-clamp-2">{useCase.description}</p>
<div className="mt-3">
<div className="flex items-center justify-between text-sm mb-1">
<span className="text-gray-500">Fortschritt</span>
<span className="font-medium">{completionPercent}%</span>
</div>
<div className="h-2 bg-gray-100 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all ${
completionPercent === 100 ? 'bg-green-500' : 'bg-purple-600'
}`}
style={{ width: `${completionPercent}%` }}
/>
</div>
</div>
{useCase.assessmentResult && (
<div className="mt-3 flex items-center gap-2">
<span
className={`px-2 py-1 text-xs rounded-full ${
useCase.assessmentResult.riskLevel === 'CRITICAL'
? 'bg-red-100 text-red-700'
: useCase.assessmentResult.riskLevel === 'HIGH'
? 'bg-orange-100 text-orange-700'
: useCase.assessmentResult.riskLevel === 'MEDIUM'
? 'bg-yellow-100 text-yellow-700'
: 'bg-green-100 text-green-700'
}`}
>
Risiko: {useCase.assessmentResult.riskLevel}
</span>
{useCase.assessmentResult.dsfaRequired && (
<span className="px-2 py-1 text-xs bg-purple-100 text-purple-700 rounded-full">
DSFA erforderlich
</span>
)}
</div>
)}
</div>
</div>
</div>
)
}
// =============================================================================
// WIZARD
// =============================================================================
interface WizardFormData {
name: string
description: string
category: string
dataCategories: string[]
processesPersonalData: boolean
specialCategories: boolean
aiTechnologies: string[]
dataVolume: string
riskLevel: string
notes: string
}
function UseCaseWizard({
onComplete,
onCancel,
}: {
onComplete: (useCase: UseCaseAssessment) => void
onCancel: () => void
}) {
const [currentStep, setCurrentStep] = useState(1)
const [formData, setFormData] = useState<WizardFormData>({
name: '',
description: '',
category: '',
dataCategories: [],
processesPersonalData: false,
specialCategories: false,
aiTechnologies: [],
dataVolume: 'medium',
riskLevel: 'medium',
notes: '',
})
const updateFormData = (updates: Partial<WizardFormData>) => {
setFormData(prev => ({ ...prev, ...updates }))
}
const handleNext = () => {
if (currentStep < 5) {
setCurrentStep(prev => prev + 1)
} else {
// Create use case
const newUseCase: UseCaseAssessment = {
id: `uc-${Date.now()}`,
name: formData.name,
description: formData.description,
category: formData.category,
stepsCompleted: 5,
steps: WIZARD_STEPS.map(s => ({
id: `step-${s.id}`,
name: s.name,
completed: true,
data: {},
})),
assessmentResult: {
riskLevel: formData.riskLevel as 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL',
applicableRegulations: ['DSGVO', 'AI Act'],
recommendedControls: ['Datenschutz-Folgenabschätzung', 'Technische Maßnahmen'],
dsfaRequired: formData.specialCategories || formData.riskLevel === 'HIGH',
aiActClassification: formData.aiTechnologies.length > 0 ? 'LIMITED' : 'MINIMAL',
},
createdAt: new Date(),
updatedAt: new Date(),
}
onComplete(newUseCase)
}
}
const handleBack = () => {
if (currentStep > 1) {
setCurrentStep(prev => prev - 1)
}
}
return (
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
{/* Header */}
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold text-gray-900">Neuer Use Case</h2>
<button onClick={onCancel} className="text-gray-400 hover:text-gray-600">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* Progress */}
<div className="mt-4 flex items-center gap-2">
{WIZARD_STEPS.map((step, index) => (
<React.Fragment key={step.id}>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${
step.id < currentStep
? 'bg-green-500 text-white'
: step.id === currentStep
? 'bg-purple-600 text-white'
: 'bg-gray-200 text-gray-500'
}`}
>
{step.id < currentStep ? (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
) : (
step.id
)}
</div>
{index < WIZARD_STEPS.length - 1 && (
<div
className={`flex-1 h-1 rounded ${
step.id < currentStep ? 'bg-green-500' : 'bg-gray-200'
}`}
/>
)}
</React.Fragment>
))}
</div>
<p className="mt-2 text-sm text-gray-500">
Schritt {currentStep}: {WIZARD_STEPS[currentStep - 1].description}
</p>
</div>
{/* Content */}
<div className="p-6">
{currentStep === 1 && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Name des Use Cases *</label>
<input
type="text"
value={formData.name}
onChange={e => updateFormData({ name: e.target.value })}
placeholder="z.B. Marketing-KI für Kundensegmentierung"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Beschreibung *</label>
<textarea
value={formData.description}
onChange={e => updateFormData({ description: e.target.value })}
placeholder="Beschreiben Sie den Anwendungsfall und den Geschäftszweck..."
rows={4}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Kategorie</label>
<select
value={formData.category}
onChange={e => updateFormData({ category: e.target.value })}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
<option value="">Kategorie wählen...</option>
<option value="marketing">Marketing & Vertrieb</option>
<option value="hr">Personal & HR</option>
<option value="finance">Finanzen & Controlling</option>
<option value="operations">Betrieb & Produktion</option>
<option value="customer">Kundenservice</option>
<option value="other">Sonstiges</option>
</select>
</div>
</div>
)}
{currentStep === 2 && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Werden personenbezogene Daten verarbeitet?
</label>
<div className="flex items-center gap-4">
<label className="flex items-center gap-2">
<input
type="radio"
checked={formData.processesPersonalData}
onChange={() => updateFormData({ processesPersonalData: true })}
className="w-4 h-4 text-purple-600"
/>
<span>Ja</span>
</label>
<label className="flex items-center gap-2">
<input
type="radio"
checked={!formData.processesPersonalData}
onChange={() => updateFormData({ processesPersonalData: false })}
className="w-4 h-4 text-purple-600"
/>
<span>Nein</span>
</label>
</div>
</div>
{formData.processesPersonalData && (
<>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Welche Datenkategorien? (Mehrfachauswahl)
</label>
<div className="grid grid-cols-2 gap-2">
{['Name/Kontakt', 'E-Mail', 'Adresse', 'Telefon', 'Geburtsdatum', 'Finanzdaten', 'Standort', 'Nutzungsverhalten'].map(
cat => (
<label key={cat} className="flex items-center gap-2 p-2 border rounded-lg hover:bg-gray-50">
<input
type="checkbox"
checked={formData.dataCategories.includes(cat)}
onChange={e => {
if (e.target.checked) {
updateFormData({ dataCategories: [...formData.dataCategories, cat] })
} else {
updateFormData({
dataCategories: formData.dataCategories.filter(c => c !== cat),
})
}
}}
className="w-4 h-4 text-purple-600"
/>
<span className="text-sm">{cat}</span>
</label>
)
)}
</div>
</div>
<div>
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={formData.specialCategories}
onChange={e => updateFormData({ specialCategories: e.target.checked })}
className="w-4 h-4 text-purple-600"
/>
<span className="text-sm font-medium text-gray-700">
Besondere Kategorien (Art. 9 DSGVO): Gesundheit, Biometrie, Religion, etc.
</span>
</label>
{formData.specialCategories && (
<p className="mt-2 text-sm text-amber-600 bg-amber-50 p-3 rounded-lg">
Bei besonderen Kategorien ist eine DSFA in der Regel erforderlich!
</p>
)}
</div>
</>
)}
</div>
)}
{currentStep === 3 && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Eingesetzte KI-Technologien (Mehrfachauswahl)
</label>
<div className="grid grid-cols-2 gap-2">
{[
'Machine Learning',
'Deep Learning',
'Natural Language Processing',
'Computer Vision',
'Generative AI (LLM)',
'Empfehlungssysteme',
'Predictive Analytics',
'Chatbots/Assistenten',
].map(tech => (
<label key={tech} className="flex items-center gap-2 p-2 border rounded-lg hover:bg-gray-50">
<input
type="checkbox"
checked={formData.aiTechnologies.includes(tech)}
onChange={e => {
if (e.target.checked) {
updateFormData({ aiTechnologies: [...formData.aiTechnologies, tech] })
} else {
updateFormData({
aiTechnologies: formData.aiTechnologies.filter(t => t !== tech),
})
}
}}
className="w-4 h-4 text-purple-600"
/>
<span className="text-sm">{tech}</span>
</label>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Erwartetes Datenvolumen</label>
<select
value={formData.dataVolume}
onChange={e => updateFormData({ dataVolume: e.target.value })}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
<option value="small">Klein (&lt; 1.000 Datensätze)</option>
<option value="medium">Mittel (1.000 - 100.000 Datensätze)</option>
<option value="large">Groß (100.000 - 1 Mio. Datensätze)</option>
<option value="xlarge">Sehr groß (&gt; 1 Mio. Datensätze)</option>
</select>
</div>
</div>
)}
{currentStep === 4 && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Erste Risikoeinschätzung
</label>
<div className="space-y-2">
{[
{ value: 'low', label: 'Niedrig', description: 'Keine personenbezogenen Daten, kein kritischer Einsatz' },
{ value: 'medium', label: 'Mittel', description: 'Personenbezogene Daten, aber kein kritischer Einsatz' },
{ value: 'high', label: 'Hoch', description: 'Besondere Kategorien oder automatisierte Entscheidungen' },
{ value: 'critical', label: 'Kritisch', description: 'Hochrisiko-KI nach AI Act' },
].map(option => (
<label
key={option.value}
className={`flex items-start gap-3 p-4 border rounded-lg cursor-pointer transition-colors ${
formData.riskLevel === option.value
? 'border-purple-500 bg-purple-50'
: 'hover:bg-gray-50'
}`}
>
<input
type="radio"
checked={formData.riskLevel === option.value}
onChange={() => updateFormData({ riskLevel: option.value })}
className="mt-1 w-4 h-4 text-purple-600"
/>
<div>
<div className="font-medium">{option.label}</div>
<div className="text-sm text-gray-500">{option.description}</div>
</div>
</label>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Notizen</label>
<textarea
value={formData.notes}
onChange={e => updateFormData({ notes: e.target.value })}
placeholder="Zusätzliche Anmerkungen zur Risikobewertung..."
rows={3}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
)}
{currentStep === 5 && (
<div className="space-y-6">
<div className="bg-gray-50 rounded-lg p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Zusammenfassung</h3>
<dl className="space-y-3">
<div className="flex justify-between">
<dt className="text-gray-500">Name:</dt>
<dd className="font-medium text-gray-900">{formData.name || '-'}</dd>
</div>
<div className="flex justify-between">
<dt className="text-gray-500">Kategorie:</dt>
<dd className="font-medium text-gray-900">{formData.category || '-'}</dd>
</div>
<div className="flex justify-between">
<dt className="text-gray-500">Personenbezogene Daten:</dt>
<dd className="font-medium text-gray-900">
{formData.processesPersonalData ? 'Ja' : 'Nein'}
</dd>
</div>
{formData.processesPersonalData && (
<div className="flex justify-between">
<dt className="text-gray-500">Datenkategorien:</dt>
<dd className="font-medium text-gray-900">{formData.dataCategories.join(', ') || '-'}</dd>
</div>
)}
<div className="flex justify-between">
<dt className="text-gray-500">KI-Technologien:</dt>
<dd className="font-medium text-gray-900">{formData.aiTechnologies.join(', ') || '-'}</dd>
</div>
<div className="flex justify-between">
<dt className="text-gray-500">Risikostufe:</dt>
<dd
className={`font-medium px-2 py-0.5 rounded ${
formData.riskLevel === 'critical'
? 'bg-red-100 text-red-700'
: formData.riskLevel === 'high'
? 'bg-orange-100 text-orange-700'
: formData.riskLevel === 'medium'
? 'bg-yellow-100 text-yellow-700'
: 'bg-green-100 text-green-700'
}`}
>
{formData.riskLevel.toUpperCase()}
</dd>
</div>
</dl>
</div>
{(formData.specialCategories || formData.riskLevel === 'high' || formData.riskLevel === 'critical') && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4">
<div className="flex items-start gap-3">
<svg
className="w-5 h-5 text-amber-600 mt-0.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div>
<h4 className="font-medium text-amber-800">DSFA erforderlich</h4>
<p className="text-sm text-amber-700 mt-1">
Basierend auf Ihrer Eingabe wird eine Datenschutz-Folgenabschätzung empfohlen.
</p>
</div>
</div>
</div>
)}
</div>
)}
</div>
{/* Footer */}
<div className="px-6 py-4 border-t border-gray-200 bg-gray-50 flex items-center justify-between">
<button
onClick={currentStep === 1 ? onCancel : handleBack}
className="px-4 py-2 text-gray-600 hover:text-gray-900"
>
{currentStep === 1 ? 'Abbrechen' : 'Zurück'}
</button>
<button
onClick={handleNext}
disabled={currentStep === 1 && !formData.name}
className={`px-6 py-2 rounded-lg font-medium transition-colors ${
currentStep === 1 && !formData.name
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
: 'bg-purple-600 text-white hover:bg-purple-700'
}`}
>
{currentStep === 5 ? 'Abschließen' : 'Weiter'}
</button>
</div>
</div>
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function AdvisoryBoardPage() {
const { state, dispatch } = useSDK()
const [showWizard, setShowWizard] = useState(false)
const handleCreateUseCase = (useCase: UseCaseAssessment) => {
dispatch({ type: 'ADD_USE_CASE', payload: useCase })
dispatch({ type: 'SET_ACTIVE_USE_CASE', payload: useCase.id })
setShowWizard(false)
}
const handleDeleteUseCase = (id: string) => {
if (confirm('Möchten Sie diesen Use Case wirklich löschen?')) {
dispatch({ type: 'DELETE_USE_CASE', payload: id })
}
}
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">Use Case Workshop</h1>
<p className="mt-1 text-gray-500">
Erfassen Sie Ihre KI-Anwendungsfälle und erhalten Sie eine erste Compliance-Bewertung
</p>
</div>
<div className="flex items-center gap-3">
<Link
href="/sdk/advisory-board/documentation"
className="inline-flex items-center gap-2 px-4 py-2 text-sm text-purple-600 hover:text-purple-700 hover:bg-purple-50 border border-purple-300 rounded-lg transition-colors"
>
UCCA-System Dokumentation ansehen
</Link>
{!showWizard && (
<button
onClick={() => setShowWizard(true)}
className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
Neuer Use Case
</button>
)}
</div>
</div>
{/* Wizard or List */}
{showWizard ? (
<UseCaseWizard onComplete={handleCreateUseCase} onCancel={() => setShowWizard(false)} />
) : state.useCases.length === 0 ? (
<div className="bg-white rounded-xl border border-gray-200 p-12 text-center">
<div className="w-16 h-16 mx-auto bg-purple-100 rounded-full flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
/>
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900">Noch keine Use Cases</h3>
<p className="mt-2 text-gray-500 max-w-md mx-auto">
Erstellen Sie Ihren ersten Use Case, um mit dem Compliance Assessment zu beginnen.
</p>
<button
onClick={() => setShowWizard(true)}
className="mt-6 px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
>
Ersten Use Case erstellen
</button>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{state.useCases.map(useCase => (
<UseCaseCard
key={useCase.id}
useCase={useCase}
isActive={state.activeUseCase === useCase.id}
onSelect={() => dispatch({ type: 'SET_ACTIVE_USE_CASE', payload: useCase.id })}
onDelete={() => handleDeleteUseCase(useCase.id)}
/>
))}
</div>
)}
</div>
)
}