'use client' import { useState, useCallback, useEffect } from 'react' import { useRouter } from 'next/navigation' import { useSDK } from '@/lib/sdk' import type { ImportedDocument, ImportedDocumentType, GapAnalysis, GapItem } from '@/lib/sdk/types' // ============================================================================= // DOCUMENT TYPE OPTIONS // ============================================================================= const DOCUMENT_TYPES: { value: ImportedDocumentType; label: string; icon: string }[] = [ { value: 'DSFA', label: 'Datenschutz-Folgenabschaetzung (DSFA)', icon: '📄' }, { value: 'TOM', label: 'Technisch-organisatorische Massnahmen (TOMs)', icon: '🔒' }, { value: 'VVT', label: 'Verarbeitungsverzeichnis (VVT)', icon: '📊' }, { value: 'AGB', label: 'Allgemeine Geschaeftsbedingungen (AGB)', icon: '📜' }, { value: 'PRIVACY_POLICY', label: 'Datenschutzerklaerung', icon: '🔐' }, { value: 'COOKIE_POLICY', label: 'Cookie-Richtlinie', icon: '🍪' }, { value: 'RISK_ASSESSMENT', label: 'Risikobewertung', icon: '⚠️' }, { value: 'AUDIT_REPORT', label: 'Audit-Bericht', icon: '✅' }, { value: 'OTHER', label: 'Sonstiges Dokument', icon: '📎' }, ] // ============================================================================= // UPLOAD ZONE // ============================================================================= interface UploadedFile { id: string file: File type: ImportedDocumentType status: 'pending' | 'uploading' | 'analyzing' | 'complete' | 'error' progress: number error?: string } function UploadZone({ onFilesAdded, isDisabled, }: { onFilesAdded: (files: File[]) => void isDisabled: boolean }) { const [isDragging, setIsDragging] = useState(false) const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault() if (!isDisabled) setIsDragging(true) }, [isDisabled]) const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault() setIsDragging(false) }, []) const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault() setIsDragging(false) if (isDisabled) return const files = Array.from(e.dataTransfer.files).filter( f => f.type === 'application/pdf' || f.type.startsWith('image/') ) if (files.length > 0) { onFilesAdded(files) } }, [onFilesAdded, isDisabled] ) const handleFileSelect = useCallback( (e: React.ChangeEvent) => { if (e.target.files && !isDisabled) { const files = Array.from(e.target.files) onFilesAdded(files) } }, [onFilesAdded, isDisabled] ) return (

{isDragging ? 'Dateien hier ablegen' : 'Dokumente hochladen'}

Ziehen Sie PDF-Dateien hierher oder klicken Sie zum Auswaehlen

Unterstuetzte Formate: PDF JPG PNG
) } // ============================================================================= // FILE LIST // ============================================================================= function FileItem({ file, onTypeChange, onRemove, }: { file: UploadedFile onTypeChange: (id: string, type: ImportedDocumentType) => void onRemove: (id: string) => void }) { return (
{/* File Icon */}
{/* File Info */}

{file.file.name}

{(file.file.size / 1024 / 1024).toFixed(2)} MB

{/* Type Selector */} {/* Status / Actions */} {file.status === 'pending' && ( )} {file.status === 'uploading' && (
{file.progress}%
)} {file.status === 'analyzing' && (
Analysiere...
)} {file.status === 'complete' && file.error === 'offline' && (
Offline — nicht analysiert
)} {file.status === 'complete' && file.error !== 'offline' && (
Fertig
)} {file.status === 'error' && (
{file.error || 'Fehler'}
)}
) } // ============================================================================= // GAP ANALYSIS PREVIEW // ============================================================================= function GapAnalysisPreview({ analysis }: { analysis: GapAnalysis }) { return (
📊

Gap-Analyse Ergebnis

{analysis.totalGaps} Luecken in {analysis.gaps.length} Kategorien gefunden

