'use client'
import React, { useState, useCallback, useEffect } 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'
// =============================================================================
// TYPES
// =============================================================================
interface DSFA {
id: string
title: string
description: string
status: 'draft' | 'in-review' | 'approved' | 'needs-update'
createdAt: string
updatedAt: string
approvedBy: string | null
riskLevel: 'low' | 'medium' | 'high' | 'critical'
processingActivity: string
dataCategories: string[]
recipients: string[]
measures: string[]
}
// =============================================================================
// COMPONENTS
// =============================================================================
function DSFACard({
dsfa,
onStatusChange,
onDelete,
}: {
dsfa: DSFA
onStatusChange: (id: string, status: string) => void
onDelete: (id: string) => void
}) {
const statusColors = {
draft: 'bg-gray-100 text-gray-600 border-gray-200',
'in-review': 'bg-yellow-100 text-yellow-700 border-yellow-200',
approved: 'bg-green-100 text-green-700 border-green-200',
'needs-update': 'bg-orange-100 text-orange-700 border-orange-200',
}
const statusLabels = {
draft: 'Entwurf',
'in-review': 'In Pruefung',
approved: 'Genehmigt',
'needs-update': 'Aktualisierung erforderlich',
}
const riskColors = {
low: 'bg-green-100 text-green-700',
medium: 'bg-yellow-100 text-yellow-700',
high: 'bg-orange-100 text-orange-700',
critical: 'bg-red-100 text-red-700',
}
const createdDate = dsfa.createdAt
? new Date(dsfa.createdAt).toLocaleDateString('de-DE')
: '—'
return (
{statusLabels[dsfa.status]}
Risiko: {dsfa.riskLevel === 'low' ? 'Niedrig' :
dsfa.riskLevel === 'medium' ? 'Mittel' :
dsfa.riskLevel === 'high' ? 'Hoch' : 'Kritisch'}
{dsfa.title}
{dsfa.description}
Verarbeitungstaetigkeit: {dsfa.processingActivity}
{dsfa.dataCategories.map(cat => (
{cat}
))}
{dsfa.measures.length > 0 && (
Massnahmen:
{dsfa.measures.map(m => (
{m}
))}
)}
Erstellt: {createdDate}
{dsfa.approvedBy && (
Genehmigt von: {dsfa.approvedBy}
)}
{dsfa.status === 'draft' && (
)}
{dsfa.status === 'in-review' && (
)}
)
}
function GeneratorWizard({ onClose, onSubmit }: { onClose: () => void; onSubmit: (data: Partial) => Promise }) {
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([])
const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high' | 'critical'>('low')
const [selectedMeasures, setSelectedMeasures] = useState([])
const riskMap: Record = {
Niedrig: 'low', Mittel: 'medium', Hoch: 'high', Kritisch: 'critical',
}
const handleSubmit = async () => {
setSaving(true)
try {
await onSubmit({
title,
description,
processingActivity,
dataCategories: selectedCategories,
riskLevel,
measures: selectedMeasures,
status: 'draft',
})
onClose()
} finally {
setSaving(false)
}
}
return (
{/* Progress Steps */}
{[1, 2, 3, 4].map(s => (
{s < 4 && }
))}
{/* Step Content */}
{step === 1 && (
)}
{step === 2 && (
)}
{step === 3 && (
)}
{step === 4 && (
)}
{/* Navigation */}
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function DSFAPage() {
const router = useRouter()
const { state } = useSDK()
const [dsfas, setDsfas] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
const [showGenerator, setShowGenerator] = useState(false)
const [filter, setFilter] = useState('all')
const loadDSFAs = useCallback(async () => {
setIsLoading(true)
setError(null)
try {
const res = await fetch('/api/sdk/v1/dsfa?tenant_id=default')
if (!res.ok) throw new Error(`Fehler: ${res.status}`)
const data = await res.json()
const mapped: DSFA[] = (Array.isArray(data) ? data : []).map((d: Record) => ({
id: d.id as string,
title: d.title as string,
description: (d.description as string) || '',
status: (d.status as DSFA['status']) || 'draft',
createdAt: d.created_at as string,
updatedAt: d.updated_at as string,
approvedBy: (d.approved_by as string) || null,
riskLevel: (d.risk_level as DSFA['riskLevel']) || 'low',
processingActivity: (d.processing_activity as string) || '',
dataCategories: (d.data_categories as string[]) || [],
recipients: (d.recipients as string[]) || [],
measures: (d.measures as string[]) || [],
}))
setDsfas(mapped)
} catch (err) {
setError(err instanceof Error ? err.message : 'Unbekannter Fehler')
} finally {
setIsLoading(false)
}
}, [])
useEffect(() => {
loadDSFAs()
}, [loadDSFAs])
const handleCreateDSFA = useCallback(async (data: Partial) => {
const res = await fetch('/api/sdk/v1/dsfa?tenant_id=default', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: data.title,
description: data.description || '',
processing_activity: data.processingActivity || '',
data_categories: data.dataCategories || [],
recipients: data.recipients || [],
measures: data.measures || [],
risk_level: data.riskLevel || 'low',
status: data.status || 'draft',
}),
})
if (!res.ok) throw new Error(`Fehler beim Erstellen: ${res.status}`)
await loadDSFAs()
}, [loadDSFAs])
const handleStatusChange = useCallback(async (id: string, status: string) => {
const res = await fetch(`/api/sdk/v1/dsfa/${id}/status?tenant_id=default`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status }),
})
if (!res.ok) throw new Error(`Statuswechsel fehlgeschlagen: ${res.status}`)
await loadDSFAs()
}, [loadDSFAs])
const handleDeleteDSFA = useCallback(async (id: string) => {
if (!confirm('DSFA wirklich loeschen?')) return
const res = await fetch(`/api/sdk/v1/dsfa/${id}?tenant_id=default`, { method: 'DELETE' })
if (!res.ok) throw new Error(`Loeschen fehlgeschlagen: ${res.status}`)
await loadDSFAs()
}, [loadDSFAs])
// Handle uploaded document
const handleDocumentProcessed = useCallback((doc: UploadedDocument) => {
console.log('[DSFA Page] Document processed:', doc)
}, [])
// Open document in workflow editor
const handleOpenInEditor = useCallback((doc: UploadedDocument) => {
router.push(`/sdk/workflow?documentType=dsfa&documentId=${doc.id}&mode=change`)
}, [router])
const filteredDSFAs = filter === 'all'
? dsfas
: dsfas.filter(d => d.status === filter)
const draftCount = dsfas.filter(d => d.status === 'draft').length
const inReviewCount = dsfas.filter(d => d.status === 'in-review').length
const approvedCount = dsfas.filter(d => d.status === 'approved').length
const stepInfo = STEP_EXPLANATIONS['dsfa']
return (
{/* Step Header */}
{!showGenerator && (
)}
{/* Generator */}
{showGenerator && (
setShowGenerator(false)}
onSubmit={handleCreateDSFA}
/>
)}
{/* Document Upload Section */}
{/* Stats */}
In Pruefung
{inReviewCount}
Genehmigt
{approvedCount}
{/* Error */}
{error && (
Fehler beim Laden: {error}
)}
{/* Filter */}
Filter:
{['all', 'draft', 'in-review', 'approved', 'needs-update'].map(f => (
))}
{/* Loading */}
{isLoading && (
Lade DSFAs...
)}
{/* DSFA List */}
{!isLoading && (
{filteredDSFAs.map(dsfa => (
))}
)}
{!isLoading && filteredDSFAs.length === 0 && !showGenerator && (
Keine DSFAs gefunden
Erstellen Sie eine neue Datenschutz-Folgenabschaetzung.
)}
)
}