'use client' /** * Audit Export Wizard * * Features: * - Export type selection * - Scope filtering (regulations, domains) * - Export generation * - Download * - Export history */ import { useState, useEffect } from 'react' import Link from 'next/link' import AdminLayout from '@/components/admin/AdminLayout' interface Export { id: string export_type: string export_name: string status: string requested_by: string requested_at: string completed_at: string | null file_path: string | null file_hash: string | null file_size_bytes: number | null total_controls: number | null total_evidence: number | null compliance_score: number | null error_message: string | null } interface Regulation { code: string name: string } const EXPORT_TYPES = [ { value: 'full', label: 'Vollstaendiger Export', description: 'Alle Daten inkl. Regulations, Controls, Evidence, Risks' }, { value: 'controls_only', label: 'Nur Controls', description: 'Control Catalogue mit Mappings' }, { value: 'evidence_only', label: 'Nur Nachweise', description: 'Evidence-Dateien und Metadaten' }, ] const DOMAIN_OPTIONS = [ { value: 'gov', label: 'Governance' }, { value: 'priv', label: 'Datenschutz' }, { value: 'iam', label: 'Identity & Access' }, { value: 'crypto', label: 'Kryptografie' }, { value: 'sdlc', label: 'Secure Dev' }, { value: 'ops', label: 'Operations' }, { value: 'ai', label: 'KI-spezifisch' }, { value: 'cra', label: 'Supply Chain' }, { value: 'aud', label: 'Audit' }, ] export default function ExportPage() { const [exports, setExports] = useState([]) const [regulations, setRegulations] = useState([]) const [loading, setLoading] = useState(true) const [generating, setGenerating] = useState(false) const [wizardStep, setWizardStep] = useState(1) const [exportType, setExportType] = useState('full') const [selectedRegulations, setSelectedRegulations] = useState([]) const [selectedDomains, setSelectedDomains] = useState([]) const [currentExport, setCurrentExport] = useState(null) const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000' useEffect(() => { loadData() }, []) const loadData = async () => { setLoading(true) try { const [exportsRes, regulationsRes] = await Promise.all([ fetch(`${BACKEND_URL}/api/v1/compliance/exports`), fetch(`${BACKEND_URL}/api/v1/compliance/regulations`), ]) if (exportsRes.ok) { const data = await exportsRes.json() setExports(data.exports || []) } if (regulationsRes.ok) { const data = await regulationsRes.json() setRegulations(data.regulations || []) } } catch (error) { console.error('Failed to load data:', error) } finally { setLoading(false) } } const startExport = async () => { setGenerating(true) try { const res = await fetch(`${BACKEND_URL}/api/v1/compliance/export`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ export_type: exportType, included_regulations: selectedRegulations.length > 0 ? selectedRegulations : null, included_domains: selectedDomains.length > 0 ? selectedDomains : null, }), }) if (res.ok) { const exportData = await res.json() setCurrentExport(exportData) setWizardStep(4) loadData() } else { const error = await res.text() alert(`Export fehlgeschlagen: ${error}`) } } catch (error) { console.error('Export failed:', error) alert('Export fehlgeschlagen') } finally { setGenerating(false) } } const downloadExport = (exportId: string) => { window.open(`${BACKEND_URL}/api/v1/compliance/export/${exportId}/download`, '_blank') } const resetWizard = () => { setWizardStep(1) setExportType('full') setSelectedRegulations([]) setSelectedDomains([]) setCurrentExport(null) } const toggleRegulation = (code: string) => { setSelectedRegulations((prev) => prev.includes(code) ? prev.filter((r) => r !== code) : [...prev, code] ) } const toggleDomain = (domain: string) => { setSelectedDomains((prev) => prev.includes(domain) ? prev.filter((d) => d !== domain) : [...prev, domain] ) } const formatFileSize = (bytes: number | null) => { if (!bytes) return '-' if (bytes < 1024) return `${bytes} B` if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` return `${(bytes / (1024 * 1024)).toFixed(1)} MB` } const renderWizardSteps = () => (
{[ { num: 1, label: 'Typ' }, { num: 2, label: 'Scope' }, { num: 3, label: 'Bestaetigen' }, { num: 4, label: 'Download' }, ].map((step, idx) => (
= step.num ? 'bg-primary-600 text-white' : 'bg-slate-200 text-slate-500' }`}> {wizardStep > step.num ? ( ) : ( step.num )}
= step.num ? 'text-slate-900' : 'text-slate-500'}`}> {step.label} {idx < 3 && (
step.num ? 'bg-primary-600' : 'bg-slate-200'}`} /> )}
))}
) const renderStep1 = () => (

Export-Typ waehlen

{EXPORT_TYPES.map((type) => ( ))}
) const renderStep2 = () => (

Scope definieren (optional)

{/* Regulations Filter */}

Verordnungen filtern

Leer lassen fuer alle Verordnungen

{regulations.map((reg) => ( ))}
{/* Domains Filter */}

Domains filtern

Leer lassen fuer alle Domains

{DOMAIN_OPTIONS.map((domain) => ( ))}
) const renderStep3 = () => (

Export bestaetigen

Export-Typ: {EXPORT_TYPES.find((t) => t.value === exportType)?.label}
Verordnungen: {selectedRegulations.length > 0 ? selectedRegulations.join(', ') : 'Alle'}
Domains: {selectedDomains.length > 0 ? selectedDomains.map((d) => DOMAIN_OPTIONS.find((o) => o.value === d)?.label).join(', ') : 'Alle'}

Der Export kann je nach Datenmenge einige Sekunden dauern. Nach Abschluss koennen Sie die ZIP-Datei herunterladen.

) const renderStep4 = () => (
{currentExport?.status === 'completed' ? ( <>

Export erfolgreich!

Compliance Score: {currentExport.compliance_score?.toFixed(1)}%
Controls: {currentExport.total_controls}
Nachweise: {currentExport.total_evidence}
Dateigroesse: {formatFileSize(currentExport.file_size_bytes)}
SHA-256: {currentExport.file_hash}
) : ( <>

Export fehlgeschlagen

{currentExport?.error_message || 'Unbekannter Fehler'}

)}
) return ( {/* Header */}
Zurueck
{loading ? (
) : (
{/* Wizard */}
{renderWizardSteps()} {wizardStep === 1 && renderStep1()} {wizardStep === 2 && renderStep2()} {wizardStep === 3 && renderStep3()} {wizardStep === 4 && renderStep4()}
{/* Export History */}

Letzte Exports

{exports.length === 0 ? (

Noch keine Exports vorhanden

) : (
{exports.slice(0, 10).map((exp) => (
{exp.status} {new Date(exp.requested_at).toLocaleDateString('de-DE')}

{exp.export_name}

{exp.export_type} - {formatFileSize(exp.file_size_bytes)}

{exp.status === 'completed' && ( )}
))}
)}
)} ) }