refactor(admin): split advisory-board page.tsx into colocated components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-14 23:02:35 +02:00
parent eeb9931d87
commit 554320770a
16 changed files with 1048 additions and 898 deletions

View File

@@ -0,0 +1,54 @@
'use client'
import React from 'react'
interface Props {
currentStep: number
isSubmitting: boolean
isEditMode: boolean
titleEmpty: boolean
onBack: () => void
onNext: () => void
onSubmit: () => void
}
export function NavigationButtons({ currentStep, isSubmitting, isEditMode, titleEmpty, onBack, onNext, onSubmit }: Props) {
return (
<div className="flex items-center justify-between">
<button
onClick={onBack}
className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
>
{currentStep === 1 ? 'Abbrechen' : 'Zurueck'}
</button>
{currentStep < 8 ? (
<button
onClick={onNext}
disabled={currentStep === 1 && titleEmpty}
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50"
>
Weiter
</button>
) : (
<button
onClick={onSubmit}
disabled={isSubmitting || titleEmpty}
className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
>
{isSubmitting ? (
<>
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
Bewerte...
</>
) : (
isEditMode ? 'Speichern & neu bewerten' : 'Assessment starten'
)}
</button>
)}
</div>
)
}

View File

@@ -0,0 +1,40 @@
'use client'
import React from 'react'
import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard'
interface Props {
result: unknown
onGoToAssessment: (id: string) => void
onGoToOverview: () => void
}
export function ResultView({ result, onGoToAssessment, onGoToOverview }: Props) {
const r = result as { assessment?: { id: string }; result?: Record<string, unknown> }
return (
<div className="max-w-4xl mx-auto space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">Assessment Ergebnis</h1>
<div className="flex gap-2">
{r.assessment?.id && (
<button
onClick={() => onGoToAssessment(r.assessment!.id)}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
>
Zum Assessment
</button>
)}
<button
onClick={onGoToOverview}
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200"
>
Zur Uebersicht
</button>
</div>
</div>
{r.result && (
<AssessmentResultCard result={r.result as unknown as Parameters<typeof AssessmentResultCard>[0]['result']} />
)}
</div>
)
}

View File