{/* Summary Stats */}
{analysis.criticalGaps}
Kritisch
{analysis.highGaps}
Hoch
{analysis.mediumGaps}
Mittel
{analysis.lowGaps}
Niedrig
{/* Gap List */}
{analysis.gaps.slice(0, 5).map((gap: GapItem) => (
{gap.category}

{gap.description}

{gap.severity}
Regulierung: {gap.regulation} | Aktion: {gap.requiredAction}
))} {analysis.gaps.length > 5 && (

+ {analysis.gaps.length - 5} weitere Luecken

)}
) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function ImportPage() { const router = useRouter() const { state, addImportedDocument, setGapAnalysis, dispatch } = useSDK() const [files, setFiles] = useState([]) const [isAnalyzing, setIsAnalyzing] = useState(false) const [analysisResult, setAnalysisResult] = useState(null) const [importHistory, setImportHistory] = useState([]) const [historyLoading, setHistoryLoading] = useState(false) const [objectUrls, setObjectUrls] = useState([]) // 4.1: Load import history useEffect(() => { const loadHistory = async () => { setHistoryLoading(true) try { const response = await fetch('/api/sdk/v1/import?tenant_id=default') if (response.ok) { const data = await response.json() setImportHistory(Array.isArray(data) ? data : data.items || []) } } catch (err) { console.error('Failed to load import history:', err) } finally { setHistoryLoading(false) } } loadHistory() }, [analysisResult]) // 4.4: Cleanup ObjectURLs on unmount useEffect(() => { return () => { objectUrls.forEach(url => URL.revokeObjectURL(url)) } }, [objectUrls]) // Helper to create and track ObjectURLs const createTrackedObjectURL = useCallback((file: File) => { const url = URL.createObjectURL(file) setObjectUrls(prev => [...prev, url]) return url }, []) const handleFilesAdded = useCallback((newFiles: File[]) => { const uploadedFiles: UploadedFile[] = newFiles.map(file => ({ id: `file-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, file, type: 'OTHER' as ImportedDocumentType, status: 'pending' as const, progress: 0, })) setFiles(prev => [...prev, ...uploadedFiles]) }, []) const handleTypeChange = useCallback((id: string, type: ImportedDocumentType) => { setFiles(prev => prev.map(f => (f.id === id ? { ...f, type } : f))) }, []) const handleRemove = useCallback((id: string) => { setFiles(prev => prev.filter(f => f.id !== id)) }, []) const handleAnalyze = async () => { if (files.length === 0) return setIsAnalyzing(true) const allGaps: GapItem[] = [] for (let i = 0; i < files.length; i++) { const file = files[i] // Update to uploading setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, status: 'uploading' as const } : f))) // Upload progress setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: 30 } : f))) // Prepare form data for backend const formData = new FormData() formData.append('file', file.file) formData.append('document_type', file.type) formData.append('tenant_id', 'default') setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: 60, status: 'analyzing' as const } : f))) try { const response = await fetch('/api/sdk/v1/import/analyze', { method: 'POST', body: formData, }) if (response.ok) { const result = await response.json() // Create imported document from backend response const doc: ImportedDocument = { id: result.document_id || file.id, name: file.file.name, type: result.detected_type || file.type, fileUrl: createTrackedObjectURL(file.file), uploadedAt: new Date(), analyzedAt: new Date(), analysisResult: { detectedType: result.detected_type || file.type, confidence: result.confidence || 0.85, extractedEntities: result.extracted_entities || [], gaps: result.gap_analysis?.gaps || [], recommendations: result.recommendations || [], }, } addImportedDocument(doc) // Collect gaps if (result.gap_analysis?.gaps) { for (const gap of result.gap_analysis.gaps) { allGaps.push({ id: gap.id, category: gap.category, description: gap.description, severity: gap.severity, regulation: gap.regulation, requiredAction: gap.required_action, relatedStepId: gap.related_step_id || '', }) } } setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: 100, status: 'complete' as const } : f))) } else { setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, status: 'error' as const, error: 'Analyse fehlgeschlagen' } : f))) } } catch { // Offline-Modus: create basic document without backend analysis const doc: ImportedDocument = { id: file.id, name: file.file.name, type: file.type, fileUrl: createTrackedObjectURL(file.file), uploadedAt: new Date(), analyzedAt: new Date(), analysisResult: { detectedType: file.type, confidence: 0.5, extractedEntities: [], gaps: [], recommendations: ['Offline-Modus — Backend nicht erreichbar, manuelle Pruefung empfohlen'], }, } addImportedDocument(doc) setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: 100, status: 'complete' as const, error: 'offline' } : f))) } } // Build gap analysis summary const gapAnalysis: GapAnalysis = { id: `analysis-${Date.now()}`, createdAt: new Date(), totalGaps: allGaps.length, criticalGaps: allGaps.filter(g => g.severity === 'CRITICAL').length, highGaps: allGaps.filter(g => g.severity === 'HIGH').length, mediumGaps: allGaps.filter(g => g.severity === 'MEDIUM').length, lowGaps: allGaps.filter(g => g.severity === 'LOW').length, gaps: allGaps, recommendedPackages: allGaps.length > 0 ? ['analyse', 'dokumentation'] : [], } setAnalysisResult(gapAnalysis) setGapAnalysis(gapAnalysis) setIsAnalyzing(false) // Mark step as complete dispatch({ type: 'COMPLETE_STEP', payload: 'import' }) } const handleContinue = () => { router.push('/sdk/screening') } // Redirect if not existing customer if (state.customerType === 'new') { router.push('/sdk') return null } return (
{/* Header */}

Dokumente importieren

Laden Sie Ihre bestehenden Compliance-Dokumente hoch. Unsere KI analysiert sie und identifiziert Luecken fuer KI-Compliance.

{/* Upload Zone */} {/* File List */} {files.length > 0 && (

{files.length} Dokument(e)

{!isAnalyzing && !analysisResult && ( )}
{files.map(file => ( ))}
)} {/* Analyze Button */} {files.length > 0 && !analysisResult && (
)} {/* Analysis Result */} {analysisResult && } {/* Continue Button */} {analysisResult && (

Die Gap-Analyse wurde gespeichert. Sie koennen jetzt mit dem Compliance-Assessment fortfahren.

)} {/* Import-Verlauf (4.1) */} {importHistory.length > 0 && (

Import-Verlauf

{importHistory.length} fruehere Imports

{importHistory.map((item: any, idx: number) => (

{item.name || item.filename || `Import #${idx + 1}`}

{item.document_type || item.type || 'Unbekannt'} — {item.uploaded_at ? new Date(item.uploaded_at).toLocaleString('de-DE') : 'Unbekannt'}

))}
)} {historyLoading && (
Import-Verlauf wird geladen...
)}
) }