This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/app/(admin)/compliance/obligations/page.tsx
BreakPilot Dev 660295e218 fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit
restores all missing components:

- Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education,
  infrastructure, communication, development, onboarding, rbac
- SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen,
  vendor-compliance, tom-generator, dsr, and more
- Developer portal (25 pages): API docs, SDK guides, frameworks
- All components, lib files, hooks, and types
- Updated package.json with all dependencies

The issue was caused by incomplete initial repository state - the full
admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2
but was never fully synced to the main admin-v2 directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 23:40:15 -08:00

1424 lines
61 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
/**
* Obligations Dashboard - Regulatory Obligations Overview
*
* Features:
* - Organization facts input for assessment
* - Applicable regulations based on sector/size
* - Obligations list with filtering (by regulation, deadline, responsible)
* - Incident reporting deadlines
* - Executive summary and PDF export
*/
import { useState, useEffect } from 'react'
import { PagePurpose } from '@/components/common/PagePurpose'
// Types matching the backend
interface Obligation {
id: string
regulation_id: string
title: string
description: string
legal_basis: { norm: string; article?: string }[]
category: string
responsible: string
deadline?: {
type: 'absolute' | 'relative'
date?: string
duration?: string
}
sanctions?: {
max_fine: string
personal_liability?: boolean
criminal?: boolean
}
evidence: string[]
priority: 'critical' | 'high' | 'medium' | 'low'
dependencies?: string[]
iso27001_mapping?: string[]
}
interface ApplicableRegulation {
id: string
name: string
classification: string
reason: string
obligation_count: number
}
interface IncidentDeadline {
regulation_id: string
phase: string
deadline: string
content: string
recipient: string
legal_basis: string
}
interface ExecutiveSummary {
total_regulations: number
total_obligations: number
critical_obligations: number
overdue_obligations: number
upcoming_deadlines: number
key_risks: string[]
recommended_actions: string[]
}
interface SanctionsSummary {
max_financial_risk: string
personal_liability_risk: boolean
criminal_risk: boolean
summary: string
}
interface ObligationsOverview {
id: string
organization_name: string
assessment_date: string
applicable_regulations: ApplicableRegulation[]
obligations: Obligation[]
incident_deadlines: IncidentDeadline[]
executive_summary: ExecutiveSummary
sanctions_summary: SanctionsSummary
}
// NIS2 Sector Options
const NIS2_SECTORS = {
annex_i: [
{ value: 'energy', label: 'Energie', description: 'Strom, Gas, Oel, Fernwaerme, Wasserstoff' },
{ value: 'transport', label: 'Verkehr', description: 'Luft, Schiene, Wasser, Strasse' },
{ value: 'banking', label: 'Bankwesen', description: 'Kreditinstitute' },
{ value: 'financial_markets', label: 'Finanzmarktinfrastruktur', description: 'Handelsplaetze, zentrale Gegenparteien' },
{ value: 'healthcare', label: 'Gesundheitswesen', description: 'Krankenhaeuser, Labore, Pharma' },
{ value: 'utilities', label: 'Trinkwasser', description: 'Trinkwasserversorgung' },
{ value: 'wastewater', label: 'Abwasser', description: 'Abwasserentsorgung' },
{ value: 'digital_infrastructure', label: 'Digitale Infrastruktur', description: 'IXP, DNS, TLD, Cloud, Rechenzentren' },
{ value: 'ict_services', label: 'ICT-Dienstleistungen', description: 'Managed Services, Managed Security' },
{ value: 'public_administration', label: 'Oeffentliche Verwaltung', description: 'Zentralregierung' },
{ value: 'space', label: 'Weltraum', description: 'Raumfahrtinfrastruktur' },
],
annex_ii: [
{ value: 'postal', label: 'Post- und Kurierdienste', description: 'Postdienstleister' },
{ value: 'waste_management', label: 'Abfallbewirtschaftung', description: 'Entsorgung, Recycling' },
{ value: 'chemicals', label: 'Chemie', description: 'Herstellung, Produktion, Vertrieb' },
{ value: 'food', label: 'Lebensmittel', description: 'Produktion, Verarbeitung, Grosshandel' },
{ value: 'manufacturing', label: 'Verarbeitendes Gewerbe', description: 'Medizinprodukte, IT, Fahrzeuge, Maschinen' },
{ value: 'digital_providers', label: 'Digitale Dienste', description: 'Online-Marktplaetze, Suchmaschinen, Social Media' },
{ value: 'research', label: 'Forschung', description: 'Forschungseinrichtungen' },
],
other: [
{ value: 'retail', label: 'Einzelhandel', description: 'Nicht NIS2-relevant' },
{ value: 'education', label: 'Bildung', description: 'Schulen, Universitaeten (ggf. oeffentlich)' },
{ value: 'other', label: 'Sonstige', description: 'Nicht in NIS2-Sektoren' },
],
}
// Size categories
const SIZE_CATEGORIES = [
{ value: 'micro', label: 'Kleinstunternehmen', description: '< 10 MA, < 2 Mio. EUR Umsatz' },
{ value: 'small', label: 'Kleines Unternehmen', description: '< 50 MA, < 10 Mio. EUR Umsatz' },
{ value: 'medium', label: 'Mittleres Unternehmen', description: '< 250 MA, < 50 Mio. EUR Umsatz' },
{ value: 'large', label: 'Grossunternehmen', description: '>= 250 MA oder >= 50 Mio. EUR Umsatz' },
]
// Special services (NIS2)
const SPECIAL_SERVICES = [
{ value: 'dns', label: 'DNS-Dienste', description: 'Authoritative oder rekursive DNS' },
{ value: 'tld', label: 'TLD-Registry', description: 'Top-Level-Domain Registrierung' },
{ value: 'cloud', label: 'Cloud-Computing', description: 'IaaS, PaaS, SaaS' },
{ value: 'datacenter', label: 'Rechenzentrum', description: 'Co-Location, Housing' },
{ value: 'cdn', label: 'CDN', description: 'Content Delivery Network' },
{ value: 'trust_services', label: 'Vertrauensdienste', description: 'QTSP, Zertifikate' },
{ value: 'msp', label: 'Managed Services', description: 'IT-Management fuer Dritte' },
{ value: 'mssp', label: 'Managed Security', description: 'Security Services fuer Dritte' },
]
// AI Act High-Risk Categories (Annex III)
const AI_HIGH_RISK_CATEGORIES = [
{ value: 'biometric', label: 'Biometrische Identifizierung', description: 'Gesichtserkennung, Fingerabdruck' },
{ value: 'critical_infrastructure', label: 'Kritische Infrastruktur', description: 'Wasser, Gas, Strom, Verkehr' },
{ value: 'education', label: 'Bildung & Ausbildung', description: 'Zulassung, Bewertung von Lernenden' },
{ value: 'employment', label: 'Beschaeftigung', description: 'Recruiting, Leistungsbewertung' },
{ value: 'public_services', label: 'Oeffentliche Dienste', description: 'Sozialleistungen, Kreditwuerdigkeit' },
{ value: 'law_enforcement', label: 'Strafverfolgung', description: 'Risikoeinschaetzung, Polygraph' },
{ value: 'migration', label: 'Migration & Asyl', description: 'Visumspruefung, Asylverfahren' },
{ value: 'justice', label: 'Justiz', description: 'Rechtsprechung, Streitbeilegung' },
]
// AI System Types
const AI_SYSTEM_TYPES = [
{ value: 'chatbot', label: 'Chatbot / Conversational AI', description: 'Kundendialog, Support' },
{ value: 'recommendation', label: 'Empfehlungssystem', description: 'Produkt-, Inhaltsempfehlungen' },
{ value: 'analytics', label: 'Predictive Analytics', description: 'Vorhersagen, Forecasting' },
{ value: 'document_processing', label: 'Dokumentenverarbeitung', description: 'OCR, NER, Klassifizierung' },
{ value: 'image_recognition', label: 'Bilderkennung', description: 'Objekterkennung, Qualitaetspruefung' },
{ value: 'voice_recognition', label: 'Spracherkennung', description: 'Speech-to-Text, Stimmanalyse' },
{ value: 'generative', label: 'Generative KI', description: 'Text-, Bild-, Code-Generierung' },
{ value: 'decision_support', label: 'Entscheidungsunterstuetzung', description: 'Automatisierte Entscheidungen' },
]
const PRIORITY_COLORS = {
critical: { bg: 'bg-red-100', text: 'text-red-800', border: 'border-red-300' },
high: { bg: 'bg-orange-100', text: 'text-orange-800', border: 'border-orange-300' },
medium: { bg: 'bg-yellow-100', text: 'text-yellow-800', border: 'border-yellow-300' },
low: { bg: 'bg-green-100', text: 'text-green-800', border: 'border-green-300' },
}
const CATEGORY_ICONS: Record<string, string> = {
meldepflicht: '📋',
governance: '👔',
technisch: '🔧',
personal: '👥',
lieferkette: '🔗',
audit: '📊',
}
export default function ObligationsPage() {
// Assessment input state
const [organizationName, setOrganizationName] = useState('')
const [sector, setSector] = useState('')
const [sizeCategory, setSizeCategory] = useState('')
const [specialServices, setSpecialServices] = useState<string[]>([])
const [isKritis, setIsKritis] = useState(false)
const [isPartOfGroup, setIsPartOfGroup] = useState(false)
const [processesPersonalData, setProcessesPersonalData] = useState(true)
const [usesAI, setUsesAI] = useState(false)
const [country, setCountry] = useState('DE')
// AI Act specific state
const [aiHighRiskCategories, setAiHighRiskCategories] = useState<string[]>([])
const [aiSystemTypes, setAiSystemTypes] = useState<string[]>([])
const [isGPAIProvider, setIsGPAIProvider] = useState(false)
const [aiUsesPublicSpaces, setAiUsesPublicSpaces] = useState(false)
const [aiAffectsEmployees, setAiAffectsEmployees] = useState(false)
// Export state
const [exporting, setExporting] = useState(false)
const [exportFormat, setExportFormat] = useState<'pdf' | 'markdown'>('pdf')
// Assessment result state
const [overview, setOverview] = useState<ObligationsOverview | null>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
// Filter state
const [filterRegulation, setFilterRegulation] = useState<string>('all')
const [filterPriority, setFilterPriority] = useState<string>('all')
const [filterResponsible, setFilterResponsible] = useState<string>('all')
const [activeTab, setActiveTab] = useState<'all' | 'by-deadline' | 'by-regulation' | 'by-responsible'>('all')
// Check for stored assessment
useEffect(() => {
const stored = localStorage.getItem('obligations_assessment')
if (stored) {
try {
setOverview(JSON.parse(stored))
} catch (e) {
console.error('Failed to parse stored assessment:', e)
}
}
}, [])
const runAssessment = async () => {
setLoading(true)
setError(null)
// Build facts from inputs
const facts = {
organization: {
employee_count: sizeCategory === 'micro' ? 5 : sizeCategory === 'small' ? 30 : sizeCategory === 'medium' ? 150 : 500,
annual_revenue: sizeCategory === 'micro' ? 1000000 : sizeCategory === 'small' ? 5000000 : sizeCategory === 'medium' ? 30000000 : 100000000,
country: country,
is_part_of_group: isPartOfGroup,
eu_member: ['DE', 'AT', 'FR', 'IT', 'ES', 'NL', 'BE', 'PL'].includes(country),
},
sector: {
primary_sector: sector,
special_services: specialServices,
is_kritis: isKritis,
kritis_threshold_met: isKritis,
},
data_protection: {
processes_personal_data: processesPersonalData,
},
ai_usage: {
uses_ai: usesAI,
high_risk_categories: aiHighRiskCategories,
system_types: aiSystemTypes,
is_gpai_provider: isGPAIProvider,
public_spaces_biometric: aiUsesPublicSpaces,
affects_employees: aiAffectsEmployees,
},
}
try {
// Try to call the SDK backend
const res = await fetch('/api/sdk/v1/ucca/obligations/assess', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
facts,
organization_name: organizationName,
}),
})
if (res.ok) {
const data = await res.json()
setOverview(data.overview)
localStorage.setItem('obligations_assessment', JSON.stringify(data.overview))
} else {
// Fallback: Generate mock data for demo
const mockOverview = generateMockOverview(organizationName, sector, sizeCategory, specialServices, isKritis, usesAI, aiHighRiskCategories)
setOverview(mockOverview)
localStorage.setItem('obligations_assessment', JSON.stringify(mockOverview))
}
} catch (err) {
console.error('Assessment failed:', err)
// Generate mock data for demo
const mockOverview = generateMockOverview(organizationName, sector, sizeCategory, specialServices, isKritis, usesAI, aiHighRiskCategories)
setOverview(mockOverview)
localStorage.setItem('obligations_assessment', JSON.stringify(mockOverview))
} finally {
setLoading(false)
}
}
const clearAssessment = () => {
setOverview(null)
localStorage.removeItem('obligations_assessment')
}
const exportMemo = async () => {
if (!overview) return
setExporting(true)
try {
// Try SDK export endpoint first
const res = await fetch('/api/sdk/v1/ucca/obligations/export/direct', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
overview: overview,
format: exportFormat,
language: 'de',
}),
})
if (res.ok) {
const data = await res.json()
if (exportFormat === 'pdf' && data.content) {
// Decode base64 and download PDF
const byteCharacters = atob(data.content)
const byteNumbers = new Array(byteCharacters.length)
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i)
}
const byteArray = new Uint8Array(byteNumbers)
const blob = new Blob([byteArray], { type: 'application/pdf' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = data.filename || `pflichten-memo-${new Date().toISOString().split('T')[0]}.pdf`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
setExporting(false)
return
} else if (data.content) {
// Download markdown
const blob = new Blob([data.content], { type: 'text/markdown;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = data.filename || `pflichten-memo-${new Date().toISOString().split('T')[0]}.md`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
setExporting(false)
return
}
}
} catch (err) {
console.error('SDK export failed, falling back to local export:', err)
}
// Fallback: Generate markdown locally
let content = `# Pflichten-Uebersicht fuer die Geschaeftsfuehrung\n\n`
content += `**Datum:** ${new Date(overview.assessment_date).toLocaleDateString('de-DE')}\n`
content += `**Organisation:** ${overview.organization_name || 'Nicht angegeben'}\n\n`
content += `---\n\n`
content += `## Executive Summary\n\n`
content += `| Kennzahl | Wert |\n`
content += `|----------|------|\n`
content += `| Anwendbare Regulierungen | ${overview.executive_summary.total_regulations} |\n`
content += `| Gesamtzahl Pflichten | ${overview.executive_summary.total_obligations} |\n`
content += `| Kritische Pflichten | ${overview.executive_summary.critical_obligations} |\n`
content += `| Ueberfaellige Pflichten | ${overview.executive_summary.overdue_obligations} |\n\n`
if (overview.executive_summary.key_risks.length > 0) {
content += `### Hauptrisiken\n\n`
overview.executive_summary.key_risks.forEach(risk => {
content += `- ${risk}\n`
})
content += `\n`
}
if (overview.executive_summary.recommended_actions.length > 0) {
content += `### Empfohlene Massnahmen\n\n`
overview.executive_summary.recommended_actions.forEach((action, i) => {
content += `${i + 1}. ${action}\n`
})
content += `\n`
}
content += `## Anwendbare Regulierungen\n\n`
overview.applicable_regulations.forEach(reg => {
content += `### ${reg.name}\n`
content += `- **Klassifizierung:** ${reg.classification}\n`
content += `- **Begruendung:** ${reg.reason}\n`
content += `- **Anzahl Pflichten:** ${reg.obligation_count}\n\n`
})
content += `## Sanktionsrisiken\n\n`
content += `${overview.sanctions_summary.summary}\n\n`
if (overview.sanctions_summary.max_financial_risk) {
content += `- **Maximales Bussgeld:** ${overview.sanctions_summary.max_financial_risk}\n`
}
if (overview.sanctions_summary.personal_liability_risk) {
content += `- **Persoenliche Haftung:** Ja\n`
}
content += `\n`
content += `## Kritische Pflichten\n\n`
overview.obligations
.filter(o => o.priority === 'critical')
.forEach(obl => {
content += `### ${obl.id}: ${obl.title}\n\n`
content += `${obl.description}\n\n`
content += `- **Verantwortlich:** ${obl.responsible}\n`
if (obl.deadline) {
content += `- **Frist:** ${obl.deadline.date || obl.deadline.duration}\n`
}
content += `\n`
})
if (overview.incident_deadlines.length > 0) {
content += `## Meldepflichten bei Sicherheitsvorfaellen\n\n`
content += `| Phase | Frist | Empfaenger |\n`
content += `|-------|-------|------------|\n`
overview.incident_deadlines.forEach(d => {
content += `| ${d.phase} | ${d.deadline} | ${d.recipient} |\n`
})
content += `\n`
}
content += `---\n\n`
content += `*Dieses Dokument wurde automatisch generiert und ersetzt keine Rechtsberatung.*\n`
// Download as markdown
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `pflichten-memo-${new Date().toISOString().split('T')[0]}.md`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
setExporting(false)
}
// Filter obligations
const filteredObligations = overview?.obligations.filter(o => {
if (filterRegulation !== 'all' && o.regulation_id !== filterRegulation) return false
if (filterPriority !== 'all' && o.priority !== filterPriority) return false
if (filterResponsible !== 'all' && o.responsible !== filterResponsible) return false
return true
}) || []
// Group obligations
const obligationsByRegulation = overview?.obligations.reduce((acc, o) => {
if (!acc[o.regulation_id]) acc[o.regulation_id] = []
acc[o.regulation_id].push(o)
return acc
}, {} as Record<string, Obligation[]>) || {}
const obligationsByResponsible = overview?.obligations.reduce((acc, o) => {
if (!acc[o.responsible]) acc[o.responsible] = []
acc[o.responsible].push(o)
return acc
}, {} as Record<string, Obligation[]>) || {}
// Get unique values for filters
const uniqueRegulations = [...new Set(overview?.obligations.map(o => o.regulation_id) || [])]
const uniqueResponsibles = [...new Set(overview?.obligations.map(o => o.responsible) || [])]
return (
<div className="space-y-6">
<PagePurpose
title="Pflichten-Uebersicht"
purpose="Aggregierte Uebersicht aller regulatorischen Pflichten aus NIS2, DSGVO, AI Act und weiteren Vorschriften. Basierend auf Ihren Unternehmensdaten werden automatisch anwendbare Pflichten, Fristen und Sanktionen ermittelt."
audience={['Geschaeftsfuehrung', 'DSB', 'CISO', 'Compliance Officer']}
gdprArticles={['NIS2 Art. 21', 'BSIG-E § 30-33', 'Art. 5 DSGVO']}
architecture={{
services: ['ai-compliance-sdk (Go)'],
databases: ['PostgreSQL'],
}}
relatedPages={[
{ name: 'Compliance Hub', href: '/compliance/hub', description: 'Zentrale Uebersicht' },
{ name: 'Controls', href: '/compliance/controls', description: 'Technische Massnahmen' },
{ name: 'AI Act', href: '/compliance/ai-act', description: 'KI-Risikoklassifizierung' },
]}
/>
{/* Assessment Form or Results */}
{!overview ? (
<div className="bg-white rounded-xl shadow-sm border p-6">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Pflichten-Assessment starten</h2>
<p className="text-sm text-slate-600 mb-6">
Geben Sie Ihre Unternehmensdaten ein, um automatisch zu ermitteln, welche regulatorischen Pflichten fuer Sie gelten.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Organization Name */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Organisationsname (optional)
</label>
<input
type="text"
value={organizationName}
onChange={(e) => setOrganizationName(e.target.value)}
placeholder="z.B. Muster GmbH"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
{/* Country */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Land
</label>
<select
value={country}
onChange={(e) => setCountry(e.target.value)}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
<option value="DE">Deutschland</option>
<option value="AT">Oesterreich</option>
<option value="CH">Schweiz</option>
<option value="FR">Frankreich</option>
<option value="IT">Italien</option>
<option value="NL">Niederlande</option>
</select>
</div>
{/* Sector */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-slate-700 mb-2">
Branche/Sektor
</label>
<select
value={sector}
onChange={(e) => setSector(e.target.value)}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
<option value="">Bitte waehlen...</option>
<optgroup label="NIS2 Anhang I (Besonders wichtig)">
{NIS2_SECTORS.annex_i.map(s => (
<option key={s.value} value={s.value}>{s.label} - {s.description}</option>
))}
</optgroup>
<optgroup label="NIS2 Anhang II (Wichtig)">
{NIS2_SECTORS.annex_ii.map(s => (
<option key={s.value} value={s.value}>{s.label} - {s.description}</option>
))}
</optgroup>
<optgroup label="Sonstige">
{NIS2_SECTORS.other.map(s => (
<option key={s.value} value={s.value}>{s.label} - {s.description}</option>
))}
</optgroup>
</select>
</div>
{/* Size Category */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Unternehmensgroesse
</label>
<select
value={sizeCategory}
onChange={(e) => setSizeCategory(e.target.value)}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
<option value="">Bitte waehlen...</option>
{SIZE_CATEGORIES.map(s => (
<option key={s.value} value={s.value}>{s.label} - {s.description}</option>
))}
</select>
</div>
{/* Part of Group */}
<div className="flex items-center gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={isPartOfGroup}
onChange={(e) => setIsPartOfGroup(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">Teil eines Konzerns</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={isKritis}
onChange={(e) => setIsKritis(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">KRITIS-Betreiber</span>
</label>
</div>
{/* Special Services */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-slate-700 mb-2">
Besondere Dienste (NIS2 Annex I)
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
{SPECIAL_SERVICES.map(service => (
<label key={service.value} className="flex items-center gap-2 cursor-pointer p-2 rounded-lg border border-slate-200 hover:bg-slate-50">
<input
type="checkbox"
checked={specialServices.includes(service.value)}
onChange={(e) => {
if (e.target.checked) {
setSpecialServices([...specialServices, service.value])
} else {
setSpecialServices(specialServices.filter(s => s !== service.value))
}
}}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">{service.label}</span>
</label>
))}
</div>
</div>
{/* Additional flags */}
<div className="md:col-span-2 flex flex-wrap gap-6">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={processesPersonalData}
onChange={(e) => setProcessesPersonalData(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">Verarbeitet personenbezogene Daten (DSGVO)</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={usesAI}
onChange={(e) => setUsesAI(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">Setzt KI-Systeme ein (AI Act)</span>
</label>
</div>
{/* AI Act Specific Questions - shown when usesAI is true */}
{usesAI && (
<>
<div className="md:col-span-2 border-t pt-6 mt-2">
<h3 className="text-md font-semibold text-slate-900 mb-4 flex items-center gap-2">
<span className="text-xl">🤖</span> AI Act Details
</h3>
{/* AI System Types */}
<div className="mb-6">
<label className="block text-sm font-medium text-slate-700 mb-2">
Welche KI-Systeme setzen Sie ein?
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
{AI_SYSTEM_TYPES.map(type => (
<label key={type.value} className="flex items-center gap-2 cursor-pointer p-2 rounded-lg border border-slate-200 hover:bg-slate-50">
<input
type="checkbox"
checked={aiSystemTypes.includes(type.value)}
onChange={(e) => {
if (e.target.checked) {
setAiSystemTypes([...aiSystemTypes, type.value])
} else {
setAiSystemTypes(aiSystemTypes.filter(s => s !== type.value))
}
}}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<div>
<span className="text-sm text-slate-700">{type.label}</span>
<p className="text-xs text-slate-500">{type.description}</p>
</div>
</label>
))}
</div>
</div>
{/* High-Risk Categories */}
<div className="mb-6">
<label className="block text-sm font-medium text-slate-700 mb-2">
In welchen Hochrisiko-Bereichen setzen Sie KI ein? (Annex III AI Act)
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
{AI_HIGH_RISK_CATEGORIES.map(cat => (
<label key={cat.value} className="flex items-center gap-2 cursor-pointer p-2 rounded-lg border border-slate-200 hover:bg-slate-50">
<input
type="checkbox"
checked={aiHighRiskCategories.includes(cat.value)}
onChange={(e) => {
if (e.target.checked) {
setAiHighRiskCategories([...aiHighRiskCategories, cat.value])
} else {
setAiHighRiskCategories(aiHighRiskCategories.filter(s => s !== cat.value))
}
}}
className="w-4 h-4 text-orange-600 rounded focus:ring-orange-500"
/>
<div>
<span className="text-sm text-slate-700">{cat.label}</span>
<p className="text-xs text-slate-500">{cat.description}</p>
</div>
</label>
))}
</div>
</div>
{/* Additional AI flags */}
<div className="flex flex-wrap gap-6">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={isGPAIProvider}
onChange={(e) => setIsGPAIProvider(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">Anbieter von General-Purpose AI (z.B. LLM)</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={aiUsesPublicSpaces}
onChange={(e) => setAiUsesPublicSpaces(e.target.checked)}
className="w-4 h-4 text-red-600 rounded focus:ring-red-500"
/>
<span className="text-sm text-slate-700">Biometrische Identifizierung im oeffentlichen Raum</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={aiAffectsEmployees}
onChange={(e) => setAiAffectsEmployees(e.target.checked)}
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span className="text-sm text-slate-700">KI-Entscheidungen betreffen Mitarbeiter</span>
</label>
</div>
</div>
</>
)}
</div>
{/* Submit Button */}
<div className="mt-6">
<button
onClick={runAssessment}
disabled={loading || !sector || !sizeCategory}
className="px-6 py-3 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? (
<span className="flex items-center gap-2">
<svg className="animate-spin h-5 w-5" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Analysiere...
</span>
) : (
'Pflichten ermitteln'
)}
</button>
</div>
</div>
) : (
<>
{/* Results Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-slate-900">
Pflichten-Assessment: {overview.organization_name || 'Unbenannt'}
</h2>
<p className="text-sm text-slate-500">
Erstellt am {new Date(overview.assessment_date).toLocaleDateString('de-DE')}
</p>
</div>
<div className="flex gap-2 items-center">
{/* Export Format Toggle */}
<div className="flex rounded-lg border border-slate-300 overflow-hidden">
<button
onClick={() => setExportFormat('pdf')}
className={`px-3 py-2 text-sm ${exportFormat === 'pdf' ? 'bg-purple-600 text-white' : 'bg-white text-slate-600 hover:bg-slate-50'}`}
>
PDF
</button>
<button
onClick={() => setExportFormat('markdown')}
className={`px-3 py-2 text-sm ${exportFormat === 'markdown' ? 'bg-purple-600 text-white' : 'bg-white text-slate-600 hover:bg-slate-50'}`}
>
Markdown
</button>
</div>
<button
onClick={exportMemo}
disabled={exporting}
className="px-4 py-2 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{exporting ? (
<>
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Exportiere...
</>
) : (
<>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Exportieren
</>
)}
</button>
<button
onClick={clearAssessment}
className="px-4 py-2 bg-slate-100 text-slate-700 rounded-lg text-sm hover:bg-slate-200"
>
Neues Assessment
</button>
</div>
</div>
{/* Executive Summary Cards */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div className="bg-white rounded-xl shadow-sm border p-4">
<p className="text-sm text-slate-500">Regulierungen</p>
<p className="text-3xl font-bold text-blue-600">{overview.executive_summary.total_regulations}</p>
<p className="text-xs text-slate-500">anwendbar</p>
</div>
<div className="bg-white rounded-xl shadow-sm border p-4">
<p className="text-sm text-slate-500">Pflichten</p>
<p className="text-3xl font-bold text-purple-600">{overview.executive_summary.total_obligations}</p>
<p className="text-xs text-slate-500">gesamt</p>
</div>
<div className="bg-white rounded-xl shadow-sm border p-4">
<p className="text-sm text-slate-500">Kritisch</p>
<p className="text-3xl font-bold text-red-600">{overview.executive_summary.critical_obligations}</p>
<p className="text-xs text-slate-500">hoechste Prioritaet</p>
</div>
<div className="bg-white rounded-xl shadow-sm border p-4">
<p className="text-sm text-slate-500">Ueberfaellig</p>
<p className="text-3xl font-bold text-orange-600">{overview.executive_summary.overdue_obligations}</p>
<p className="text-xs text-slate-500">sofort handeln</p>
</div>
<div className="bg-white rounded-xl shadow-sm border p-4">
<p className="text-sm text-slate-500">Max. Risiko</p>
<p className="text-xl font-bold text-red-600">{overview.sanctions_summary.max_financial_risk}</p>
<p className="text-xs text-slate-500">
{overview.sanctions_summary.personal_liability_risk ? '+ pers. Haftung' : ''}
</p>
</div>
</div>
{/* Applicable Regulations */}
<div className="bg-white rounded-xl shadow-sm border p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Anwendbare Regulierungen</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{overview.applicable_regulations.map(reg => (
<div key={reg.id} className="p-4 rounded-lg border border-slate-200 bg-slate-50">
<div className="flex items-center justify-between mb-2">
<span className="font-medium text-slate-900">{reg.name}</span>
<span className="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs">
{reg.obligation_count} Pflichten
</span>
</div>
<p className="text-sm text-slate-600 mb-2">
<span className="font-medium">Klassifizierung:</span> {reg.classification}
</p>
<p className="text-xs text-slate-500">{reg.reason}</p>
</div>
))}
</div>
</div>
{/* Incident Deadlines */}
{overview.incident_deadlines.length > 0 && (
<div className="bg-white rounded-xl shadow-sm border p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Meldepflichten bei Sicherheitsvorfaellen</h3>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-slate-50">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Phase</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Frist</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Empfaenger</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Inhalt</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-200">
{overview.incident_deadlines.map((deadline, i) => (
<tr key={i} className="hover:bg-slate-50">
<td className="px-4 py-3 font-medium text-slate-900">{deadline.phase}</td>
<td className="px-4 py-3">
<span className="px-2 py-1 bg-red-100 text-red-700 rounded-full text-sm font-medium">
{deadline.deadline}
</span>
</td>
<td className="px-4 py-3 text-slate-600">{deadline.recipient}</td>
<td className="px-4 py-3 text-sm text-slate-500">{deadline.content}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{/* Obligations List with Filters */}
<div className="bg-white rounded-xl shadow-sm border">
<div className="p-4 border-b">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<h3 className="text-lg font-semibold text-slate-900">Pflichten ({filteredObligations.length})</h3>
<div className="flex flex-wrap gap-2">
<select
value={filterRegulation}
onChange={(e) => setFilterRegulation(e.target.value)}
className="px-3 py-1.5 text-sm border border-slate-300 rounded-lg"
>
<option value="all">Alle Regulierungen</option>
{uniqueRegulations.map(r => (
<option key={r} value={r}>{r.toUpperCase()}</option>
))}
</select>
<select
value={filterPriority}
onChange={(e) => setFilterPriority(e.target.value)}
className="px-3 py-1.5 text-sm border border-slate-300 rounded-lg"
>
<option value="all">Alle Prioritaeten</option>
<option value="critical">Kritisch</option>
<option value="high">Hoch</option>
<option value="medium">Mittel</option>
<option value="low">Niedrig</option>
</select>
<select
value={filterResponsible}
onChange={(e) => setFilterResponsible(e.target.value)}
className="px-3 py-1.5 text-sm border border-slate-300 rounded-lg"
>
<option value="all">Alle Verantwortlichen</option>
{uniqueResponsibles.map(r => (
<option key={r} value={r}>{r}</option>
))}
</select>
</div>
</div>
</div>
{/* Obligations Table */}
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-slate-50">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">ID</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Pflicht</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Kategorie</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Verantwortlich</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Frist</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Prioritaet</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Sanktion</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-200">
{filteredObligations.map(obl => {
const priorityColors = PRIORITY_COLORS[obl.priority]
return (
<tr key={obl.id} className="hover:bg-slate-50">
<td className="px-4 py-3">
<span className="font-mono text-sm text-purple-600">{obl.id}</span>
</td>
<td className="px-4 py-3">
<p className="font-medium text-slate-900">{obl.title}</p>
<p className="text-xs text-slate-500 mt-1 line-clamp-2">{obl.description}</p>
</td>
<td className="px-4 py-3">
<span className="flex items-center gap-1">
<span>{CATEGORY_ICONS[obl.category.toLowerCase()] || '📋'}</span>
<span className="text-sm text-slate-600 capitalize">{obl.category}</span>
</span>
</td>
<td className="px-4 py-3 text-sm text-slate-600">{obl.responsible}</td>
<td className="px-4 py-3">
{obl.deadline ? (
<span className="text-sm text-slate-600">
{obl.deadline.date
? new Date(obl.deadline.date).toLocaleDateString('de-DE')
: obl.deadline.duration}
</span>
) : (
<span className="text-sm text-slate-400">-</span>
)}
</td>
<td className="px-4 py-3">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${priorityColors.bg} ${priorityColors.text}`}>
{obl.priority === 'critical' ? 'Kritisch' :
obl.priority === 'high' ? 'Hoch' :
obl.priority === 'medium' ? 'Mittel' : 'Niedrig'}
</span>
</td>
<td className="px-4 py-3">
{obl.sanctions ? (
<div>
<p className="text-sm font-medium text-red-600">{obl.sanctions.max_fine}</p>
{obl.sanctions.personal_liability && (
<p className="text-xs text-red-500">+ pers. Haftung</p>
)}
</div>
) : (
<span className="text-sm text-slate-400">-</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* Key Risks and Recommendations */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Key Risks */}
<div className="bg-red-50 border border-red-200 rounded-xl p-6">
<h3 className="font-semibold text-red-800 flex items-center gap-2 mb-4">
<span></span> Hauptrisiken
</h3>
<ul className="space-y-2">
{overview.executive_summary.key_risks.map((risk, i) => (
<li key={i} className="flex items-start gap-2 text-sm text-red-700">
<span className="text-red-500"></span>
{risk}
</li>
))}
</ul>
</div>
{/* Recommendations */}
<div className="bg-green-50 border border-green-200 rounded-xl p-6">
<h3 className="font-semibold text-green-800 flex items-center gap-2 mb-4">
<span></span> Empfohlene Massnahmen
</h3>
<ol className="space-y-2">
{overview.executive_summary.recommended_actions.map((action, i) => (
<li key={i} className="flex items-start gap-2 text-sm text-green-700">
<span className="font-medium text-green-600">{i + 1}.</span>
{action}
</li>
))}
</ol>
</div>
</div>
</>
)}
</div>
)
}
// Mock data generator for demo purposes
function generateMockOverview(
orgName: string,
sector: string,
sizeCategory: string,
specialServices: string[],
isKritis: boolean,
usesAI: boolean = false,
aiHighRiskCategories: string[] = []
): ObligationsOverview {
const isEssential = sizeCategory === 'large' || isKritis || specialServices.length > 0
const isImportant = sizeCategory === 'medium' && !isEssential
const isNIS2Affected = isEssential || isImportant
const isAIHighRisk = usesAI && aiHighRiskCategories.length > 0
const regulations: ApplicableRegulation[] = [
{
id: 'dsgvo',
name: 'DSGVO',
classification: 'Anwendbar',
reason: 'Verarbeitung personenbezogener Daten',
obligation_count: 8,
},
]
if (isNIS2Affected) {
regulations.push({
id: 'nis2',
name: 'NIS2-Richtlinie / BSIG-E',
classification: isEssential ? 'Besonders wichtige Einrichtung' : 'Wichtige Einrichtung',
reason: isEssential
? 'Sektor in Anhang I und Groessenkriterien erfuellt'
: 'Sektor in Anhang II und mittleres Unternehmen',
obligation_count: isEssential ? 14 : 12,
})
}
if (usesAI) {
regulations.push({
id: 'ai_act',
name: 'EU AI Act',
classification: isAIHighRisk ? 'Hochrisiko-KI' : 'Begrenztes Risiko',
reason: isAIHighRisk
? 'KI-Einsatz in Hochrisiko-Kategorien (Annex III)'
: 'KI-Einsatz mit Transparenzpflichten',
obligation_count: isAIHighRisk ? 8 : 3,
})
}
const obligations: Obligation[] = []
// NIS2 Obligations
if (isNIS2Affected) {
obligations.push(
{
id: 'NIS2-OBL-001',
regulation_id: 'nis2',
title: 'BSI-Registrierung',
description: 'Registrierung beim BSI ueber das Meldeportal mit Kontaktdaten, IP-Bereichen und Zustaendigkeiten.',
legal_basis: [{ norm: '§ 33 BSIG-E' }],
category: 'Meldepflicht',
responsible: 'Geschaeftsfuehrung',
deadline: { type: 'absolute', date: '2025-01-17' },
sanctions: { max_fine: '500.000 EUR' },
evidence: ['Registrierungsbestaetigung BSI'],
priority: 'critical',
},
{
id: 'NIS2-OBL-002',
regulation_id: 'nis2',
title: 'Risikomanagement-Massnahmen',
description: 'Implementierung angemessener technischer und organisatorischer Massnahmen zur Beherrschung von Risiken.',
legal_basis: [{ norm: 'Art. 21 NIS2' }, { norm: '§ 30 BSIG-E' }],
category: 'Technisch',
responsible: 'CISO',
deadline: { type: 'relative', duration: '18 Monate nach Inkrafttreten' },
sanctions: { max_fine: '10 Mio. EUR oder 2% Jahresumsatz', personal_liability: true },
evidence: ['ISMS-Dokumentation', 'Risikoanalyse'],
priority: 'high',
},
{
id: 'NIS2-OBL-003',
regulation_id: 'nis2',
title: 'Schulung Leitungsorgane',
description: 'Regelmaessige Schulung der Geschaeftsfuehrung zu Cyberrisiken und Risikomanagement.',
legal_basis: [{ norm: 'Art. 20 Abs. 2 NIS2' }],
category: 'Personal',
responsible: 'Geschaeftsfuehrung',
deadline: { type: 'relative', duration: 'Jaehrlich' },
sanctions: { max_fine: '10 Mio. EUR', personal_liability: true },
evidence: ['Schulungsnachweise'],
priority: 'high',
},
{
id: 'NIS2-OBL-004',
regulation_id: 'nis2',
title: 'Incident-Meldeprozess',
description: 'Etablierung eines 24h/72h/1M Meldeprozesses fuer Sicherheitsvorfaelle an das BSI.',
legal_basis: [{ norm: '§ 32 BSIG-E' }],
category: 'Meldepflicht',
responsible: 'CISO',
sanctions: { max_fine: '500.000 EUR' },
evidence: ['Meldeprozess-Dokumentation', 'Erreichbarkeit 24/7'],
priority: 'critical',
},
{
id: 'NIS2-OBL-005',
regulation_id: 'nis2',
title: 'Supply-Chain-Security',
description: 'Sicherheitsanforderungen an Lieferanten und Dienstleister vertraglich festlegen.',
legal_basis: [{ norm: 'Art. 21 Abs. 2 lit. d NIS2' }],
category: 'Lieferkette',
responsible: 'Einkauf / CISO',
evidence: ['Lieferantenvertraege', 'Sicherheitsanforderungen'],
priority: 'medium',
}
)
if (isEssential) {
obligations.push({
id: 'NIS2-OBL-006',
regulation_id: 'nis2',
title: 'Regelmaessige Sicherheitspruefungen',
description: 'Durchfuehrung regelmaessiger Audits und Penetrationstests (nur besonders wichtige Einrichtungen).',
legal_basis: [{ norm: '§ 39 BSIG-E' }],
category: 'Audit',
responsible: 'CISO',
deadline: { type: 'relative', duration: 'Alle 2 Jahre' },
sanctions: { max_fine: '10 Mio. EUR' },
evidence: ['Audit-Berichte', 'Pentest-Reports'],
priority: 'high',
})
}
}
// DSGVO Obligations
obligations.push(
{
id: 'DSGVO-OBL-001',
regulation_id: 'dsgvo',
title: 'Verarbeitungsverzeichnis fuehren',
description: 'Dokumentation aller Verarbeitungstaetigkeiten gemaess Art. 30 DSGVO.',
legal_basis: [{ norm: 'Art. 30 DSGVO' }],
category: 'Governance',
responsible: 'DSB',
sanctions: { max_fine: '10 Mio. EUR oder 2% Jahresumsatz' },
evidence: ['VVT-Dokumentation'],
priority: 'high',
},
{
id: 'DSGVO-OBL-002',
regulation_id: 'dsgvo',
title: 'Technische und organisatorische Massnahmen',
description: 'Implementierung angemessener TOMs zum Schutz personenbezogener Daten.',
legal_basis: [{ norm: 'Art. 32 DSGVO' }],
category: 'Technisch',
responsible: 'IT-Leitung',
sanctions: { max_fine: '10 Mio. EUR' },
evidence: ['TOM-Dokumentation'],
priority: 'high',
},
{
id: 'DSGVO-OBL-003',
regulation_id: 'dsgvo',
title: 'Datenschutz-Folgenabschaetzung',
description: 'DSFA bei Verarbeitungen mit hohem Risiko fuer Betroffene.',
legal_basis: [{ norm: 'Art. 35 DSGVO' }],
category: 'Governance',
responsible: 'DSB',
sanctions: { max_fine: '10 Mio. EUR' },
evidence: ['DSFA-Dokumentation'],
priority: 'medium',
}
)
// AI Act Obligations
if (usesAI) {
// Transparency obligations for all AI systems
obligations.push({
id: 'AIACT-OBL-001',
regulation_id: 'ai_act',
title: 'Transparenzpflichten',
description: 'Nutzer muessen informiert werden, dass sie mit einem KI-System interagieren.',
legal_basis: [{ norm: 'Art. 50 AI Act' }],
category: 'Compliance',
responsible: 'KI-Verantwortlicher',
deadline: { type: 'absolute', date: '2025-08-02' },
sanctions: { max_fine: '15 Mio. EUR oder 3% Jahresumsatz' },
evidence: ['Transparenzhinweise', 'Nutzerschnittstellen-Screenshots'],
priority: 'high',
})
if (isAIHighRisk) {
// High-risk AI obligations
obligations.push(
{
id: 'AIACT-OBL-002',
regulation_id: 'ai_act',
title: 'Risikomanagement-System',
description: 'Einrichtung, Dokumentation und Pflege eines Risikomanagement-Systems fuer das KI-System.',
legal_basis: [{ norm: 'Art. 9 AI Act' }],
category: 'Governance',
responsible: 'KI-Verantwortlicher',
deadline: { type: 'absolute', date: '2026-08-02' },
sanctions: { max_fine: '35 Mio. EUR oder 7% Jahresumsatz' },
evidence: ['Risikomanagement-Dokumentation', 'Risikoregister'],
priority: 'critical',
},
{
id: 'AIACT-OBL-003',
regulation_id: 'ai_act',
title: 'Datengovernance',
description: 'Sicherstellung hoher Datenqualitaet fuer Training, Validierung und Tests.',
legal_basis: [{ norm: 'Art. 10 AI Act' }],
category: 'Technisch',
responsible: 'Data Engineer / KI-Team',
sanctions: { max_fine: '35 Mio. EUR oder 7% Jahresumsatz' },
evidence: ['Datenqualitaetsberichte', 'Bias-Analysen'],
priority: 'high',
},
{
id: 'AIACT-OBL-004',
regulation_id: 'ai_act',
title: 'Technische Dokumentation',
description: 'Erstellung umfassender technischer Dokumentation vor Inbetriebnahme.',
legal_basis: [{ norm: 'Art. 11 AI Act' }],
category: 'Governance',
responsible: 'KI-Verantwortlicher',
sanctions: { max_fine: '35 Mio. EUR oder 7% Jahresumsatz' },
evidence: ['Technische Dokumentation', 'System-Architektur'],
priority: 'high',
},
{
id: 'AIACT-OBL-005',
regulation_id: 'ai_act',
title: 'Menschliche Aufsicht',
description: 'Massnahmen fuer wirksame menschliche Aufsicht waehrend des Betriebs.',
legal_basis: [{ norm: 'Art. 14 AI Act' }],
category: 'Governance',
responsible: 'KI-Verantwortlicher',
sanctions: { max_fine: '35 Mio. EUR oder 7% Jahresumsatz' },
evidence: ['Aufsichtskonzept', 'Eingriffsprozesse'],
priority: 'critical',
},
{
id: 'AIACT-OBL-006',
regulation_id: 'ai_act',
title: 'Grundrechte-Folgenabschaetzung (FRIA)',
description: 'Durchfuehrung einer Folgenabschaetzung fuer Grundrechte vor Einsatz.',
legal_basis: [{ norm: 'Art. 27 AI Act' }],
category: 'Compliance',
responsible: 'KI-Verantwortlicher',
deadline: { type: 'relative', duration: 'Vor Inbetriebnahme' },
sanctions: { max_fine: '15 Mio. EUR oder 3% Jahresumsatz' },
evidence: ['FRIA-Dokumentation'],
priority: 'critical',
}
)
}
}
const incidentDeadlines: IncidentDeadline[] = isNIS2Affected ? [
{
regulation_id: 'nis2',
phase: 'Fruehwarnung',
deadline: '24 Stunden',
content: 'Erste Meldung ueber erheblichen Sicherheitsvorfall',
recipient: 'BSI',
legal_basis: '§ 32 Abs. 1 BSIG-E',
},
{
regulation_id: 'nis2',
phase: 'Vorfallmeldung',
deadline: '72 Stunden',
content: 'Schweregrad, IoCs, erste Bewertung',
recipient: 'BSI',
legal_basis: '§ 32 Abs. 2 BSIG-E',
},
{
regulation_id: 'nis2',
phase: 'Abschlussbericht',
deadline: '1 Monat',
content: 'Root Cause, ergriffene Massnahmen, Auswirkungen',
recipient: 'BSI',
legal_basis: '§ 32 Abs. 3 BSIG-E',
},
{
regulation_id: 'dsgvo',
phase: 'Meldung Datenschutzverletzung',
deadline: '72 Stunden',
content: 'Meldung bei Verletzung des Schutzes personenbezogener Daten',
recipient: 'Aufsichtsbehoerde',
legal_basis: 'Art. 33 DSGVO',
},
] : [
{
regulation_id: 'dsgvo',
phase: 'Meldung Datenschutzverletzung',
deadline: '72 Stunden',
content: 'Meldung bei Verletzung des Schutzes personenbezogener Daten',
recipient: 'Aufsichtsbehoerde',
legal_basis: 'Art. 33 DSGVO',
},
]
return {
id: `assessment-${Date.now()}`,
organization_name: orgName || 'Unbenannte Organisation',
assessment_date: new Date().toISOString(),
applicable_regulations: regulations,
obligations: obligations,
incident_deadlines: incidentDeadlines,
executive_summary: {
total_regulations: regulations.length,
total_obligations: obligations.length,
critical_obligations: obligations.filter(o => o.priority === 'critical').length,
overdue_obligations: 0,
upcoming_deadlines: obligations.filter(o => o.deadline?.date && new Date(o.deadline.date) < new Date('2025-03-01')).length,
key_risks: [
...(isNIS2Affected ? [
'BSI-Registrierung bis 17.01.2025 erforderlich',
'Persoenliche Haftung der Geschaeftsfuehrung bei Verstoessen',
'Meldepflichten mit kurzen Fristen (24h/72h)',
] : []),
...(isAIHighRisk ? [
'Hochrisiko-KI erfordert umfassende Dokumentation und Risikomanagement',
'Grundrechte-Folgenabschaetzung (FRIA) vor Inbetriebnahme erforderlich',
'Bussgelder bis 35 Mio. EUR oder 7% Jahresumsatz bei Verstoessen',
] : []),
...(usesAI && !isAIHighRisk ? [
'KI-Transparenzpflichten ab August 2025',
] : []),
'DSGVO-Dokumentationspflichten muessen erfuellt werden',
'Bei Datenschutzverletzungen: 72h Meldefrist',
],
recommended_actions: [
...(isNIS2Affected ? [
'Sofortige BSI-Registrierung einleiten',
'ISMS nach ISO 27001 oder BSI IT-Grundschutz aufbauen',
'Incident-Response-Plan mit 24h-Erreichbarkeit etablieren',
] : []),
...(isAIHighRisk ? [
'KI-Risikomanagement-System aufbauen',
'Technische Dokumentation fuer KI-Systeme erstellen',
'FRIA durchfuehren und dokumentieren',
'Menschliche Aufsicht implementieren',
] : []),
...(usesAI && !isAIHighRisk ? [
'Transparenzhinweise fuer KI-Interaktionen implementieren',
] : []),
'Verarbeitungsverzeichnis erstellen/aktualisieren',
'TOMs dokumentieren und regelmaessig pruefen',
],
},
sanctions_summary: {
max_financial_risk: isAIHighRisk
? '35 Mio. EUR oder 7% Jahresumsatz'
: (isNIS2Affected ? '10 Mio. EUR oder 2% Jahresumsatz' : '10 Mio. EUR'),
personal_liability_risk: isNIS2Affected,
criminal_risk: false,
summary: [
isNIS2Affected ? 'Hohe Sanktionsrisiken durch NIS2.' : '',
isAIHighRisk ? 'Sehr hohe Bussgelder durch AI Act bei Hochrisiko-KI.' : '',
usesAI && !isAIHighRisk ? 'Moderate Bussgelder bei AI Act Transparenzverletzungen.' : '',
'DSGVO-Bussgelder bis 10 Mio. EUR oder 2% des Jahresumsatzes.',
isNIS2Affected ? 'Persoenliche Haftung der Geschaeftsfuehrung moeglich.' : '',
].filter(s => s).join(' '),
},
}
}