'use client' /** * DSFA - Datenschutz-Folgenabschätzung * * Art. 35 DSGVO - Datenschutz-Folgenabschätzung * * Migriert auf SDK API: /sdk/v1/dsgvo/dsfa */ import { useState, useEffect } from 'react' import { PagePurpose } from '@/components/common/PagePurpose' interface DSFARisk { id: string category: string // confidentiality, integrity, availability, rights_freedoms description: string likelihood: string // low, medium, high impact: string // low, medium, high risk_level: string // low, medium, high, very_high affected_data?: string[] } interface DSFAMitigation { id: string risk_id: string description: string type: string // technical, organizational, legal status: string // planned, in_progress, implemented, verified implemented_at?: string residual_risk: string // low, medium, high responsible_party: string } interface DSFA { id: string tenant_id: string namespace_id?: string processing_activity_id?: string name: string description: string processing_description: string necessity_assessment: string proportionality_assessment: string risks: DSFARisk[] mitigations: DSFAMitigation[] dpo_consulted: boolean dpo_opinion?: string authority_consulted: boolean authority_reference?: string status: string // draft, in_progress, completed, approved, rejected overall_risk_level: string // low, medium, high, very_high conclusion: string created_at: string updated_at: string created_by: string approved_by?: string approved_at?: string } export default function DSFAPage() { const [dsfas, setDsfas] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [expandedProject, setExpandedProject] = useState(null) const [activeTab, setActiveTab] = useState<'projects' | 'methodology'>('projects') const [showCreateModal, setShowCreateModal] = useState(false) const [newDsfa, setNewDsfa] = useState({ name: '', description: '', processing_description: '', necessity_assessment: '', proportionality_assessment: '', overall_risk_level: 'medium', status: 'draft', conclusion: '' }) useEffect(() => { loadDSFAs() }, []) async function loadDSFAs() { setLoading(true) setError(null) try { const res = await fetch('/sdk/v1/dsgvo/dsfa', { headers: { 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', 'X-User-ID': localStorage.getItem('bp_user_id') || '', } }) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } const data = await res.json() setDsfas(data.dsfas || []) if ((data.dsfas || []).length > 0) { setExpandedProject(data.dsfas[0].id) } } catch (err) { console.error('Failed to load DSFAs:', err) setError('Fehler beim Laden der DSFAs') } finally { setLoading(false) } } async function createDSFA() { try { const res = await fetch('/sdk/v1/dsgvo/dsfa', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', 'X-User-ID': localStorage.getItem('bp_user_id') || '', }, body: JSON.stringify(newDsfa) }) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } setShowCreateModal(false) setNewDsfa({ name: '', description: '', processing_description: '', necessity_assessment: '', proportionality_assessment: '', overall_risk_level: 'medium', status: 'draft', conclusion: '' }) loadDSFAs() } catch (err) { console.error('Failed to create DSFA:', err) alert('Fehler beim Erstellen der DSFA') } } async function deleteDSFA(id: string) { if (!confirm('DSFA wirklich löschen?')) return try { const res = await fetch(`/sdk/v1/dsgvo/dsfa/${id}`, { method: 'DELETE', headers: { 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', 'X-User-ID': localStorage.getItem('bp_user_id') || '', } }) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } loadDSFAs() } catch (err) { console.error('Failed to delete DSFA:', err) alert('Fehler beim Löschen') } } async function exportDSFA(id: string) { try { const res = await fetch(`/sdk/v1/dsgvo/dsfa/${id}/export`, { headers: { 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', 'X-User-ID': localStorage.getItem('bp_user_id') || '', } }) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } const blob = await res.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `dsfa-export.json` a.click() window.URL.revokeObjectURL(url) } catch (err) { console.error('Export failed:', err) alert('Export fehlgeschlagen') } } const getStatusBadge = (status: string) => { switch (status) { case 'completed': return Abgeschlossen case 'approved': return Genehmigt case 'in_progress': return In Bearbeitung case 'draft': return Entwurf case 'rejected': return Abgelehnt default: return null } } const getRiskBadge = (level: string) => { switch (level) { case 'very_high': return Sehr hoch case 'high': return Hoch case 'medium': return Mittel case 'low': return Niedrig default: return null } } const getCategoryLabel = (cat: string) => { const labels: Record = { 'confidentiality': 'Vertraulichkeit', 'integrity': 'Integrität', 'availability': 'Verfügbarkeit', 'rights_freedoms': 'Rechte der Betroffenen', } return labels[cat] || cat } if (loading) { return (
Lade DSFAs...
) } return (
{error && (
{error}
)} {/* Tabs */}
{[ { id: 'projects', label: 'DSFA-Projekte' }, { id: 'methodology', label: 'Methodik' }, ].map(tab => ( ))}
{activeTab === 'projects' && ( )}
{/* Projects Tab */} {activeTab === 'projects' && (
{/* Statistics */}
{dsfas.length}
DSFA-Projekte
{dsfas.filter(d => d.status === 'completed' || d.status === 'approved').length}
Abgeschlossen
{dsfas.filter(d => d.status === 'in_progress').length}
In Bearbeitung
{dsfas.filter(d => d.overall_risk_level === 'high' || d.overall_risk_level === 'very_high').length}
Hohes Risiko
{/* DSFA List */} {dsfas.length === 0 ? (
⚠️

Keine DSFAs vorhanden

Erstellen Sie eine Datenschutz-Folgenabschätzung für Verarbeitungen mit hohem Risiko.

) : (
{dsfas.map(dsfa => (
{expandedProject === dsfa.id && (
{/* Left: Assessments */}
{dsfa.processing_description && (

Verarbeitungsbeschreibung

{dsfa.processing_description}

)} {dsfa.necessity_assessment && (

Notwendigkeitsbewertung

{dsfa.necessity_assessment}

)} {dsfa.conclusion && (

Fazit

{dsfa.conclusion}

)}
{/* Right: Meta */}

Erstellt

{new Date(dsfa.created_at).toLocaleDateString('de-DE')}

Aktualisiert

{new Date(dsfa.updated_at).toLocaleDateString('de-DE')}

DSB-Konsultation

{dsfa.dpo_consulted ? 'Ja' : 'Ausstehend'}

Aufsichtsbehörde

{dsfa.authority_consulted ? 'Konsultiert' : 'Nicht konsultiert'}

{dsfa.dpo_opinion && (

DSB-Stellungnahme

{dsfa.dpo_opinion}

)}
{/* Risks */} {dsfa.risks && dsfa.risks.length > 0 && (

Identifizierte Risiken

{dsfa.risks.map(risk => ( ))}
Kategorie Beschreibung Risiko
{getCategoryLabel(risk.category)} {risk.description} {getRiskBadge(risk.risk_level)}
)} {/* Mitigations */} {dsfa.mitigations && dsfa.mitigations.length > 0 && (

Maßnahmen

{dsfa.mitigations.map(mit => (
{mit.description}
{mit.type === 'technical' ? 'Technisch' : mit.type === 'organizational' ? 'Organisatorisch' : 'Rechtlich'} {getStatusBadge(mit.status)}
Restrisiko
{getRiskBadge(mit.residual_risk)}
))}
)}
)}
))}
)}
)} {/* Methodology Tab */} {activeTab === 'methodology' && (

DSFA-Prozess nach Art. 35 DSGVO

{[ { step: 1, title: 'Schwellwertanalyse', description: 'Prüfung ob eine DSFA erforderlich ist anhand der Kriterien aus Art. 35 Abs. 3 und der DSK-Positivliste.', details: ['Verarbeitung besonderer Kategorien (Art. 9)?', 'Systematisches Profiling?', 'Neue Technologien im Einsatz?', 'Daten von Minderjährigen?'] }, { step: 2, title: 'Beschreibung der Verarbeitung', description: 'Systematische Beschreibung der geplanten Verarbeitungsvorgänge und Zwecke.', details: ['Art, Umfang, Umstände der Verarbeitung', 'Zweck der Verarbeitung', 'Betroffene Personengruppen', 'Verantwortlichkeiten'] }, { step: 3, title: 'Notwendigkeit & Verhältnismäßigkeit', description: 'Bewertung ob die Verarbeitung notwendig und verhältnismäßig ist.', details: ['Rechtsgrundlage vorhanden?', 'Zweckbindung eingehalten?', 'Datenminimierung beachtet?', 'Speicherbegrenzung definiert?'] }, { step: 4, title: 'Risikobewertung', description: 'Systematische Bewertung der Risiken für Rechte und Freiheiten der Betroffenen.', details: ['Risiken identifizieren', 'Eintrittswahrscheinlichkeit bewerten', 'Schwere der Auswirkungen bewerten', 'Risiko-Score berechnen'] }, { step: 5, title: 'Abhilfemaßnahmen', description: 'Definition von Maßnahmen zur Eindämmung der identifizierten Risiken.', details: ['Technische Maßnahmen (TOMs)', 'Organisatorische Maßnahmen', 'Restrisiko-Bewertung', 'Implementierungsplan'] }, { step: 6, title: 'DSB-Konsultation', description: 'Einholung der Stellungnahme des Datenschutzbeauftragten.', details: ['DSFA dem DSB vorlegen', 'Stellungnahme dokumentieren', 'Ggf. Anpassungen vornehmen', 'Freigabe erteilen'] }, { step: 7, title: 'Vorherige Konsultation (Art. 36)', description: 'Bei verbleibendem hohen Risiko: Konsultation der Aufsichtsbehörde.', details: ['Nur bei hohem Restrisiko erforderlich', 'Aufsichtsbehörde hat 8 Wochen zur Prüfung', 'Dokumentation der Konsultation', 'Umsetzung der Auflagen'] } ].map(item => (
{item.step}

{item.title}

{item.description}

    {item.details.map((detail, idx) => (
  • {detail}
  • ))}
))}
{/* When is DSFA required */}

Wann ist eine DSFA erforderlich?

Art. 35 Abs. 3 - Pflichtfälle:

  • Systematische Bewertung persönlicher Aspekte (Profiling)
  • Umfangreiche Verarbeitung besonderer Kategorien (Art. 9)
  • Systematische Überwachung öffentlicher Bereiche

Zusätzliche Kriterien (DSK-Liste):

  • Verarbeitung von Daten Minderjähriger
  • Einsatz neuer Technologien (z.B. KI)
  • Zusammenführung von Datensätzen
  • Automatisierte Entscheidungsfindung
)} {/* Info */}

Wichtiger Hinweis

Eine DSFA ist vor Beginn der Verarbeitung durchzuführen. Bei wesentlichen Änderungen an bestehenden Verarbeitungen muss die DSFA aktualisiert werden. Die Dokumentation muss der Aufsichtsbehörde auf Anfrage vorgelegt werden können.

{/* Create Modal */} {showCreateModal && (

Neue DSFA erstellen

setNewDsfa({ ...newDsfa, name: e.target.value })} className="w-full border border-slate-300 rounded-lg px-3 py-2" placeholder="z.B. KI-gestützte Korrektur und Bewertung" />