feat: DSFA pre-fill from Company Profile + Scope answers
- New prefill-from-scope.ts utility: - headquartersState → federal_state (Bundesland for authority lookup) - data_art9 → special data categories (Gesundheit, Biometrie, etc.) - data_minors → adds "Minderjährige" to data subjects + raises risk - proc_adm_scoring → Art. 22 affected rights + measures - proc_ai_usage → involves_ai flag + AI measures - proc_video_surveillance → video data categories - industry/businessModel → processing purpose + legal basis - isDSFARequired() check: shows red banner when Art. 35 triggers detected - GeneratorWizard accepts prefill prop, initializes all fields - Passes federal_state, involves_ai, legal_basis to backend POST Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,16 +2,24 @@
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import type { DSFA } from './DSFACard'
|
||||
import type { DSFAPrefillResult } from '@/lib/sdk/dsfa/prefill-from-scope'
|
||||
|
||||
export function GeneratorWizard({ onClose, onSubmit }: { onClose: () => void; onSubmit: (data: Partial<DSFA>) => Promise<void> }) {
|
||||
interface GeneratorWizardProps {
|
||||
onClose: () => void
|
||||
onSubmit: (data: Partial<DSFA>) => Promise<void>
|
||||
prefill?: DSFAPrefillResult | null
|
||||
}
|
||||
|
||||
export function GeneratorWizard({ onClose, onSubmit, prefill }: GeneratorWizardProps) {
|
||||
const [step, setStep] = useState(1)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [title, setTitle] = useState('')
|
||||
const [description, setDescription] = useState('')
|
||||
const [processingActivity, setProcessingActivity] = useState('')
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([])
|
||||
const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high' | 'critical'>('low')
|
||||
const [selectedMeasures, setSelectedMeasures] = useState<string[]>([])
|
||||
const [title, setTitle] = useState(prefill?.title || '')
|
||||
const [description, setDescription] = useState(prefill?.description || '')
|
||||
const [processingActivity, setProcessingActivity] = useState(prefill?.processingActivity || '')
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>(prefill?.dataCategories || [])
|
||||
const riskMap2: Record<string, 'low' | 'medium' | 'high' | 'critical'> = { niedrig: 'low', mittel: 'medium', hoch: 'high', kritisch: 'critical' }
|
||||
const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high' | 'critical'>(riskMap2[prefill?.riskLevel || ''] || 'low')
|
||||
const [selectedMeasures, setSelectedMeasures] = useState<string[]>(prefill?.measures || [])
|
||||
|
||||
const riskMap: Record<string, 'low' | 'medium' | 'high' | 'critical'> = {
|
||||
Niedrig: 'low', Mittel: 'medium', Hoch: 'high', Kritisch: 'critical',
|
||||
@@ -28,7 +36,10 @@ export function GeneratorWizard({ onClose, onSubmit }: { onClose: () => void; on
|
||||
riskLevel,
|
||||
measures: selectedMeasures,
|
||||
status: 'draft',
|
||||
})
|
||||
...(prefill?.federalState ? { federal_state: prefill.federalState } : {}),
|
||||
...(prefill?.involvesAi ? { involves_ai: true } : {}),
|
||||
...(prefill?.legalBasis ? { legal_basis: prefill.legalBasis } : {}),
|
||||
} as Partial<DSFA>)
|
||||
onClose()
|
||||
} finally {
|
||||
setSaving(false)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { useState, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useSDK } from '@/lib/sdk'
|
||||
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
|
||||
import { DocumentUploadSection, type UploadedDocument } from '@/components/sdk'
|
||||
import { DSFACard, type DSFA } from './_components/DSFACard'
|
||||
import { GeneratorWizard } from './_components/GeneratorWizard'
|
||||
import { prefillDSFAFromScope, isDSFARequired } from '@/lib/sdk/dsfa/prefill-from-scope'
|
||||
|
||||
export default function DSFAPage() {
|
||||
const router = useRouter()
|
||||
@@ -17,6 +18,14 @@ export default function DSFAPage() {
|
||||
const [showGenerator, setShowGenerator] = useState(false)
|
||||
const [filter, setFilter] = useState<string>('all')
|
||||
|
||||
// Pre-fill from Company Profile + Scope answers
|
||||
const scopeAnswers = state.complianceScope?.answers || []
|
||||
const prefill = useMemo(
|
||||
() => prefillDSFAFromScope(state.companyProfile || null, scopeAnswers),
|
||||
[state.companyProfile, scopeAnswers]
|
||||
)
|
||||
const dsfaCheck = useMemo(() => isDSFARequired(scopeAnswers), [scopeAnswers])
|
||||
|
||||
const loadDSFAs = useCallback(async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
@@ -120,10 +129,27 @@ export default function DSFAPage() {
|
||||
)}
|
||||
</StepHeader>
|
||||
|
||||
{/* DSFA Requirement Check */}
|
||||
{dsfaCheck.required && dsfas.length === 0 && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-xl p-5">
|
||||
<h3 className="font-semibold text-red-800">DSFA erforderlich (Art. 35 DSGVO)</h3>
|
||||
<p className="text-sm text-red-700 mt-1">Basierend auf Ihrem Scope-Profiling wurde festgestellt:</p>
|
||||
<ul className="mt-2 space-y-1">
|
||||
{dsfaCheck.triggers.map(t => (
|
||||
<li key={t} className="text-sm text-red-600 flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 bg-red-500 rounded-full flex-shrink-0" />
|
||||
{t}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showGenerator && (
|
||||
<GeneratorWizard
|
||||
onClose={() => setShowGenerator(false)}
|
||||
onSubmit={handleCreateDSFA}
|
||||
prefill={prefill}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user