Files
breakpilot-compliance/admin-compliance/components/sdk/dsfa/Art22AssessmentPanel.tsx
Benjamin Admin 308d559c85
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 38s
CI / test-python-backend-compliance (push) Successful in 33s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 19s
feat: DSFA Section 8 KI-Anwendungsfälle + Bundesland RAG-Ingest
- Migration 028: ai_use_case_modules JSONB + section_8_complete auf compliance_dsfas
- Neues ai-use-case-types.ts: AIUseCaseModule Interface, 8 Typen, Art22Assessment,
  AI Act Risikoklassen, WP248-Kriterien, Privacy by Design, createEmptyModule() Helper
- types.ts: Section 8 in DSFA_SECTIONS, ai_use_case_modules im DSFA Interface,
  section_8_complete in DSFASectionProgress
- api.ts: addAIUseCaseModule, updateAIUseCaseModule, removeAIUseCaseModule
- 5 neue UI-Komponenten: AIUseCaseTypeSelector, Art22AssessmentPanel,
  AIRiskCriteriaChecklist, AIUseCaseModuleEditor (7 Tabs), AIUseCaseSection
- DSFASidebar: Section 8 Eintrag + calculateSectionProgress case 8
- ReviewScheduleSection: ai_use_case_module Trigger-Typ ergänzt
- page.tsx: Section 8 Rendering + Weiter-Button auf activeSection < 8 + KI-Module Counter
- scripts/ingest-dsfa-bundesland.sh: WP248 + alle 17 Behörden → bp_dsfa_corpus
- Docs: dsfa.md Section 8 + RAG-Corpus, Developer Portal DSFA mit AI-Modul-Code-Beispielen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 09:20:27 +01:00

150 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React from 'react'
import { Art22Assessment, Art22ExceptionType } from '@/lib/sdk/dsfa/ai-use-case-types'
interface Art22AssessmentPanelProps {
assessment: Art22Assessment
onChange: (updated: Art22Assessment) => void
readonly?: boolean
}
const EXCEPTION_OPTIONS: { value: Art22ExceptionType; label: string; description: string }[] = [
{
value: 'contract',
label: 'Art. 22 Abs. 2 lit. a Vertragserfüllung',
description: 'Die Entscheidung ist für den Abschluss oder die Erfüllung eines Vertrags erforderlich',
},
{
value: 'legal',
label: 'Art. 22 Abs. 2 lit. b Rechtliche Verpflichtung',
description: 'Die Entscheidung ist durch Unionsrecht oder mitgliedstaatliches Recht zugelassen',
},
{
value: 'consent',
label: 'Art. 22 Abs. 2 lit. c Ausdrückliche Einwilligung',
description: 'Die betroffene Person hat ausdrücklich eingewilligt',
},
]
export function Art22AssessmentPanel({ assessment, onChange, readonly }: Art22AssessmentPanelProps) {
const missingRequiredSafeguards = assessment.applies &&
!assessment.safeguards.some(s => s.id === 'human_review' && s.implemented)
return (
<div className="space-y-4">
{/* Art. 22 Toggle */}
<div className="flex items-start gap-3 p-4 rounded-xl border border-gray-200 bg-gray-50">
<input
type="checkbox"
id="art22_applies"
checked={assessment.applies}
onChange={e => onChange({ ...assessment, applies: e.target.checked })}
disabled={readonly}
className="mt-1 h-4 w-4 rounded border-gray-300 text-purple-600 focus:ring-purple-500"
/>
<label htmlFor="art22_applies" className="flex-1">
<div className="font-medium text-gray-900">
Automatisierte Einzelentscheidung mit Rechtswirkung (Art. 22 DSGVO)
</div>
<p className="text-xs text-gray-500 mt-0.5">
Das KI-System trifft Entscheidungen, die rechtliche Wirkung oder ähnlich erhebliche
Auswirkungen auf Personen haben, ohne maßgebliche menschliche Beteiligung.
</p>
</label>
</div>
{/* Warning if applies without safeguards */}
{missingRequiredSafeguards && (
<div className="flex items-start gap-2 p-3 bg-red-50 border border-red-200 rounded-lg">
<svg className="w-4 h-4 text-red-500 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<p className="text-sm text-red-700">
Art. 22 gilt als anwendbar, aber die erforderliche Schutzmaßnahme Recht auf menschliche Überprüfung" ist nicht implementiert.
</p>
</div>
)}
{assessment.applies && (
<>
{/* Justification */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Begründung der Ausnahme (Art. 22 Abs. 2)
</label>
<textarea
value={assessment.justification || ''}
onChange={e => onChange({ ...assessment, justification: e.target.value })}
disabled={readonly}
rows={3}
placeholder="Begründen Sie, auf welcher Rechtsgrundlage die automatisierte Entscheidung zulässig ist..."
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none"
/>
</div>
{/* Exception Type */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Ausnahmetatbestand</label>
<div className="space-y-2">
{EXCEPTION_OPTIONS.map(opt => (
<label key={opt.value} className="flex items-start gap-3 p-3 rounded-lg border border-gray-200 hover:bg-gray-50 cursor-pointer">
<input
type="radio"
name="art22_exception"
value={opt.value}
checked={assessment.exception_type === opt.value}
onChange={() => onChange({ ...assessment, exception_type: opt.value })}
disabled={readonly}
className="mt-0.5 h-4 w-4 text-purple-600"
/>
<div>
<div className="text-sm font-medium text-gray-900">{opt.label}</div>
<p className="text-xs text-gray-500 mt-0.5">{opt.description}</p>
</div>
</label>
))}
</div>
</div>
{/* Safeguards */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Schutzmaßnahmen (Art. 22 Abs. 3 DSGVO)
</label>
<div className="space-y-2">
{assessment.safeguards.map((safeguard, idx) => (
<label key={safeguard.id} className="flex items-start gap-3 p-3 rounded-lg border border-gray-200 hover:bg-gray-50 cursor-pointer">
<input
type="checkbox"
checked={safeguard.implemented}
onChange={e => {
const updated = assessment.safeguards.map((s, i) =>
i === idx ? { ...s, implemented: e.target.checked } : s
)
onChange({ ...assessment, safeguards: updated })
}}
disabled={readonly}
className="mt-0.5 h-4 w-4 rounded border-gray-300 text-purple-600"
/>
<div>
<div className={`text-sm font-medium ${safeguard.id === 'human_review' && !safeguard.implemented ? 'text-red-700' : 'text-gray-900'}`}>
{safeguard.label}
{safeguard.id === 'human_review' && (
<span className="ml-1 text-xs text-red-500">*Pflicht</span>
)}
</div>
{safeguard.description && (
<p className="text-xs text-gray-500 mt-0.5">{safeguard.description}</p>
)}
</div>
</label>
))}
</div>
</div>
</>
)}
</div>
)
}