@@ -0,0 +1,76 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { AI_USE_CATEGORIES } from '../_data'
interface Props extends StepProps {
profileIndustry: string | string[] | undefined
}
export function Step1Basics({ form, updateForm, profileIndustry }: Props) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Grundlegende Informationen</h2>
{/* Branche aus Profil (nur Anzeige) */}
{profileIndustry && (Array.isArray(profileIndustry) ? profileIndustry.length > 0 : true) && (
<div className="bg-gray-50 rounded-lg border border-gray-200 px-4 py-3">
<span className="text-xs font-medium text-gray-500 uppercase tracking-wide">Branche (aus Unternehmensprofil)</span>
<p className="text-sm text-gray-900 mt-0.5">
{Array.isArray(profileIndustry) ? profileIndustry.join(', ') : profileIndustry}
</p>
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Titel des Anwendungsfalls</label>
<input
type="text"
value={form.title}
onChange={e => updateForm({ title: e.target.value })}
placeholder="z.B. Chatbot fuer Kundenservice"
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={form.use_case_text}
onChange={e => updateForm({ use_case_text: e.target.value })}
rows={4}
placeholder="Beschreiben Sie den Anwendungsfall..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
{/* KI-Anwendungskategorie als Kacheln */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
In welchem Bereich kommt KI zum Einsatz?
</label>
<p className="text-sm text-gray-500 mb-3">Waehlen Sie die passende Kategorie fuer Ihren Anwendungsfall.</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{AI_USE_CATEGORIES.map(cat => (
<button
key={cat.value}
type="button"
onClick={() => updateForm({ category: cat.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.category === cat.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{cat.icon}</span>
<span className="text-sm font-medium text-gray-900">{cat.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{cat.desc}</p>
</button>
))}
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,94 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { DATA_CATEGORY_GROUPS } from '../_data-categories'
import { toggleInArray } from '../_data'
export function Step2DataCategories({ form, updateForm }: StepProps) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Welche Daten werden verarbeitet?</h2>
<p className="text-sm text-gray-500">Waehlen Sie alle Datenkategorien, die in diesem Use Case verarbeitet werden.</p>
{DATA_CATEGORY_GROUPS.map(group => (
<div key={group.group}>
<h3 className={`text-sm font-semibold mb-2 ${group.art9 ? 'text-orange-700' : 'text-gray-700'}`}>
{group.art9 && '⚠️ '}{group.group}
</h3>
{group.art9 && (
<p className="text-xs text-orange-600 mb-2">Besonders schutzwuerdig erhoehte Anforderungen an Rechtsgrundlage und TOM</p>
)}
<div className="grid grid-cols-2 md:grid-cols-3 gap-2 mb-4">
{group.items.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ data_categories: toggleInArray(form.data_categories, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.data_categories.includes(item.value)
? group.art9
? 'border-orange-500 bg-orange-50 ring-1 ring-orange-300'
: 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
))}
{/* Sonstige Datentypen */}
<div className="border border-gray-200 rounded-lg p-4 space-y-3">
<div className="font-medium text-gray-900">Sonstige Datentypen</div>
<p className="text-sm text-gray-500">
Falls Ihre Datenkategorie oben nicht aufgefuehrt ist, koennen Sie sie hier ergaenzen.
</p>
{form.custom_data_types.map((dt, idx) => (
<div key={idx} className="flex items-center gap-2">
<input
type="text"
value={dt}
onChange={e => {
const updated = [...form.custom_data_types]
updated[idx] = e.target.value
updateForm({ custom_data_types: updated })
}}
placeholder="Datentyp eingeben..."
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<button
onClick={() => updateForm({ custom_data_types: form.custom_data_types.filter((_, i) => i !== idx) })}
className="p-2 text-red-500 hover:bg-red-50 rounded-lg"
title="Entfernen"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
))}
<button
onClick={() => updateForm({ custom_data_types: [...form.custom_data_types, ''] })}
className="flex items-center gap-1 text-sm text-purple-600 hover:text-purple-700 font-medium"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
Weiteren Datentyp hinzufuegen
</button>
</div>
{form.data_categories.length > 0 && (
<div className="bg-purple-50 border border-purple-200 rounded-lg px-4 py-3 text-sm text-purple-800">
<span className="font-medium">{form.data_categories.length}</span> Datenkategorie{form.data_categories.length !== 1 ? 'n' : ''} ausgewaehlt
{form.data_categories.some(c => DATA_CATEGORY_GROUPS.find(g => g.art9)?.items.some(i => i.value === c)) && (
<span className="ml-2 text-orange-700 font-medium"> inkl. besonderer Kategorien (Art. 9)</span>
)}
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,49 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { PURPOSE_TILES } from '../_tiles'
import { toggleInArray } from '../_data'
export function Step3Purposes({ form, updateForm }: StepProps) {
return (
<div className="space-y-4">
<h2 className="text-lg font-semibold text-gray-900">Zweck der Verarbeitung</h2>
<p className="text-sm text-gray-500">Waehlen Sie alle zutreffenden Verarbeitungszwecke. Die passende Rechtsgrundlage wird vom SDK automatisch ermittelt.</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{PURPOSE_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ purposes: toggleInArray(form.purposes, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.purposes.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
{form.purposes.includes('profiling') && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 text-sm text-amber-800">
<div className="font-medium mb-1">Hinweis: Profiling</div>
<p>Profiling unterliegt besonderen Anforderungen nach Art. 22 DSGVO. Betroffene haben das Recht auf Information und Widerspruch.</p>
</div>
)}
{form.purposes.includes('automated_decision') && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 text-sm text-red-800">
<div className="font-medium mb-1">Achtung: Automatisierte Entscheidung</div>
<p>Art. 22 DSGVO: Vollautomatisierte Entscheidungen mit rechtlicher Wirkung erfordern besondere Schutzmassnahmen, Informationspflichten und das Recht auf menschliche Ueberpruefung.</p>
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,47 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { AUTOMATION_TILES } from '../_tiles'
export function Step4Automation({ form, updateForm }: StepProps) {
return (
<div className="space-y-4">
<h2 className="text-lg font-semibold text-gray-900">Grad der Automatisierung</h2>
<p className="text-sm text-gray-600">
Wie stark greift die KI in Entscheidungen ein? Je hoeher der Automatisierungsgrad, desto strenger die regulatorischen Anforderungen.
</p>
<div className="grid grid-cols-1 gap-3">
{AUTOMATION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ automation: item.value })}
className={`p-4 rounded-xl border-2 text-left transition-all ${
form.automation === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-3 mb-1">
<span className="text-2xl">{item.icon}</span>
<span className="text-sm font-semibold text-gray-900">{item.label}</span>
</div>
<p className="text-sm text-gray-500 ml-11">{item.desc}</p>
<p className="text-xs text-gray-400 ml-11 mt-1">Beispiele: {item.examples}</p>
</button>
))}
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-sm text-blue-800">
<div className="font-medium mb-1">Warum ist das wichtig?</div>
<p>
Art. 22 DSGVO regelt automatisierte Einzelentscheidungen. Vollautomatisierte Systeme, die Personen
erheblich beeinflussen (z.B. Kreditvergabe, Bewerbungsauswahl), unterliegen strengen Auflagen:
Informationspflicht, Recht auf menschliche Ueberpruefung und Anfechtungsmoeglichkeit.
</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,105 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { HOSTING_PROVIDER_TILES, HOSTING_REGION_TILES, MODEL_USAGE_TILES } from '../_tiles'
import { toggleInArray } from '../_data'
export function Step5Hosting({ form, updateForm }: StepProps) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Technische Details</h2>
{/* Hosting Provider */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Hosting-Anbieter</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{HOSTING_PROVIDER_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ hosting_provider: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.hosting_provider === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Hosting Region */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Hosting-Region</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{HOSTING_REGION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ hosting_region: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.hosting_region === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Model Usage */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-1">Wie wird das KI-Modell genutzt?</h3>
<p className="text-sm text-gray-500 mb-3">Waehlen Sie alle zutreffenden Optionen.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{MODEL_USAGE_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ model_usage: toggleInArray(form.model_usage, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.model_usage.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Info-Box: Begriffe erklaert */}
<details className="bg-amber-50 border border-amber-200 rounded-lg overflow-hidden">
<summary className="px-4 py-3 text-sm font-medium text-amber-800 cursor-pointer hover:bg-amber-100">
Begriffe erklaert: ML, DL, NLP, LLM &mdash; Was bedeutet das?
</summary>
<div className="px-4 pb-4 space-y-3 text-sm text-amber-900">
<div><span className="font-semibold">ML (Machine Learning)</span> &mdash; Computer lernt Muster aus Daten. Beispiel: Spam-Filter.</div>
<div><span className="font-semibold">DL (Deep Learning)</span> &mdash; ML mit neuronalen Netzen. Beispiel: Bilderkennung, Spracherkennung.</div>
<div><span className="font-semibold">NLP (Natural Language Processing)</span> &mdash; KI versteht Sprache. Beispiel: ChatGPT, DeepL.</div>
<div><span className="font-semibold">LLM (Large Language Model)</span> &mdash; Grosses Sprachmodell. Beispiel: GPT-4, Claude, Llama.</div>
<div><span className="font-semibold">RAG</span> &mdash; LLM erhaelt Kontext aus eigener Datenbank. Vorteil: Aktuelle, firmenspezifische Antworten.</div>
<div><span className="font-semibold">Fine-Tuning</span> &mdash; Bestehendes Modell mit eigenen Daten weitertrainieren. Achtung: Daten werden Teil des Modells.</div>
</div>
</details>
</div>
)
}

View File

@@ -0,0 +1,83 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { TRANSFER_TARGET_TILES, TRANSFER_MECHANISM_TILES } from '../_tiles'
import { toggleInArray } from '../_data'
export function Step6Transfer({ form, updateForm }: StepProps) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Internationaler Datentransfer</h2>
<p className="text-sm text-gray-500">Wohin werden die Daten uebermittelt? Waehlen Sie alle zutreffenden Ziellaender/-regionen.</p>
{/* Transfer Targets */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Datentransfer-Ziele</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{TRANSFER_TARGET_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ transfer_targets: toggleInArray(form.transfer_targets, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.transfer_targets.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Transfer Mechanism — only if not "no_transfer" only */}
{form.transfer_targets.length > 0 && !form.transfer_targets.every(t => t === 'no_transfer') && (
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Transfer-Mechanismus</h3>
<p className="text-sm text-gray-500 mb-3">Welche Schutzgarantie nutzen Sie fuer den Drittlandtransfer?</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{TRANSFER_MECHANISM_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ transfer_mechanism: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.transfer_mechanism === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
)}
{/* Specific countries text input */}
{form.transfer_targets.some(t => !['no_transfer'].includes(t)) && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Konkrete Ziellaender (optional)</label>
<input
type="text"
value={form.transfer_countries.join(', ')}
onChange={e => updateForm({ transfer_countries: e.target.value.split(',').map(s => s.trim()).filter(Boolean) })}
placeholder="z.B. USA, UK, Schweiz, Japan"
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<p className="text-xs text-gray-500 mt-1">Kommagetrennte Laendernamen oder -kuerzel</p>
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,55 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { RETENTION_TILES } from '../_tiles'
export function Step7Retention({ form, updateForm }: StepProps) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Datenhaltung & Aufbewahrung</h2>
<p className="text-sm text-gray-500">Wie lange sollen die Daten gespeichert werden?</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{RETENTION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ retention_period: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.retention_period === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Zweck der Aufbewahrung (optional)
</label>
<textarea
value={form.retention_purpose}
onChange={e => updateForm({ retention_purpose: e.target.value })}
rows={2}
placeholder="z.B. Vertragliche Pflichten, gesetzliche Aufbewahrungsfristen..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
{form.retention_period === 'indefinite' && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 text-sm text-amber-800">
<div className="font-medium mb-1">Hinweis: Unbefristete Speicherung</div>
<p>Die DSGVO fordert Datenminimierung und Speicherbegrenzung (Art. 5 Abs. 1e). Unbefristete Speicherung muss besonders gut begruendet sein.</p>
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,49 @@
'use client'
import React from 'react'
import type { StepProps } from '../_types'
import { CONTRACT_TILES } from '../_tiles'
import { toggleInArray } from '../_data'
export function Step8Contracts({ form, updateForm }: StepProps) {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Vertraege & Compliance-Dokumentation</h2>
<p className="text-sm text-gray-500">Welche Compliance-Dokumente liegen bereits vor? (Mehrfachauswahl moeglich)</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{CONTRACT_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ contracts: toggleInArray(form.contracts, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.contracts.includes(item.value)
? item.value === 'none'
? 'border-amber-500 bg-amber-50 ring-1 ring-amber-300'
: 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Subprozessoren (optional)</label>
<textarea
value={form.subprocessors}
onChange={e => updateForm({ subprocessors: e.target.value })}
rows={2}
placeholder="z.B. OpenAI (USA, SCC), Hetzner Cloud (DE)..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,36 @@
'use client'
import React from 'react'
import { WIZARD_STEPS } from '../_data'
interface Props {
currentStep: number
onStepClick: (id: number) => void
}
export function StepIndicator({ currentStep, onStepClick }: Props) {
return (
<div className="flex items-center gap-2">
{WIZARD_STEPS.map((step, idx) => (
<React.Fragment key={step.id}>
<button
onClick={() => onStepClick(step.id)}
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors ${
currentStep === step.id
? 'bg-purple-600 text-white'
: currentStep > step.id
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-500'
}`}
>
<span className="w-6 h-6 rounded-full bg-white/20 flex items-center justify-center text-xs font-bold">
{currentStep > step.id ? '✓' : step.id}
</span>
<span className="hidden md:inline">{step.title}</span>
</button>
{idx < WIZARD_STEPS.length - 1 && <div className="flex-1 h-px bg-gray-200" />}
</React.Fragment>
))}
</div>
)
}

View File

@@ -0,0 +1,112 @@
// =============================================================================
// DATA CATEGORIES (Step 2) — grouped tile selection
// =============================================================================
export const DATA_CATEGORY_GROUPS = [
{
group: 'Stamm- & Kontaktdaten',
items: [
{ value: 'basic_identity', label: 'Name & Identitaet', icon: '👤', desc: 'Vor-/Nachname, Geburtsdatum, Geschlecht' },
{ value: 'contact_data', label: 'Kontaktdaten', icon: '📧', desc: 'E-Mail, Telefon, Fax' },
{ value: 'address_data', label: 'Adressdaten', icon: '🏠', desc: 'Wohn-/Meldeadresse, PLZ, Lieferadresse' },
{ value: 'government_ids', label: 'Ausweisdaten', icon: '🪪', desc: 'Personalausweis-Nr., Reisepass, Fuehrerschein' },
{ value: 'customer_ids', label: 'Kundennummern', icon: '🏷️', desc: 'Kunden-ID, Vertrags-Nr., Mitgliedsnummer' },
],
},
{
group: 'Besondere Kategorien (Art. 9 DSGVO)',
art9: true,
items: [
{ value: 'health_data', label: 'Gesundheitsdaten', icon: '🏥', desc: 'Diagnosen, Medikation, AU, Pflegegrad' },
{ value: 'biometric_data', label: 'Biometrische Daten', icon: '🔐', desc: 'Fingerabdruck, Gesichtserkennung, Iris-Scan' },
{ value: 'genetic_data', label: 'Genetische Daten', icon: '🧬', desc: 'DNA-Profil, Genomsequenzen, Erbkrankheitstests' },
{ value: 'racial_ethnic', label: 'Ethnische Herkunft', icon: '🌍', desc: 'Rassische/ethnische Zugehoerigkeit' },
{ value: 'political_opinions', label: 'Politische Meinungen', icon: '🗳️', desc: 'Politische Ueberzeugungen, Parteizugehoerigkeit' },
{ value: 'religious_beliefs', label: 'Religion', icon: '🕊️', desc: 'Religionszugehoerigkeit, Weltanschauung' },
{ value: 'trade_union', label: 'Gewerkschaft', icon: '🤝', desc: 'Gewerkschaftsmitgliedschaft' },
{ value: 'sexual_orientation', label: 'Sexuelle Orientierung', icon: '🏳️‍🌈', desc: 'Sexualleben und Orientierung' },
],
},
{
group: 'Finanz- & Steuerdaten',
items: [
{ value: 'bank_account', label: 'Bankverbindung', icon: '🏦', desc: 'IBAN, BIC, Kontonummer' },
{ value: 'payment_card', label: 'Zahlungskarten', icon: '💳', desc: 'Kreditkarten-Nr., CVV (PCI-DSS)' },
{ value: 'transaction_data', label: 'Transaktionsdaten', icon: '🧾', desc: 'Zahlungshistorie, Ueberweisungen, Kaufhistorie' },
{ value: 'credit_score', label: 'Bonitaet / Schufa', icon: '📈', desc: 'Kreditwuerdigkeit, Schuldenhistorie' },
{ value: 'income_salary', label: 'Einkommen & Gehalt', icon: '💰', desc: 'Bruttogehalt, Nettolohn, Boni' },
{ value: 'tax_ids', label: 'Steuer-IDs', icon: '📋', desc: 'Steuer-ID, Steuernummer, USt-IdNr.' },
{ value: 'insurance_data', label: 'Versicherungsdaten', icon: '☂️', desc: 'Versicherungsnummern, Policen, Schadenmeldungen' },
],
},
{
group: 'Fahrzeug- & Mobilitaetsdaten',
items: [
{ value: 'vehicle_ids', label: 'Fahrzeug-IDs (VIN)', icon: '🚗', desc: 'Fahrgestellnummer (VIN/FIN), Fahrzeugschein' },
{ value: 'license_plates', label: 'Kennzeichen', icon: '🔢', desc: 'Amtliches Kennzeichen, Wunschkennzeichen' },
{ value: 'gps_tracking', label: 'GPS & Routen', icon: '📍', desc: 'Echtzeitposition, Fahrtenprotokolle' },
{ value: 'telematics', label: 'Telematikdaten', icon: '📡', desc: 'Fahrverhalten, Geschwindigkeit, Motordiagnose' },
{ value: 'fleet_data', label: 'Fuhrpark / Logistik', icon: '🚛', desc: 'Einsatzzeiten, Kilometerstand, Fahrerzuweisung' },
],
},
{
group: 'Technische Identifikatoren',
items: [
{ value: 'ip_address', label: 'IP-Adresse', icon: '🌐', desc: 'IPv4/IPv6 (EuGH: personenbezogen)' },
{ value: 'device_ids', label: 'Geraete-IDs', icon: '📱', desc: 'IMEI, UUID, Advertising-ID, Seriennummer' },
{ value: 'cookies_tracking', label: 'Cookies & Tracking', icon: '🍪', desc: 'Session-/Persistent Cookies, Pixel-Tags' },
{ value: 'browser_fingerprint', label: 'Browser-Fingerprint', icon: '🔎', desc: 'Browser-Typ, OS, Plugins, Canvas-Fingerprint' },
{ value: 'mac_address', label: 'MAC-Adresse', icon: '📶', desc: 'Netzwerkadapter-Kennung, WLAN-Praesenz' },
],
},
{
group: 'Verhaltens- & Nutzungsdaten',
items: [
{ value: 'clickstream', label: 'Klick- & Nutzungspfade', icon: '🖱️', desc: 'Klickpfade, Scrolltiefe, Verweildauer, Heatmaps' },
{ value: 'purchase_history', label: 'Kaufverhalten', icon: '🛒', desc: 'Bestellhistorie, Warenkorb, Wunschlisten' },
{ value: 'app_usage', label: 'App-Nutzung', icon: '📲', desc: 'Genutzte Apps, Nutzungsdauer, In-App-Aktivitaeten' },
{ value: 'profiling_scores', label: 'Profiling / Scoring', icon: '📊', desc: 'KI-generierte Profile, Segmente, Affinitaetsscores' },
],
},
{
group: 'Kommunikation & Medien',
items: [
{ value: 'email_content', label: 'E-Mail-Inhalte', icon: '✉️', desc: 'E-Mail-Texte, Anhaenge, Metadaten' },
{ value: 'chat_messages', label: 'Chat & Messaging', icon: '💬', desc: 'Textnachrichten, Messenger, Teams, Slack' },
{ value: 'call_recordings', label: 'Telefonaufzeichnungen', icon: '📞', desc: 'Gespraeche, Transkripte, Anrufmetadaten' },
{ value: 'video_conference', label: 'Videokonferenzen', icon: '📹', desc: 'Meeting-Aufzeichnungen, Teilnehmerlisten' },
{ value: 'photographs', label: 'Fotos & Bilder', icon: '📷', desc: 'Portraitfotos, Profilbilder, Produktfotos' },
{ value: 'cctv_surveillance', label: 'Videoueberwachung', icon: '📹', desc: 'CCTV-Aufnahmen, Zutrittskontrolle' },
{ value: 'voice_recordings', label: 'Sprachaufnahmen', icon: '🎙️', desc: 'Voicemails, Sprachmemos, Diktate' },
],
},
{
group: 'HR & Beschaeftigung',
items: [
{ value: 'employment_data', label: 'Beschaeftigungsdaten', icon: '💼', desc: 'Arbeitgeber, Berufsbezeichnung, Vertragsart' },
{ value: 'performance_data', label: 'Leistungsbeurteilungen', icon: '🏆', desc: 'Zielerreichung, Feedback, Abmahnungen' },
{ value: 'work_time', label: 'Arbeitszeit', icon: '⏰', desc: 'Zeiterfassung, Ueberstunden, Schichtplaene' },
{ value: 'candidate_data', label: 'Bewerberdaten', icon: '📝', desc: 'Lebenslaeufe, Interviews, Assessment-Ergebnisse' },
{ value: 'social_security', label: 'Sozialversicherungs-Nr.', icon: '🛡️', desc: 'RVNR (Art. 9 — kodiert Geburtsdatum/Geschlecht)' },
],
},
{
group: 'IoT & Sensordaten',
items: [
{ value: 'industrial_sensor', label: 'Industriesensoren', icon: '🏭', desc: 'Maschinendaten, Fehlerprotokolle, Produktionsmesswerte' },
{ value: 'wearable_data', label: 'Wearable-Daten', icon: '⌚', desc: 'Herzfrequenz, Schritte, Schlaf (Art. 9 — Gesundheit)' },
{ value: 'smart_home', label: 'Smart-Home', icon: '🏡', desc: 'Heizung, Licht, Bewegungsmelder, Nutzungszeiten' },
{ value: 'energy_data', label: 'Energieverbrauch', icon: '🔌', desc: 'Smart-Meter, Verbrauchsprofil (enthuellt Verhalten)' },
],
},
{
group: 'Sonstige Kategorien',
items: [
{ value: 'children_data', label: 'Kinderdaten (unter 16)', icon: '👶', desc: 'Besonderer Schutz, Eltern-Einwilligung erforderlich' },
{ value: 'criminal_data', label: 'Strafrechtliche Daten', icon: '⚖️', desc: 'Vorstrafen, Ermittlungsverfahren (Art. 10 DSGVO)' },
{ value: 'location_data', label: 'Standortdaten', icon: '📍', desc: 'GPS, Mobilfunk, WLAN-Ortung, Bewegungsprofile' },
{ value: 'social_media', label: 'Social-Media-Daten', icon: '📱', desc: 'Profile, Posts, Follower, Interaktionen' },
{ value: 'auth_credentials', label: 'Login & Zugangsdaten', icon: '🔑', desc: 'Passwoerter, 2FA, Session-Tokens, Zugriffsprotokolle' },
],
},
]

View File

@@ -0,0 +1,62 @@
// =============================================================================
// WIZARD STEPS CONFIG
// =============================================================================
export const WIZARD_STEPS = [
{ id: 1, title: 'Grundlegendes', description: 'Titel, Beschreibung und KI-Kategorie' },
{ id: 2, title: 'Datenkategorien', description: 'Welche Daten werden verarbeitet?' },
{ id: 3, title: 'Verarbeitungszweck', description: 'Zweck der Datenverarbeitung' },
{ id: 4, title: 'Automatisierung', description: 'Grad der Automatisierung' },
{ id: 5, title: 'Hosting & Modell', description: 'Technische Details' },
{ id: 6, title: 'Datentransfer', description: 'Internationaler Datentransfer' },
{ id: 7, title: 'Datenhaltung', description: 'Aufbewahrung und Speicherung' },
{ id: 8, title: 'Vertraege', description: 'Compliance und Vereinbarungen' },
]
// =============================================================================
// KI-Anwendungskategorien als Auswahlkacheln
// =============================================================================
export const AI_USE_CATEGORIES = [
{ value: 'content_generation', label: 'Content-Erstellung', icon: '✍️', desc: 'Texte, Berichte, E-Mails, Dokumentation automatisch erstellen' },
{ value: 'image_generation', label: 'Bilder erstellen', icon: '🎨', desc: 'KI-generierte Bilder, Grafiken, Produktfotos' },
{ value: 'marketing_material', label: 'Marketingmaterial', icon: '📢', desc: 'Werbetexte, Social Media Posts, Newsletter generieren' },
{ value: 'customer_service', label: 'Kundenservice / Chatbot', icon: '💬', desc: 'Automatisierte Kundenanfragen, FAQ-Bots, Support-Tickets' },
{ value: 'crm_analytics', label: 'CRM & Kundenanalyse', icon: '👥', desc: 'Kundensegmentierung, Churn-Vorhersage, Lead-Scoring' },
{ value: 'hr_recruiting', label: 'Bewerberauswahl / HR', icon: '🧑‍💼', desc: 'CV-Screening, Matching, Mitarbeiteranalysen' },
{ value: 'financial_analysis', label: 'Finanzdaten analysieren', icon: '📊', desc: 'Buchhaltung, Forecasting, Betrugs­erkennung, Risikobewertung' },
{ value: 'predictive_maintenance', label: 'Predictive Maintenance', icon: '🔧', desc: 'Vorausschauende Wartung, Ausfallvorhersage, IoT-Sensoranalyse' },
{ value: 'production_analytics', label: 'Produktionsdaten­auswertung', icon: '🏭', desc: 'Qualitaetskontrolle, Prozess­optimierung, OEE-Analyse' },
{ value: 'document_analysis', label: 'Dokumentenanalyse', icon: '📄', desc: 'Vertraege, Rechnungen, PDFs automatisch auswerten und klassifizieren' },
{ value: 'code_development', label: 'Softwareentwicklung', icon: '💻', desc: 'Code-Generierung, Code-Review, Test-Erstellung, Dokumentation' },
{ value: 'translation', label: 'Uebersetzung', icon: '🌍', desc: 'Automatische Uebersetzung von Texten, Dokumenten, Webinhalten' },
{ value: 'search_knowledge', label: 'Wissensmanagement / Suche', icon: '🔍', desc: 'Interne Wissensdatenbank, RAG-basierte Suche, FAQ-Systeme' },
{ value: 'data_extraction', label: 'Datenextraktion', icon: '⛏️', desc: 'OCR, Formularerkennung, strukturierte Daten aus Freitext' },
{ value: 'risk_compliance', label: 'Risiko & Compliance', icon: '⚖️', desc: 'Compliance-Pruefung, Risikobewertung, Audit-Unterstuetzung' },
{ value: 'supply_chain', label: 'Lieferkette & Logistik', icon: '🚛', desc: 'Bedarfsprognose, Routenoptimierung, Bestandsmanagement' },
{ value: 'medical_health', label: 'Medizin & Gesundheit', icon: '🏥', desc: 'Diagnoseunterstuetzung, Bildanalyse, Patientendaten' },
{ value: 'security_monitoring', label: 'Sicherheit & Monitoring', icon: '🛡️', desc: 'Anomalieerkennung, Bedrohungsanalyse, Zugriffskontrolle' },
{ value: 'personalization', label: 'Personalisierung', icon: '🎯', desc: 'Produktempfehlungen, dynamische Preisgestaltung, A/B-Testing' },
{ value: 'voice_speech', label: 'Sprache & Audio', icon: '🎙️', desc: 'Spracherkennung, Text-to-Speech, Meeting-Transkription' },
{ value: 'other', label: 'Sonstiges', icon: '', desc: 'Anderer KI-Anwendungsfall' },
]
// Map Profil-Branche to domain value for backend compatibility
export function industryToDomain(industries: string[]): string {
if (!industries || industries.length === 0) return 'general'
const first = industries[0].toLowerCase()
if (first.includes('gesundheit') || first.includes('pharma')) return 'healthcare'
if (first.includes('finanz') || first.includes('versicherung')) return 'finance'
if (first.includes('bildung')) return 'education'
if (first.includes('handel') || first.includes('commerce')) return 'retail'
if (first.includes('it') || first.includes('technologie')) return 'it_services'
if (first.includes('beratung') || first.includes('consulting')) return 'consulting'
if (first.includes('produktion') || first.includes('industrie') || first.includes('maschinenbau')) return 'manufacturing'
if (first.includes('marketing') || first.includes('agentur')) return 'marketing'
if (first.includes('recht')) return 'legal'
return 'general'
}
export function toggleInArray(arr: string[], value: string): string[] {
return arr.includes(value) ? arr.filter(v => v !== value) : [...arr, value]
}

View File

@@ -0,0 +1,110 @@
// =============================================================================
// PROCESSING PURPOSES (Step 3) — tile selection
// =============================================================================
export const PURPOSE_TILES = [
{ value: 'service_delivery', label: 'Serviceerbringung', icon: '⚙️', desc: 'Kernfunktion des Produkts oder Services' },
{ value: 'analytics', label: 'Analyse & BI', icon: '📊', desc: 'Statistische Auswertung, Business Intelligence, Reporting' },
{ value: 'marketing', label: 'Marketing & Werbung', icon: '📢', desc: 'Werbung, Personalisierung, Targeting, Newsletter' },
{ value: 'profiling', label: 'Profiling', icon: '🎯', desc: 'Automatisierte Analyse personenbezogener Aspekte' },
{ value: 'automated_decision', label: 'Automatisierte Entscheidung', icon: '🤖', desc: 'Art. 22 DSGVO — Entscheidung ohne menschliches Zutun' },
{ value: 'customer_support', label: 'Kundensupport', icon: '🎧', desc: 'Anfragenbearbeitung, Ticketsystem, Chatbot' },
{ value: 'quality_control', label: 'Qualitaetskontrolle', icon: '✅', desc: 'Produktpruefung, Fehleranalyse, Prozessoptimierung' },
{ value: 'hr_management', label: 'Personalverwaltung', icon: '👥', desc: 'Recruiting, Onboarding, Mitarbeiterentwicklung' },
{ value: 'fraud_detection', label: 'Betrugserkennung', icon: '🕵️', desc: 'Anomalieerkennung, Transaktionsueberwachung' },
{ value: 'research', label: 'Forschung & Entwicklung', icon: '🔬', desc: 'Wissenschaftliche Auswertung, Produktentwicklung' },
{ value: 'compliance_audit', label: 'Compliance & Audit', icon: '📜', desc: 'Regulatorische Pruefung, Dokumentation, Audit-Trail' },
{ value: 'communication', label: 'Kommunikation', icon: '💬', desc: 'Interne/externe Kommunikation, Uebersetzung' },
{ value: 'content_creation', label: 'Content-Erstellung', icon: '✍️', desc: 'Text-, Bild-, Video-Generierung' },
{ value: 'predictive', label: 'Vorhersage & Prognose', icon: '🔮', desc: 'Demand Forecasting, Predictive Analytics, Wartungsvorhersage' },
{ value: 'security', label: 'IT-Sicherheit', icon: '🛡️', desc: 'Bedrohungserkennung, Zugriffskontrolle, Monitoring' },
{ value: 'archiving', label: 'Archivierung', icon: '🗄️', desc: 'Gesetzliche Aufbewahrung, Dokumentenarchiv' },
]
// =============================================================================
// AUTOMATION LEVELS (Step 4) — single-select tiles
// =============================================================================
export const AUTOMATION_TILES = [
{ value: 'assistive', label: 'Assistiv (Mensch entscheidet)', icon: '🧑‍💻', desc: 'KI liefert Vorschlaege, Mensch trifft Entscheidung', examples: 'Rechtschreibkorrektur, Suchvorschlaege, Zusammenfassungen' },
{ value: 'semi_automated', label: 'Teilautomatisiert (Mensch prueft)', icon: '🤝', desc: 'KI erstellt Ergebnisse, Mensch prueft und bestaetigt', examples: 'E-Mail-Entwuerfe mit Freigabe, KI-Vertraege mit juristischer Pruefung' },
{ value: 'fully_automated', label: 'Vollautomatisiert (KI entscheidet)', icon: '🤖', desc: 'KI trifft Entscheidungen eigenstaendig', examples: 'Automatische Kreditentscheidungen, autonome Chatbot-Antworten' },
]
// =============================================================================
// HOSTING & MODEL (Step 5) — tiles
// =============================================================================
export const HOSTING_PROVIDER_TILES = [
{ value: 'self_hosted', label: 'Eigenes Hosting', icon: '🏢', desc: 'On-Premise oder eigene Server' },
{ value: 'hetzner', label: 'Hetzner (DE)', icon: '🇩🇪', desc: 'Deutsche Cloud-Infrastruktur' },
{ value: 'aws', label: 'AWS', icon: '☁️', desc: 'Amazon Web Services' },
{ value: 'azure', label: 'Microsoft Azure', icon: '🔷', desc: 'Microsoft Cloud' },
{ value: 'gcp', label: 'Google Cloud', icon: '🔵', desc: 'Google Cloud Platform' },
{ value: 'other', label: 'Anderer Anbieter', icon: '🌐', desc: 'Sonstiger Cloud-Anbieter' },
]
export const HOSTING_REGION_TILES = [
{ value: 'de', label: 'Deutschland', icon: '🇩🇪', desc: 'Rechenzentrum in Deutschland' },
{ value: 'eu', label: 'EU / EWR', icon: '🇪🇺', desc: 'Innerhalb der Europaeischen Union' },
{ value: 'us', label: 'USA', icon: '🇺🇸', desc: 'Vereinigte Staaten' },
{ value: 'other', label: 'Andere Region', icon: '🌍', desc: 'Drittland ausserhalb EU/USA' },
]
export const MODEL_USAGE_TILES = [
{ value: 'inference', label: 'Inferenz', icon: '⚡', desc: 'Fertiges Modell direkt nutzen (z.B. ChatGPT, Claude, DeepL)' },
{ value: 'rag', label: 'RAG', icon: '📚', desc: 'Modell erhaelt Kontext aus eigenen Dokumenten' },
{ value: 'finetune', label: 'Fine-Tuning', icon: '🎛️', desc: 'Bestehendes Modell mit eigenen Daten nachtrainieren' },
{ value: 'training', label: 'Eigenes Modell trainieren', icon: '🧠', desc: 'Komplett eigenes KI-Modell von Grund auf' },
]
// =============================================================================
// DATA TRANSFER (Step 6) — tiles
// =============================================================================
export const TRANSFER_TARGET_TILES = [
{ value: 'no_transfer', label: 'Kein Drittlandtransfer', icon: '🇪🇺', desc: 'Daten verbleiben in der EU/EWR' },
{ value: 'usa', label: 'USA', icon: '🇺🇸', desc: 'Datentransfer in die USA' },
{ value: 'uk', label: 'Grossbritannien', icon: '🇬🇧', desc: 'Datentransfer nach UK (Angemessenheitsbeschluss)' },
{ value: 'switzerland', label: 'Schweiz', icon: '🇨🇭', desc: 'Datentransfer in die Schweiz (Angemessenheitsbeschluss)' },
{ value: 'other_adequate', label: 'Anderes Land (Angemessenheit)', icon: '✅', desc: 'Land mit Angemessenheitsbeschluss der EU' },
{ value: 'other_third', label: 'Sonstiges Drittland', icon: '🌍', desc: 'Land ohne Angemessenheitsbeschluss' },
]
export const TRANSFER_MECHANISM_TILES = [
{ value: 'not_needed', label: 'Nicht erforderlich', icon: '✅', desc: 'Kein Drittlandtransfer oder Angemessenheit' },
{ value: 'scc', label: 'Standardvertragsklauseln', icon: '📝', desc: 'SCC nach Art. 46 Abs. 2c DSGVO' },
{ value: 'bcr', label: 'Binding Corporate Rules', icon: '🏛️', desc: 'BCR nach Art. 47 DSGVO' },
{ value: 'adequacy', label: 'Angemessenheitsbeschluss', icon: '🤝', desc: 'EU-Kommissionsbeschluss (z.B. EU-US DPF)' },
{ value: 'derogation', label: 'Ausnahme (Art. 49)', icon: '⚠️', desc: 'Einwilligung oder zwingende Interessen' },
]
// =============================================================================
// RETENTION (Step 7) — tiles
// =============================================================================
export const RETENTION_TILES = [
{ value: 'session', label: 'Nur waehrend Session', icon: '⏱️', desc: 'Daten werden nach Sitzungsende geloescht' },
{ value: '30_days', label: '30 Tage', icon: '📅', desc: 'Kurzfristige Aufbewahrung' },
{ value: '90_days', label: '90 Tage', icon: '📅', desc: 'Standardaufbewahrung' },
{ value: '1_year', label: '1 Jahr', icon: '📆', desc: 'Jaehrliche Aufbewahrung' },
{ value: '3_years', label: '3 Jahre', icon: '📆', desc: 'Mittelfristige Aufbewahrung' },
{ value: '6_years', label: '6 Jahre', icon: '📆', desc: 'Handelsrechtliche Aufbewahrungsfrist' },
{ value: '10_years', label: '10 Jahre', icon: '📆', desc: 'Steuerrechtliche Aufbewahrungsfrist' },
{ value: 'indefinite', label: 'Unbefristet', icon: '♾️', desc: 'Keine zeitliche Begrenzung' },
]
// =============================================================================
// CONTRACTS (Step 8) — tiles
// =============================================================================
export const CONTRACT_TILES = [
{ value: 'has_dpa', label: 'AVV / DPA vorhanden', icon: '📄', desc: 'Auftragsverarbeitungsvertrag nach Art. 28 DSGVO' },
{ value: 'has_aia_doc', label: 'AI Act Dokumentation', icon: '🤖', desc: 'Risikoklassifizierung und technische Doku nach EU AI Act' },
{ value: 'has_dsfa', label: 'DSFA durchgefuehrt', icon: '📋', desc: 'Datenschutz-Folgenabschaetzung nach Art. 35 DSGVO' },
{ value: 'has_tia', label: 'TIA durchgefuehrt', icon: '🌍', desc: 'Transfer Impact Assessment fuer Drittlandtransfers' },
{ value: 'has_tom', label: 'TOM dokumentiert', icon: '🔒', desc: 'Technisch-organisatorische Massnahmen nach Art. 32 DSGVO' },
{ value: 'has_vvt', label: 'Im VVT erfasst', icon: '📚', desc: 'Im Verzeichnis von Verarbeitungstaetigkeiten eingetragen' },
{ value: 'has_consent', label: 'Einwilligungen eingeholt', icon: '✅', desc: 'Nutzereinwilligungen vorhanden und dokumentiert' },
{ value: 'none', label: 'Noch keine Dokumente', icon: '⚠️', desc: 'Compliance-Dokumentation steht noch aus' },
]

View File

@@ -0,0 +1,25 @@
export interface AdvisoryForm {
title: string
use_case_text: string
domain: string
category: string
data_categories: string[]
custom_data_types: string[]
purposes: string[]
automation: string
hosting_provider: string
hosting_region: string
model_usage: string[]
transfer_targets: string[]
transfer_countries: string[]
transfer_mechanism: string
retention_period: string
retention_purpose: string
contracts: string[]
subprocessors: string
}
export interface StepProps {
form: AdvisoryForm
updateForm: (updates: Partial<AdvisoryForm>) => void
}

View File

@@ -4,298 +4,19 @@ import React, { useState, useEffect, Suspense } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import Link from 'next/link'
import { useSDK } from '@/lib/sdk'
import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard'
// =============================================================================
// WIZARD STEPS CONFIG
// =============================================================================
const WIZARD_STEPS = [
{ id: 1, title: 'Grundlegendes', description: 'Titel, Beschreibung und KI-Kategorie' },
{ id: 2, title: 'Datenkategorien', description: 'Welche Daten werden verarbeitet?' },
{ id: 3, title: 'Verarbeitungszweck', description: 'Zweck der Datenverarbeitung' },
{ id: 4, title: 'Automatisierung', description: 'Grad der Automatisierung' },
{ id: 5, title: 'Hosting & Modell', description: 'Technische Details' },
{ id: 6, title: 'Datentransfer', description: 'Internationaler Datentransfer' },
{ id: 7, title: 'Datenhaltung', description: 'Aufbewahrung und Speicherung' },
{ id: 8, title: 'Vertraege', description: 'Compliance und Vereinbarungen' },
]
// =============================================================================
// KI-Anwendungskategorien als Auswahlkacheln
// =============================================================================
const AI_USE_CATEGORIES = [
{ value: 'content_generation', label: 'Content-Erstellung', icon: '✍️', desc: 'Texte, Berichte, E-Mails, Dokumentation automatisch erstellen' },
{ value: 'image_generation', label: 'Bilder erstellen', icon: '🎨', desc: 'KI-generierte Bilder, Grafiken, Produktfotos' },
{ value: 'marketing_material', label: 'Marketingmaterial', icon: '📢', desc: 'Werbetexte, Social Media Posts, Newsletter generieren' },
{ value: 'customer_service', label: 'Kundenservice / Chatbot', icon: '💬', desc: 'Automatisierte Kundenanfragen, FAQ-Bots, Support-Tickets' },
{ value: 'crm_analytics', label: 'CRM & Kundenanalyse', icon: '👥', desc: 'Kundensegmentierung, Churn-Vorhersage, Lead-Scoring' },
{ value: 'hr_recruiting', label: 'Bewerberauswahl / HR', icon: '🧑‍💼', desc: 'CV-Screening, Matching, Mitarbeiteranalysen' },
{ value: 'financial_analysis', label: 'Finanzdaten analysieren', icon: '📊', desc: 'Buchhaltung, Forecasting, Betrugs­erkennung, Risikobewertung' },
{ value: 'predictive_maintenance', label: 'Predictive Maintenance', icon: '🔧', desc: 'Vorausschauende Wartung, Ausfallvorhersage, IoT-Sensoranalyse' },
{ value: 'production_analytics', label: 'Produktionsdaten­auswertung', icon: '🏭', desc: 'Qualitaetskontrolle, Prozess­optimierung, OEE-Analyse' },
{ value: 'document_analysis', label: 'Dokumentenanalyse', icon: '📄', desc: 'Vertraege, Rechnungen, PDFs automatisch auswerten und klassifizieren' },
{ value: 'code_development', label: 'Softwareentwicklung', icon: '💻', desc: 'Code-Generierung, Code-Review, Test-Erstellung, Dokumentation' },
{ value: 'translation', label: 'Uebersetzung', icon: '🌍', desc: 'Automatische Uebersetzung von Texten, Dokumenten, Webinhalten' },
{ value: 'search_knowledge', label: 'Wissensmanagement / Suche', icon: '🔍', desc: 'Interne Wissensdatenbank, RAG-basierte Suche, FAQ-Systeme' },
{ value: 'data_extraction', label: 'Datenextraktion', icon: '⛏️', desc: 'OCR, Formularerkennung, strukturierte Daten aus Freitext' },
{ value: 'risk_compliance', label: 'Risiko & Compliance', icon: '⚖️', desc: 'Compliance-Pruefung, Risikobewertung, Audit-Unterstuetzung' },
{ value: 'supply_chain', label: 'Lieferkette & Logistik', icon: '🚛', desc: 'Bedarfsprognose, Routenoptimierung, Bestandsmanagement' },
{ value: 'medical_health', label: 'Medizin & Gesundheit', icon: '🏥', desc: 'Diagnoseunterstuetzung, Bildanalyse, Patientendaten' },
{ value: 'security_monitoring', label: 'Sicherheit & Monitoring', icon: '🛡️', desc: 'Anomalieerkennung, Bedrohungsanalyse, Zugriffskontrolle' },
{ value: 'personalization', label: 'Personalisierung', icon: '🎯', desc: 'Produktempfehlungen, dynamische Preisgestaltung, A/B-Testing' },
{ value: 'voice_speech', label: 'Sprache & Audio', icon: '🎙️', desc: 'Spracherkennung, Text-to-Speech, Meeting-Transkription' },
{ value: 'other', label: 'Sonstiges', icon: '', desc: 'Anderer KI-Anwendungsfall' },
]
// Map Profil-Branche to domain value for backend compatibility
function industryToDomain(industries: string[]): string {
if (!industries || industries.length === 0) return 'general'
const first = industries[0].toLowerCase()
if (first.includes('gesundheit') || first.includes('pharma')) return 'healthcare'
if (first.includes('finanz') || first.includes('versicherung')) return 'finance'
if (first.includes('bildung')) return 'education'
if (first.includes('handel') || first.includes('commerce')) return 'retail'
if (first.includes('it') || first.includes('technologie')) return 'it_services'
if (first.includes('beratung') || first.includes('consulting')) return 'consulting'
if (first.includes('produktion') || first.includes('industrie') || first.includes('maschinenbau')) return 'manufacturing'
if (first.includes('marketing') || first.includes('agentur')) return 'marketing'
if (first.includes('recht')) return 'legal'
return 'general'
}
// =============================================================================
// DATA CATEGORIES (Step 2) — grouped tile selection
// =============================================================================
const DATA_CATEGORY_GROUPS = [
{
group: 'Stamm- & Kontaktdaten',
items: [
{ value: 'basic_identity', label: 'Name & Identitaet', icon: '👤', desc: 'Vor-/Nachname, Geburtsdatum, Geschlecht' },
{ value: 'contact_data', label: 'Kontaktdaten', icon: '📧', desc: 'E-Mail, Telefon, Fax' },
{ value: 'address_data', label: 'Adressdaten', icon: '🏠', desc: 'Wohn-/Meldeadresse, PLZ, Lieferadresse' },
{ value: 'government_ids', label: 'Ausweisdaten', icon: '🪪', desc: 'Personalausweis-Nr., Reisepass, Fuehrerschein' },
{ value: 'customer_ids', label: 'Kundennummern', icon: '🏷️', desc: 'Kunden-ID, Vertrags-Nr., Mitgliedsnummer' },
],
},
{
group: 'Besondere Kategorien (Art. 9 DSGVO)',
art9: true,
items: [
{ value: 'health_data', label: 'Gesundheitsdaten', icon: '🏥', desc: 'Diagnosen, Medikation, AU, Pflegegrad' },
{ value: 'biometric_data', label: 'Biometrische Daten', icon: '🔐', desc: 'Fingerabdruck, Gesichtserkennung, Iris-Scan' },
{ value: 'genetic_data', label: 'Genetische Daten', icon: '🧬', desc: 'DNA-Profil, Genomsequenzen, Erbkrankheitstests' },
{ value: 'racial_ethnic', label: 'Ethnische Herkunft', icon: '🌍', desc: 'Rassische/ethnische Zugehoerigkeit' },
{ value: 'political_opinions', label: 'Politische Meinungen', icon: '🗳️', desc: 'Politische Ueberzeugungen, Parteizugehoerigkeit' },
{ value: 'religious_beliefs', label: 'Religion', icon: '🕊️', desc: 'Religionszugehoerigkeit, Weltanschauung' },
{ value: 'trade_union', label: 'Gewerkschaft', icon: '🤝', desc: 'Gewerkschaftsmitgliedschaft' },
{ value: 'sexual_orientation', label: 'Sexuelle Orientierung', icon: '🏳️‍🌈', desc: 'Sexualleben und Orientierung' },
],
},
{
group: 'Finanz- & Steuerdaten',
items: [
{ value: 'bank_account', label: 'Bankverbindung', icon: '🏦', desc: 'IBAN, BIC, Kontonummer' },
{ value: 'payment_card', label: 'Zahlungskarten', icon: '💳', desc: 'Kreditkarten-Nr., CVV (PCI-DSS)' },
{ value: 'transaction_data', label: 'Transaktionsdaten', icon: '🧾', desc: 'Zahlungshistorie, Ueberweisungen, Kaufhistorie' },
{ value: 'credit_score', label: 'Bonitaet / Schufa', icon: '📈', desc: 'Kreditwuerdigkeit, Schuldenhistorie' },
{ value: 'income_salary', label: 'Einkommen & Gehalt', icon: '💰', desc: 'Bruttogehalt, Nettolohn, Boni' },
{ value: 'tax_ids', label: 'Steuer-IDs', icon: '📋', desc: 'Steuer-ID, Steuernummer, USt-IdNr.' },
{ value: 'insurance_data', label: 'Versicherungsdaten', icon: '☂️', desc: 'Versicherungsnummern, Policen, Schadenmeldungen' },
],
},
{
group: 'Fahrzeug- & Mobilitaetsdaten',
items: [
{ value: 'vehicle_ids', label: 'Fahrzeug-IDs (VIN)', icon: '🚗', desc: 'Fahrgestellnummer (VIN/FIN), Fahrzeugschein' },
{ value: 'license_plates', label: 'Kennzeichen', icon: '🔢', desc: 'Amtliches Kennzeichen, Wunschkennzeichen' },
{ value: 'gps_tracking', label: 'GPS & Routen', icon: '📍', desc: 'Echtzeitposition, Fahrtenprotokolle' },
{ value: 'telematics', label: 'Telematikdaten', icon: '📡', desc: 'Fahrverhalten, Geschwindigkeit, Motordiagnose' },
{ value: 'fleet_data', label: 'Fuhrpark / Logistik', icon: '🚛', desc: 'Einsatzzeiten, Kilometerstand, Fahrerzuweisung' },
],
},
{
group: 'Technische Identifikatoren',
items: [
{ value: 'ip_address', label: 'IP-Adresse', icon: '🌐', desc: 'IPv4/IPv6 (EuGH: personenbezogen)' },
{ value: 'device_ids', label: 'Geraete-IDs', icon: '📱', desc: 'IMEI, UUID, Advertising-ID, Seriennummer' },
{ value: 'cookies_tracking', label: 'Cookies & Tracking', icon: '🍪', desc: 'Session-/Persistent Cookies, Pixel-Tags' },
{ value: 'browser_fingerprint', label: 'Browser-Fingerprint', icon: '🔎', desc: 'Browser-Typ, OS, Plugins, Canvas-Fingerprint' },
{ value: 'mac_address', label: 'MAC-Adresse', icon: '📶', desc: 'Netzwerkadapter-Kennung, WLAN-Praesenz' },
],
},
{
group: 'Verhaltens- & Nutzungsdaten',
items: [
{ value: 'clickstream', label: 'Klick- & Nutzungspfade', icon: '🖱️', desc: 'Klickpfade, Scrolltiefe, Verweildauer, Heatmaps' },
{ value: 'purchase_history', label: 'Kaufverhalten', icon: '🛒', desc: 'Bestellhistorie, Warenkorb, Wunschlisten' },
{ value: 'app_usage', label: 'App-Nutzung', icon: '📲', desc: 'Genutzte Apps, Nutzungsdauer, In-App-Aktivitaeten' },
{ value: 'profiling_scores', label: 'Profiling / Scoring', icon: '📊', desc: 'KI-generierte Profile, Segmente, Affinitaetsscores' },
],
},
{
group: 'Kommunikation & Medien',
items: [
{ value: 'email_content', label: 'E-Mail-Inhalte', icon: '✉️', desc: 'E-Mail-Texte, Anhaenge, Metadaten' },
{ value: 'chat_messages', label: 'Chat & Messaging', icon: '💬', desc: 'Textnachrichten, Messenger, Teams, Slack' },
{ value: 'call_recordings', label: 'Telefonaufzeichnungen', icon: '📞', desc: 'Gespraeche, Transkripte, Anrufmetadaten' },
{ value: 'video_conference', label: 'Videokonferenzen', icon: '📹', desc: 'Meeting-Aufzeichnungen, Teilnehmerlisten' },
{ value: 'photographs', label: 'Fotos & Bilder', icon: '📷', desc: 'Portraitfotos, Profilbilder, Produktfotos' },
{ value: 'cctv_surveillance', label: 'Videoueberwachung', icon: '📹', desc: 'CCTV-Aufnahmen, Zutrittskontrolle' },
{ value: 'voice_recordings', label: 'Sprachaufnahmen', icon: '🎙️', desc: 'Voicemails, Sprachmemos, Diktate' },
],
},
{
group: 'HR & Beschaeftigung',
items: [
{ value: 'employment_data', label: 'Beschaeftigungsdaten', icon: '💼', desc: 'Arbeitgeber, Berufsbezeichnung, Vertragsart' },
{ value: 'performance_data', label: 'Leistungsbeurteilungen', icon: '🏆', desc: 'Zielerreichung, Feedback, Abmahnungen' },
{ value: 'work_time', label: 'Arbeitszeit', icon: '⏰', desc: 'Zeiterfassung, Ueberstunden, Schichtplaene' },
{ value: 'candidate_data', label: 'Bewerberdaten', icon: '📝', desc: 'Lebenslaeufe, Interviews, Assessment-Ergebnisse' },
{ value: 'social_security', label: 'Sozialversicherungs-Nr.', icon: '🛡️', desc: 'RVNR (Art. 9 — kodiert Geburtsdatum/Geschlecht)' },
],
},
{
group: 'IoT & Sensordaten',
items: [
{ value: 'industrial_sensor', label: 'Industriesensoren', icon: '🏭', desc: 'Maschinendaten, Fehlerprotokolle, Produktionsmesswerte' },
{ value: 'wearable_data', label: 'Wearable-Daten', icon: '⌚', desc: 'Herzfrequenz, Schritte, Schlaf (Art. 9 — Gesundheit)' },
{ value: 'smart_home', label: 'Smart-Home', icon: '🏡', desc: 'Heizung, Licht, Bewegungsmelder, Nutzungszeiten' },
{ value: 'energy_data', label: 'Energieverbrauch', icon: '🔌', desc: 'Smart-Meter, Verbrauchsprofil (enthuellt Verhalten)' },
],
},
{
group: 'Sonstige Kategorien',
items: [
{ value: 'children_data', label: 'Kinderdaten (unter 16)', icon: '👶', desc: 'Besonderer Schutz, Eltern-Einwilligung erforderlich' },
{ value: 'criminal_data', label: 'Strafrechtliche Daten', icon: '⚖️', desc: 'Vorstrafen, Ermittlungsverfahren (Art. 10 DSGVO)' },
{ value: 'location_data', label: 'Standortdaten', icon: '📍', desc: 'GPS, Mobilfunk, WLAN-Ortung, Bewegungsprofile' },
{ value: 'social_media', label: 'Social-Media-Daten', icon: '📱', desc: 'Profile, Posts, Follower, Interaktionen' },
{ value: 'auth_credentials', label: 'Login & Zugangsdaten', icon: '🔑', desc: 'Passwoerter, 2FA, Session-Tokens, Zugriffsprotokolle' },
],
},
]
// =============================================================================
// PROCESSING PURPOSES (Step 3) — tile selection
// =============================================================================
const PURPOSE_TILES = [
{ value: 'service_delivery', label: 'Serviceerbringung', icon: '⚙️', desc: 'Kernfunktion des Produkts oder Services' },
{ value: 'analytics', label: 'Analyse & BI', icon: '📊', desc: 'Statistische Auswertung, Business Intelligence, Reporting' },
{ value: 'marketing', label: 'Marketing & Werbung', icon: '📢', desc: 'Werbung, Personalisierung, Targeting, Newsletter' },
{ value: 'profiling', label: 'Profiling', icon: '🎯', desc: 'Automatisierte Analyse personenbezogener Aspekte' },
{ value: 'automated_decision', label: 'Automatisierte Entscheidung', icon: '🤖', desc: 'Art. 22 DSGVO — Entscheidung ohne menschliches Zutun' },
{ value: 'customer_support', label: 'Kundensupport', icon: '🎧', desc: 'Anfragenbearbeitung, Ticketsystem, Chatbot' },
{ value: 'quality_control', label: 'Qualitaetskontrolle', icon: '✅', desc: 'Produktpruefung, Fehleranalyse, Prozessoptimierung' },
{ value: 'hr_management', label: 'Personalverwaltung', icon: '👥', desc: 'Recruiting, Onboarding, Mitarbeiterentwicklung' },
{ value: 'fraud_detection', label: 'Betrugserkennung', icon: '🕵️', desc: 'Anomalieerkennung, Transaktionsueberwachung' },
{ value: 'research', label: 'Forschung & Entwicklung', icon: '🔬', desc: 'Wissenschaftliche Auswertung, Produktentwicklung' },
{ value: 'compliance_audit', label: 'Compliance & Audit', icon: '📜', desc: 'Regulatorische Pruefung, Dokumentation, Audit-Trail' },
{ value: 'communication', label: 'Kommunikation', icon: '💬', desc: 'Interne/externe Kommunikation, Uebersetzung' },
{ value: 'content_creation', label: 'Content-Erstellung', icon: '✍️', desc: 'Text-, Bild-, Video-Generierung' },
{ value: 'predictive', label: 'Vorhersage & Prognose', icon: '🔮', desc: 'Demand Forecasting, Predictive Analytics, Wartungsvorhersage' },
{ value: 'security', label: 'IT-Sicherheit', icon: '🛡️', desc: 'Bedrohungserkennung, Zugriffskontrolle, Monitoring' },
{ value: 'archiving', label: 'Archivierung', icon: '🗄️', desc: 'Gesetzliche Aufbewahrung, Dokumentenarchiv' },
]
// =============================================================================
// AUTOMATION LEVELS (Step 4) — single-select tiles
// =============================================================================
const AUTOMATION_TILES = [
{ value: 'assistive', label: 'Assistiv (Mensch entscheidet)', icon: '🧑‍💻', desc: 'KI liefert Vorschlaege, Mensch trifft Entscheidung', examples: 'Rechtschreibkorrektur, Suchvorschlaege, Zusammenfassungen' },
{ value: 'semi_automated', label: 'Teilautomatisiert (Mensch prueft)', icon: '🤝', desc: 'KI erstellt Ergebnisse, Mensch prueft und bestaetigt', examples: 'E-Mail-Entwuerfe mit Freigabe, KI-Vertraege mit juristischer Pruefung' },
{ value: 'fully_automated', label: 'Vollautomatisiert (KI entscheidet)', icon: '🤖', desc: 'KI trifft Entscheidungen eigenstaendig', examples: 'Automatische Kreditentscheidungen, autonome Chatbot-Antworten' },
]
// =============================================================================
// HOSTING & MODEL (Step 5) — tiles
// =============================================================================
const HOSTING_PROVIDER_TILES = [
{ value: 'self_hosted', label: 'Eigenes Hosting', icon: '🏢', desc: 'On-Premise oder eigene Server' },
{ value: 'hetzner', label: 'Hetzner (DE)', icon: '🇩🇪', desc: 'Deutsche Cloud-Infrastruktur' },
{ value: 'aws', label: 'AWS', icon: '☁️', desc: 'Amazon Web Services' },
{ value: 'azure', label: 'Microsoft Azure', icon: '🔷', desc: 'Microsoft Cloud' },
{ value: 'gcp', label: 'Google Cloud', icon: '🔵', desc: 'Google Cloud Platform' },
{ value: 'other', label: 'Anderer Anbieter', icon: '🌐', desc: 'Sonstiger Cloud-Anbieter' },
]
const HOSTING_REGION_TILES = [
{ value: 'de', label: 'Deutschland', icon: '🇩🇪', desc: 'Rechenzentrum in Deutschland' },
{ value: 'eu', label: 'EU / EWR', icon: '🇪🇺', desc: 'Innerhalb der Europaeischen Union' },
{ value: 'us', label: 'USA', icon: '🇺🇸', desc: 'Vereinigte Staaten' },
{ value: 'other', label: 'Andere Region', icon: '🌍', desc: 'Drittland ausserhalb EU/USA' },
]
const MODEL_USAGE_TILES = [
{ value: 'inference', label: 'Inferenz', icon: '⚡', desc: 'Fertiges Modell direkt nutzen (z.B. ChatGPT, Claude, DeepL)' },
{ value: 'rag', label: 'RAG', icon: '📚', desc: 'Modell erhaelt Kontext aus eigenen Dokumenten' },
{ value: 'finetune', label: 'Fine-Tuning', icon: '🎛️', desc: 'Bestehendes Modell mit eigenen Daten nachtrainieren' },
{ value: 'training', label: 'Eigenes Modell trainieren', icon: '🧠', desc: 'Komplett eigenes KI-Modell von Grund auf' },
]
// =============================================================================
// DATA TRANSFER (Step 6) — tiles
// =============================================================================
const TRANSFER_TARGET_TILES = [
{ value: 'no_transfer', label: 'Kein Drittlandtransfer', icon: '🇪🇺', desc: 'Daten verbleiben in der EU/EWR' },
{ value: 'usa', label: 'USA', icon: '🇺🇸', desc: 'Datentransfer in die USA' },
{ value: 'uk', label: 'Grossbritannien', icon: '🇬🇧', desc: 'Datentransfer nach UK (Angemessenheitsbeschluss)' },
{ value: 'switzerland', label: 'Schweiz', icon: '🇨🇭', desc: 'Datentransfer in die Schweiz (Angemessenheitsbeschluss)' },
{ value: 'other_adequate', label: 'Anderes Land (Angemessenheit)', icon: '✅', desc: 'Land mit Angemessenheitsbeschluss der EU' },
{ value: 'other_third', label: 'Sonstiges Drittland', icon: '🌍', desc: 'Land ohne Angemessenheitsbeschluss' },
]
const TRANSFER_MECHANISM_TILES = [
{ value: 'not_needed', label: 'Nicht erforderlich', icon: '✅', desc: 'Kein Drittlandtransfer oder Angemessenheit' },
{ value: 'scc', label: 'Standardvertragsklauseln', icon: '📝', desc: 'SCC nach Art. 46 Abs. 2c DSGVO' },
{ value: 'bcr', label: 'Binding Corporate Rules', icon: '🏛️', desc: 'BCR nach Art. 47 DSGVO' },
{ value: 'adequacy', label: 'Angemessenheitsbeschluss', icon: '🤝', desc: 'EU-Kommissionsbeschluss (z.B. EU-US DPF)' },
{ value: 'derogation', label: 'Ausnahme (Art. 49)', icon: '⚠️', desc: 'Einwilligung oder zwingende Interessen' },
]
// =============================================================================
// RETENTION (Step 7) — tiles
// =============================================================================
const RETENTION_TILES = [
{ value: 'session', label: 'Nur waehrend Session', icon: '⏱️', desc: 'Daten werden nach Sitzungsende geloescht' },
{ value: '30_days', label: '30 Tage', icon: '📅', desc: 'Kurzfristige Aufbewahrung' },
{ value: '90_days', label: '90 Tage', icon: '📅', desc: 'Standardaufbewahrung' },
{ value: '1_year', label: '1 Jahr', icon: '📆', desc: 'Jaehrliche Aufbewahrung' },
{ value: '3_years', label: '3 Jahre', icon: '📆', desc: 'Mittelfristige Aufbewahrung' },
{ value: '6_years', label: '6 Jahre', icon: '📆', desc: 'Handelsrechtliche Aufbewahrungsfrist' },
{ value: '10_years', label: '10 Jahre', icon: '📆', desc: 'Steuerrechtliche Aufbewahrungsfrist' },
{ value: 'indefinite', label: 'Unbefristet', icon: '♾️', desc: 'Keine zeitliche Begrenzung' },
]
// =============================================================================
// CONTRACTS (Step 8) — tiles
// =============================================================================
const CONTRACT_TILES = [
{ value: 'has_dpa', label: 'AVV / DPA vorhanden', icon: '📄', desc: 'Auftragsverarbeitungsvertrag nach Art. 28 DSGVO' },
{ value: 'has_aia_doc', label: 'AI Act Dokumentation', icon: '🤖', desc: 'Risikoklassifizierung und technische Doku nach EU AI Act' },
{ value: 'has_dsfa', label: 'DSFA durchgefuehrt', icon: '📋', desc: 'Datenschutz-Folgenabschaetzung nach Art. 35 DSGVO' },
{ value: 'has_tia', label: 'TIA durchgefuehrt', icon: '🌍', desc: 'Transfer Impact Assessment fuer Drittlandtransfers' },
{ value: 'has_tom', label: 'TOM dokumentiert', icon: '🔒', desc: 'Technisch-organisatorische Massnahmen nach Art. 32 DSGVO' },
{ value: 'has_vvt', label: 'Im VVT erfasst', icon: '📚', desc: 'Im Verzeichnis von Verarbeitungstaetigkeiten eingetragen' },
{ value: 'has_consent', label: 'Einwilligungen eingeholt', icon: '✅', desc: 'Nutzereinwilligungen vorhanden und dokumentiert' },
{ value: 'none', label: 'Noch keine Dokumente', icon: '⚠️', desc: 'Compliance-Dokumentation steht noch aus' },
]
// =============================================================================
// SHARED TILE TOGGLE HELPER
// =============================================================================
function toggleInArray(arr: string[], value: string): string[] {
return arr.includes(value) ? arr.filter(v => v !== value) : [...arr, value]
}
import type { AdvisoryForm } from './_types'
import { industryToDomain } from './_data'
import { StepIndicator } from './_components/StepIndicator'
import { Step1Basics } from './_components/Step1Basics'
import { Step2DataCategories } from './_components/Step2DataCategories'
import { Step3Purposes } from './_components/Step3Purposes'
import { Step4Automation } from './_components/Step4Automation'
import { Step5Hosting } from './_components/Step5Hosting'
import { Step6Transfer } from './_components/Step6Transfer'
import { Step7Retention } from './_components/Step7Retention'
import { Step8Contracts } from './_components/Step8Contracts'
import { NavigationButtons } from './_components/NavigationButtons'
import { ResultView } from './_components/ResultView'
// =============================================================================
// MAIN COMPONENT
@@ -321,36 +42,28 @@ function AdvisoryBoardPageInner() {
)
// Form state — tile-based multi-select via arrays
const [form, setForm] = useState({
const [form, setForm] = useState<AdvisoryForm>({
title: '',
use_case_text: '',
domain: 'general',
category: '' as string,
// Data categories (multi-select tiles)
data_categories: [] as string[],
custom_data_types: [] as string[],
// Purpose (multi-select tiles)
purposes: [] as string[],
// Automation (single-select tile)
automation: '' as string,
// Hosting (single-select tile)
hosting_provider: '' as string,
hosting_region: '' as string,
// Model Usage (multi-select tiles)
model_usage: [] as string[],
// Data Transfer (Step 6 — tiles)
transfer_targets: [] as string[],
transfer_countries: [] as string[],
transfer_mechanism: '' as string,
// Retention (Step 7)
retention_period: '' as string,
category: '',
data_categories: [],
custom_data_types: [],
purposes: [],
automation: '',
hosting_provider: '',
hosting_region: '',
model_usage: [],
transfer_targets: [],
transfer_countries: [],
transfer_mechanism: '',
retention_period: '',
retention_purpose: '',
// Contracts (Step 8 — multi-select tiles)
contracts: [] as string[],
contracts: [],
subprocessors: '',
})
const updateForm = (updates: Partial<typeof form>) => {
const updateForm = (updates: Partial<AdvisoryForm>) => {
setForm(prev => ({ ...prev, ...updates }))
}
@@ -455,32 +168,12 @@ function AdvisoryBoardPageInner() {
// If we have a result, show it
if (result) {
const r = result as { assessment?: { id: string }; result?: Record<string, unknown> }
return (
<div className="max-w-4xl mx-auto space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">Assessment Ergebnis</h1>
<div className="flex gap-2">
{r.assessment?.id && (
<button
onClick={() => router.push(`/sdk/use-cases/${r.assessment!.id}`)}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
>
Zum Assessment
</button>
)}
<button
onClick={() => router.push('/sdk/use-cases')}
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200"
>
Zur Uebersicht
</button>
</div>
</div>
{r.result && (
<AssessmentResultCard result={r.result as unknown as Parameters<typeof AssessmentResultCard>[0]['result']} />
)}
</div>
<ResultView
result={result}
onGoToAssessment={(id) => router.push(`/sdk/use-cases/${id}`)}
onGoToOverview={() => router.push('/sdk/use-cases')}
/>
)
}
@@ -513,29 +206,7 @@ function AdvisoryBoardPageInner() {
</div>
)}
{/* Step Indicator */}
<div className="flex items-center gap-2">
{WIZARD_STEPS.map((step, idx) => (
<React.Fragment key={step.id}>
<button
onClick={() => setCurrentStep(step.id)}
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors ${
currentStep === step.id
? 'bg-purple-600 text-white'
: currentStep > step.id
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-500'
}`}
>
<span className="w-6 h-6 rounded-full bg-white/20 flex items-center justify-center text-xs font-bold">
{currentStep > step.id ? '✓' : step.id}
</span>
<span className="hidden md:inline">{step.title}</span>
</button>
{idx < WIZARD_STEPS.length - 1 && <div className="flex-1 h-px bg-gray-200" />}
</React.Fragment>
))}
</div>
<StepIndicator currentStep={currentStep} onStepClick={setCurrentStep} />
{/* Error */}
{error && (
@@ -544,545 +215,27 @@ function AdvisoryBoardPageInner() {
{/* Step Content */}
<div className="bg-white rounded-xl border border-gray-200 p-6">
{/* Step 1: Grundlegendes */}
{currentStep === 1 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Grundlegende Informationen</h2>
{/* Branche aus Profil (nur Anzeige) */}
{profileIndustry && (Array.isArray(profileIndustry) ? profileIndustry.length > 0 : true) && (
<div className="bg-gray-50 rounded-lg border border-gray-200 px-4 py-3">
<span className="text-xs font-medium text-gray-500 uppercase tracking-wide">Branche (aus Unternehmensprofil)</span>
<p className="text-sm text-gray-900 mt-0.5">
{Array.isArray(profileIndustry) ? profileIndustry.join(', ') : profileIndustry}
</p>
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Titel des Anwendungsfalls</label>
<input
type="text"
value={form.title}
onChange={e => updateForm({ title: e.target.value })}
placeholder="z.B. Chatbot fuer Kundenservice"
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={form.use_case_text}
onChange={e => updateForm({ use_case_text: e.target.value })}
rows={4}
placeholder="Beschreiben Sie den Anwendungsfall..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
{/* KI-Anwendungskategorie als Kacheln */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
In welchem Bereich kommt KI zum Einsatz?
</label>
<p className="text-sm text-gray-500 mb-3">Waehlen Sie die passende Kategorie fuer Ihren Anwendungsfall.</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{AI_USE_CATEGORIES.map(cat => (
<button
key={cat.value}
type="button"
onClick={() => updateForm({ category: cat.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.category === cat.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{cat.icon}</span>
<span className="text-sm font-medium text-gray-900">{cat.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{cat.desc}</p>
</button>
))}
</div>
</div>
</div>
)}
{/* Step 2: Datenkategorien */}
{currentStep === 2 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Welche Daten werden verarbeitet?</h2>
<p className="text-sm text-gray-500">Waehlen Sie alle Datenkategorien, die in diesem Use Case verarbeitet werden.</p>
{DATA_CATEGORY_GROUPS.map(group => (
<div key={group.group}>
<h3 className={`text-sm font-semibold mb-2 ${group.art9 ? 'text-orange-700' : 'text-gray-700'}`}>
{group.art9 && '⚠️ '}{group.group}
</h3>
{group.art9 && (
<p className="text-xs text-orange-600 mb-2">Besonders schutzwuerdig erhoehte Anforderungen an Rechtsgrundlage und TOM</p>
)}
<div className="grid grid-cols-2 md:grid-cols-3 gap-2 mb-4">
{group.items.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ data_categories: toggleInArray(form.data_categories, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.data_categories.includes(item.value)
? group.art9
? 'border-orange-500 bg-orange-50 ring-1 ring-orange-300'
: 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
))}
{/* Sonstige Datentypen */}
<div className="border border-gray-200 rounded-lg p-4 space-y-3">
<div className="font-medium text-gray-900">Sonstige Datentypen</div>
<p className="text-sm text-gray-500">
Falls Ihre Datenkategorie oben nicht aufgefuehrt ist, koennen Sie sie hier ergaenzen.
</p>
{form.custom_data_types.map((dt, idx) => (
<div key={idx} className="flex items-center gap-2">
<input
type="text"
value={dt}
onChange={e => {
const updated = [...form.custom_data_types]
updated[idx] = e.target.value
updateForm({ custom_data_types: updated })
}}
placeholder="Datentyp eingeben..."
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<button
onClick={() => updateForm({ custom_data_types: form.custom_data_types.filter((_, i) => i !== idx) })}
className="p-2 text-red-500 hover:bg-red-50 rounded-lg"
title="Entfernen"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
))}
<button
onClick={() => updateForm({ custom_data_types: [...form.custom_data_types, ''] })}
className="flex items-center gap-1 text-sm text-purple-600 hover:text-purple-700 font-medium"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
Weiteren Datentyp hinzufuegen
</button>
</div>
{form.data_categories.length > 0 && (
<div className="bg-purple-50 border border-purple-200 rounded-lg px-4 py-3 text-sm text-purple-800">
<span className="font-medium">{form.data_categories.length}</span> Datenkategorie{form.data_categories.length !== 1 ? 'n' : ''} ausgewaehlt
{form.data_categories.some(c => DATA_CATEGORY_GROUPS.find(g => g.art9)?.items.some(i => i.value === c)) && (
<span className="ml-2 text-orange-700 font-medium"> inkl. besonderer Kategorien (Art. 9)</span>
)}
</div>
)}
</div>
)}
{/* Step 3: Verarbeitungszweck */}
{currentStep === 3 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold text-gray-900">Zweck der Verarbeitung</h2>
<p className="text-sm text-gray-500">Waehlen Sie alle zutreffenden Verarbeitungszwecke. Die passende Rechtsgrundlage wird vom SDK automatisch ermittelt.</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{PURPOSE_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ purposes: toggleInArray(form.purposes, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.purposes.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
{form.purposes.includes('profiling') && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 text-sm text-amber-800">
<div className="font-medium mb-1">Hinweis: Profiling</div>
<p>Profiling unterliegt besonderen Anforderungen nach Art. 22 DSGVO. Betroffene haben das Recht auf Information und Widerspruch.</p>
</div>
)}
{form.purposes.includes('automated_decision') && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 text-sm text-red-800">
<div className="font-medium mb-1">Achtung: Automatisierte Entscheidung</div>
<p>Art. 22 DSGVO: Vollautomatisierte Entscheidungen mit rechtlicher Wirkung erfordern besondere Schutzmassnahmen, Informationspflichten und das Recht auf menschliche Ueberpruefung.</p>
</div>
)}
</div>
)}
{/* Step 4: Automatisierung */}
{currentStep === 4 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold text-gray-900">Grad der Automatisierung</h2>
<p className="text-sm text-gray-600">
Wie stark greift die KI in Entscheidungen ein? Je hoeher der Automatisierungsgrad, desto strenger die regulatorischen Anforderungen.
</p>
<div className="grid grid-cols-1 gap-3">
{AUTOMATION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ automation: item.value })}
className={`p-4 rounded-xl border-2 text-left transition-all ${
form.automation === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-3 mb-1">
<span className="text-2xl">{item.icon}</span>
<span className="text-sm font-semibold text-gray-900">{item.label}</span>
</div>
<p className="text-sm text-gray-500 ml-11">{item.desc}</p>
<p className="text-xs text-gray-400 ml-11 mt-1">Beispiele: {item.examples}</p>
</button>
))}
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-sm text-blue-800">
<div className="font-medium mb-1">Warum ist das wichtig?</div>
<p>
Art. 22 DSGVO regelt automatisierte Einzelentscheidungen. Vollautomatisierte Systeme, die Personen
erheblich beeinflussen (z.B. Kreditvergabe, Bewerbungsauswahl), unterliegen strengen Auflagen:
Informationspflicht, Recht auf menschliche Ueberpruefung und Anfechtungsmoeglichkeit.
</p>
</div>
</div>
)}
{/* Step 5: Hosting & Modell */}
{currentStep === 5 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Technische Details</h2>
{/* Hosting Provider */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Hosting-Anbieter</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{HOSTING_PROVIDER_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ hosting_provider: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.hosting_provider === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Hosting Region */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Hosting-Region</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{HOSTING_REGION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ hosting_region: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.hosting_region === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Model Usage */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-1">Wie wird das KI-Modell genutzt?</h3>
<p className="text-sm text-gray-500 mb-3">Waehlen Sie alle zutreffenden Optionen.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{MODEL_USAGE_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ model_usage: toggleInArray(form.model_usage, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.model_usage.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Info-Box: Begriffe erklaert */}
<details className="bg-amber-50 border border-amber-200 rounded-lg overflow-hidden">
<summary className="px-4 py-3 text-sm font-medium text-amber-800 cursor-pointer hover:bg-amber-100">
Begriffe erklaert: ML, DL, NLP, LLM &mdash; Was bedeutet das?
</summary>
<div className="px-4 pb-4 space-y-3 text-sm text-amber-900">
<div><span className="font-semibold">ML (Machine Learning)</span> &mdash; Computer lernt Muster aus Daten. Beispiel: Spam-Filter.</div>
<div><span className="font-semibold">DL (Deep Learning)</span> &mdash; ML mit neuronalen Netzen. Beispiel: Bilderkennung, Spracherkennung.</div>
<div><span className="font-semibold">NLP (Natural Language Processing)</span> &mdash; KI versteht Sprache. Beispiel: ChatGPT, DeepL.</div>
<div><span className="font-semibold">LLM (Large Language Model)</span> &mdash; Grosses Sprachmodell. Beispiel: GPT-4, Claude, Llama.</div>
<div><span className="font-semibold">RAG</span> &mdash; LLM erhaelt Kontext aus eigener Datenbank. Vorteil: Aktuelle, firmenspezifische Antworten.</div>
<div><span className="font-semibold">Fine-Tuning</span> &mdash; Bestehendes Modell mit eigenen Daten weitertrainieren. Achtung: Daten werden Teil des Modells.</div>
</div>
</details>
</div>
)}
{/* Step 6: Internationaler Datentransfer */}
{currentStep === 6 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Internationaler Datentransfer</h2>
<p className="text-sm text-gray-500">Wohin werden die Daten uebermittelt? Waehlen Sie alle zutreffenden Ziellaender/-regionen.</p>
{/* Transfer Targets */}
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Datentransfer-Ziele</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{TRANSFER_TARGET_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ transfer_targets: toggleInArray(form.transfer_targets, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.transfer_targets.includes(item.value)
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
{/* Transfer Mechanism — only if not "no_transfer" only */}
{form.transfer_targets.length > 0 && !form.transfer_targets.every(t => t === 'no_transfer') && (
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">Transfer-Mechanismus</h3>
<p className="text-sm text-gray-500 mb-3">Welche Schutzgarantie nutzen Sie fuer den Drittlandtransfer?</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{TRANSFER_MECHANISM_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ transfer_mechanism: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.transfer_mechanism === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
</div>
)}
{/* Specific countries text input */}
{form.transfer_targets.some(t => !['no_transfer'].includes(t)) && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Konkrete Ziellaender (optional)</label>
<input
type="text"
value={form.transfer_countries.join(', ')}
onChange={e => updateForm({ transfer_countries: e.target.value.split(',').map(s => s.trim()).filter(Boolean) })}
placeholder="z.B. USA, UK, Schweiz, Japan"
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<p className="text-xs text-gray-500 mt-1">Kommagetrennte Laendernamen oder -kuerzel</p>
</div>
)}
</div>
)}
{/* Step 7: Datenhaltung */}
{currentStep === 7 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Datenhaltung & Aufbewahrung</h2>
<p className="text-sm text-gray-500">Wie lange sollen die Daten gespeichert werden?</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{RETENTION_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ retention_period: item.value })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.retention_period === item.value
? 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Zweck der Aufbewahrung (optional)
</label>
<textarea
value={form.retention_purpose}
onChange={e => updateForm({ retention_purpose: e.target.value })}
rows={2}
placeholder="z.B. Vertragliche Pflichten, gesetzliche Aufbewahrungsfristen..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
{form.retention_period === 'indefinite' && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 text-sm text-amber-800">
<div className="font-medium mb-1">Hinweis: Unbefristete Speicherung</div>
<p>Die DSGVO fordert Datenminimierung und Speicherbegrenzung (Art. 5 Abs. 1e). Unbefristete Speicherung muss besonders gut begruendet sein.</p>
</div>
)}
</div>
)}
{/* Step 8: Vertraege & Compliance */}
{currentStep === 8 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold text-gray-900">Vertraege & Compliance-Dokumentation</h2>
<p className="text-sm text-gray-500">Welche Compliance-Dokumente liegen bereits vor? (Mehrfachauswahl moeglich)</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{CONTRACT_TILES.map(item => (
<button
key={item.value}
type="button"
onClick={() => updateForm({ contracts: toggleInArray(form.contracts, item.value) })}
className={`p-3 rounded-xl border-2 text-left transition-all ${
form.contracts.includes(item.value)
? item.value === 'none'
? 'border-amber-500 bg-amber-50 ring-1 ring-amber-300'
: 'border-purple-500 bg-purple-50 ring-1 ring-purple-300'
: 'border-gray-200 hover:border-purple-300 hover:bg-gray-50'
}`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{item.icon}</span>
<span className="text-sm font-medium text-gray-900">{item.label}</span>
</div>
<p className="text-xs text-gray-500 leading-tight">{item.desc}</p>
</button>
))}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Subprozessoren (optional)</label>
<textarea
value={form.subprocessors}
onChange={e => updateForm({ subprocessors: e.target.value })}
rows={2}
placeholder="z.B. OpenAI (USA, SCC), Hetzner Cloud (DE)..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
<Step1Basics form={form} updateForm={updateForm} profileIndustry={profileIndustry} />
)}
{currentStep === 2 && <Step2DataCategories form={form} updateForm={updateForm} />}
{currentStep === 3 && <Step3Purposes form={form} updateForm={updateForm} />}
{currentStep === 4 && <Step4Automation form={form} updateForm={updateForm} />}
{currentStep === 5 && <Step5Hosting form={form} updateForm={updateForm} />}
{currentStep === 6 && <Step6Transfer form={form} updateForm={updateForm} />}
{currentStep === 7 && <Step7Retention form={form} updateForm={updateForm} />}
{currentStep === 8 && <Step8Contracts form={form} updateForm={updateForm} />}
</div>
{/* Navigation Buttons */}
<div className="flex items-center justify-between">
<button
onClick={() => currentStep > 1 ? setCurrentStep(currentStep - 1) : router.push('/sdk/use-cases')}
className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
>
{currentStep === 1 ? 'Abbrechen' : 'Zurueck'}
</button>
{currentStep < 8 ? (
<button
onClick={() => setCurrentStep(currentStep + 1)}
disabled={currentStep === 1 && !form.title}
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50"
>
Weiter
</button>
) : (
<button
onClick={handleSubmit}
disabled={isSubmitting || !form.title}
className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
>
{isSubmitting ? (
<>
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
Bewerte...
</>
) : (
isEditMode ? 'Speichern & neu bewerten' : 'Assessment starten'
)}
</button>
)}
</div>
<NavigationButtons
currentStep={currentStep}
isSubmitting={isSubmitting}
isEditMode={isEditMode}
titleEmpty={!form.title}
onBack={() => currentStep > 1 ? setCurrentStep(currentStep - 1) : router.push('/sdk/use-cases')}
onNext={() => setCurrentStep(currentStep + 1)}
onSubmit={handleSubmit}
/>
</div>
)
}