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 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

423 lines
14 KiB
TypeScript

'use client'
// =============================================================================
// Step 5: Risk & Protection Level
// CIA assessment and protection level determination
// =============================================================================
import React, { useState, useEffect } from 'react'
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
import {
RiskProfile,
CIARating,
ProtectionLevel,
calculateProtectionLevel,
isDSFARequired,
} from '@/lib/sdk/tom-generator/types'
// =============================================================================
// CONSTANTS
// =============================================================================
const CIA_LEVELS: { value: CIARating; label: string; description: string }[] = [
{ value: 1, label: 'Sehr gering', description: 'Kein nennenswerter Schaden bei Verletzung' },
{ value: 2, label: 'Gering', description: 'Begrenzter, beherrschbarer Schaden' },
{ value: 3, label: 'Mittel', description: 'Erheblicher Schaden, aber kompensierbar' },
{ value: 4, label: 'Hoch', description: 'Schwerwiegender Schaden, schwer kompensierbar' },
{ value: 5, label: 'Sehr hoch', description: 'Existenzbedrohender oder irreversibler Schaden' },
]
const REGULATORY_REQUIREMENTS = [
'DSGVO',
'BDSG',
'MaRisk (Finanz)',
'BAIT (Finanz)',
'PSD2 (Zahlungsdienste)',
'SGB (Gesundheit)',
'MDR (Medizinprodukte)',
'TISAX (Automotive)',
'KRITIS (Kritische Infrastruktur)',
'NIS2',
'ISO 27001',
'SOC 2',
]
// =============================================================================
// CIA SLIDER COMPONENT
// =============================================================================
interface CIASliderProps {
label: string
description: string
value: CIARating
onChange: (value: CIARating) => void
}
function CIASlider({ label, description, value, onChange }: CIASliderProps) {
const level = CIA_LEVELS.find((l) => l.value === value)
const getColor = (v: CIARating) => {
if (v <= 2) return 'bg-green-500'
if (v === 3) return 'bg-yellow-500'
return 'bg-red-500'
}
return (
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex justify-between items-start mb-3">
<div>
<h4 className="font-medium text-gray-900">{label}</h4>
<p className="text-sm text-gray-500">{description}</p>
</div>
<span className={`px-3 py-1 rounded-full text-sm font-medium text-white ${getColor(value)}`}>
{level?.label}
</span>
</div>
<input
type="range"
min="1"
max="5"
value={value}
onChange={(e) => onChange(parseInt(e.target.value) as CIARating)}
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600"
/>
<div className="flex justify-between mt-1 text-xs text-gray-400">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
</div>
<p className="text-sm text-gray-600 mt-2 italic">{level?.description}</p>
</div>
)
}
// =============================================================================
// PROTECTION LEVEL DISPLAY
// =============================================================================
interface ProtectionLevelDisplayProps {
level: ProtectionLevel
}
function ProtectionLevelDisplay({ level }: ProtectionLevelDisplayProps) {
const config: Record<ProtectionLevel, { label: string; color: string; bg: string; description: string }> = {
NORMAL: {
label: 'Normal',
color: 'text-green-800',
bg: 'bg-green-100',
description: 'Standard-Schutzmaßnahmen ausreichend',
},
HIGH: {
label: 'Hoch',
color: 'text-yellow-800',
bg: 'bg-yellow-100',
description: 'Erweiterte Schutzmaßnahmen erforderlich',
},
VERY_HIGH: {
label: 'Sehr hoch',
color: 'text-red-800',
bg: 'bg-red-100',
description: 'Höchste Schutzmaßnahmen erforderlich',
},
}
const { label, color, bg, description } = config[level]
return (
<div className={`${bg} rounded-lg p-4 border`}>
<div className="flex items-center gap-3">
<div className={`text-2xl font-bold ${color}`}>{label}</div>
</div>
<p className={`text-sm ${color} mt-1`}>{description}</p>
</div>
)
}
// =============================================================================
// COMPONENT
// =============================================================================
export function RiskProtectionStep() {
const { state, setRiskProfile, completeCurrentStep } = useTOMGenerator()
const [formData, setFormData] = useState<Partial<RiskProfile>>({
ciaAssessment: {
confidentiality: 3,
integrity: 3,
availability: 3,
justification: '',
},
protectionLevel: 'HIGH',
specialRisks: [],
regulatoryRequirements: ['DSGVO'],
hasHighRiskProcessing: false,
dsfaRequired: false,
})
const [specialRiskInput, setSpecialRiskInput] = useState('')
// Load existing data
useEffect(() => {
if (state.riskProfile) {
setFormData(state.riskProfile)
}
}, [state.riskProfile])
// Calculate protection level when CIA changes
useEffect(() => {
if (formData.ciaAssessment) {
const level = calculateProtectionLevel(formData.ciaAssessment)
const dsfaReq = isDSFARequired(state.dataProfile, {
...formData,
protectionLevel: level,
} as RiskProfile)
setFormData((prev) => ({
...prev,
protectionLevel: level,
dsfaRequired: dsfaReq,
}))
}
}, [formData.ciaAssessment, state.dataProfile])
// Handle CIA changes
const handleCIAChange = (field: 'confidentiality' | 'integrity' | 'availability', value: CIARating) => {
setFormData((prev) => ({
...prev,
ciaAssessment: {
...prev.ciaAssessment!,
[field]: value,
},
}))
}
// Handle regulatory requirements toggle
const toggleRequirement = (req: string) => {
setFormData((prev) => {
const current = prev.regulatoryRequirements || []
const updated = current.includes(req)
? current.filter((r) => r !== req)
: [...current, req]
return { ...prev, regulatoryRequirements: updated }
})
}
// Handle special risk addition
const addSpecialRisk = () => {
if (specialRiskInput.trim()) {
setFormData((prev) => ({
...prev,
specialRisks: [...(prev.specialRisks || []), specialRiskInput.trim()],
}))
setSpecialRiskInput('')
}
}
const removeSpecialRisk = (index: number) => {
setFormData((prev) => ({
...prev,
specialRisks: (prev.specialRisks || []).filter((_, i) => i !== index),
}))
}
// Handle submit
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
const profile: RiskProfile = {
ciaAssessment: formData.ciaAssessment!,
protectionLevel: formData.protectionLevel || 'HIGH',
specialRisks: formData.specialRisks || [],
regulatoryRequirements: formData.regulatoryRequirements || [],
hasHighRiskProcessing: formData.hasHighRiskProcessing || false,
dsfaRequired: formData.dsfaRequired || false,
}
setRiskProfile(profile)
completeCurrentStep(profile)
}
return (
<form onSubmit={handleSubmit} className="space-y-8">
{/* CIA Assessment */}
<div>
<h3 className="text-lg font-medium text-gray-900 mb-2">CIA-Bewertung</h3>
<p className="text-sm text-gray-600 mb-4">
Bewerten Sie die Schutzziele für Ihre Datenverarbeitung. Was passiert, wenn die Vertraulichkeit,
Integrität oder Verfügbarkeit der Daten beeinträchtigt wird?
</p>
<div className="space-y-4">
<CIASlider
label="Vertraulichkeit (Confidentiality)"
description="Schutz vor unbefugtem Zugriff auf Daten"
value={formData.ciaAssessment?.confidentiality || 3}
onChange={(v) => handleCIAChange('confidentiality', v)}
/>
<CIASlider
label="Integrität (Integrity)"
description="Schutz vor unbefugter Änderung von Daten"
value={formData.ciaAssessment?.integrity || 3}
onChange={(v) => handleCIAChange('integrity', v)}
/>
<CIASlider
label="Verfügbarkeit (Availability)"
description="Sicherstellung des Zugriffs auf Daten"
value={formData.ciaAssessment?.availability || 3}
onChange={(v) => handleCIAChange('availability', v)}
/>
</div>
{/* Justification */}
<div className="mt-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Begründung der Bewertung
</label>
<textarea
value={formData.ciaAssessment?.justification || ''}
onChange={(e) =>
setFormData((prev) => ({
...prev,
ciaAssessment: {
...prev.ciaAssessment!,
justification: e.target.value,
},
}))
}
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
placeholder="Beschreiben Sie kurz, warum Sie diese Bewertung gewählt haben..."
/>
</div>
</div>
{/* Calculated Protection Level */}
<div>
<h3 className="text-lg font-medium text-gray-900 mb-2">Ermittelter Schutzbedarf</h3>
<p className="text-sm text-gray-600 mb-4">
Basierend auf Ihrer CIA-Bewertung ergibt sich folgender Schutzbedarf:
</p>
<ProtectionLevelDisplay level={formData.protectionLevel || 'HIGH'} />
</div>
{/* DSFA Indicator */}
{formData.dsfaRequired && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<div className="flex gap-3">
<svg className="w-6 h-6 text-red-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<div>
<h4 className="font-medium text-red-900">DSFA erforderlich</h4>
<p className="text-sm text-red-700 mt-1">
Aufgrund Ihrer Datenverarbeitung (besondere Kategorien, Minderjährige oder sehr hoher Schutzbedarf)
ist eine Datenschutz-Folgenabschätzung nach Art. 35 DSGVO erforderlich.
</p>
</div>
</div>
</div>
)}
{/* High Risk Processing */}
<div>
<label className="flex items-center gap-3 cursor-pointer p-3 border rounded-lg hover:bg-gray-50">
<input
type="checkbox"
checked={formData.hasHighRiskProcessing || false}
onChange={(e) => setFormData((prev) => ({ ...prev, hasHighRiskProcessing: e.target.checked }))}
className="w-5 h-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<div>
<span className="text-gray-700 font-medium">Hochrisiko-Verarbeitung</span>
<p className="text-sm text-gray-500">
z.B. Profiling, automatisierte Entscheidungen, systematische Überwachung
</p>
</div>
</label>
</div>
{/* Special Risks */}
<div>
<h3 className="text-lg font-medium text-gray-900 mb-2">Besondere Risiken</h3>
<p className="text-sm text-gray-600 mb-4">
Identifizieren Sie spezifische Risiken Ihrer Datenverarbeitung.
</p>
<div className="flex gap-2 mb-3">
<input
type="text"
value={specialRiskInput}
onChange={(e) => setSpecialRiskInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && (e.preventDefault(), addSpecialRisk())}
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
placeholder="z.B. Cloud-Abhängigkeit, Insider-Bedrohungen"
/>
<button
type="button"
onClick={addSpecialRisk}
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200"
>
Hinzufügen
</button>
</div>
{formData.specialRisks && formData.specialRisks.length > 0 && (
<div className="flex flex-wrap gap-2">
{formData.specialRisks.map((risk, index) => (
<span
key={index}
className="inline-flex items-center gap-1 px-3 py-1 bg-red-100 text-red-800 rounded-full text-sm"
>
{risk}
<button
type="button"
onClick={() => removeSpecialRisk(index)}
className="hover:text-red-600"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</span>
))}
</div>
)}
</div>
{/* Regulatory Requirements */}
<div>
<h3 className="text-lg font-medium text-gray-900 mb-2">Regulatorische Anforderungen</h3>
<p className="text-sm text-gray-600 mb-4">
Welche regulatorischen Anforderungen gelten für Ihre Datenverarbeitung?
</p>
<div className="flex flex-wrap gap-2">
{REGULATORY_REQUIREMENTS.map((req) => (
<button
key={req}
type="button"
onClick={() => toggleRequirement(req)}
className={`px-4 py-2 rounded-full border text-sm font-medium transition-all ${
formData.regulatoryRequirements?.includes(req)
? 'bg-blue-100 border-blue-300 text-blue-800'
: 'bg-white border-gray-300 text-gray-600 hover:border-gray-400'
}`}
>
{req}
</button>
))}
</div>
</div>
</form>
)
}
export default RiskProtectionStep