feat: Vorbereitung-Module auf 100% — Persistenz, Backend-Services, UCCA Frontend
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s

Phase A: PostgreSQL State Store (sdk_states Tabelle, InMemory-Fallback)
Phase B: Modules dynamisch vom Backend, Scope DB-Persistenz, Source Policy State
Phase C: UCCA Frontend (3 Seiten, Wizard, RiskScoreGauge), Obligations Live-Daten
Phase D: Document Import (PDF/LLM/Gap-Analyse), System Screening (SBOM/OSV.dev)
Phase E: Company Profile CRUD mit Audit-Logging
Phase F: Tests (Python + TypeScript), flow-data.ts DB-Tabellen aktualisiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-02 11:04:31 +01:00
parent cd15ab0932
commit e6d666b89b
38 changed files with 4195 additions and 420 deletions

View File

@@ -358,108 +358,105 @@ export default function ImportPage() {
if (files.length === 0) return
setIsAnalyzing(true)
const allGaps: GapItem[] = []
// Simulate upload and analysis
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)))
// Simulate upload progress
for (let p = 0; p <= 100; p += 20) {
await new Promise(resolve => setTimeout(resolve, 100))
setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: p } : 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: URL.createObjectURL(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 {
// Fallback: create basic document without backend analysis
const doc: ImportedDocument = {
id: file.id,
name: file.file.name,
type: file.type,
fileUrl: URL.createObjectURL(file.file),
uploadedAt: new Date(),
analyzedAt: new Date(),
analysisResult: {
detectedType: file.type,
confidence: 0.5,
extractedEntities: [],
gaps: [],
recommendations: ['Backend nicht erreichbar — manuelle Pruefung empfohlen'],
},
}
addImportedDocument(doc)
setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, progress: 100, status: 'complete' as const } : f)))
}
// Update to analyzing
setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, status: 'analyzing' as const } : f)))
// Simulate analysis
await new Promise(resolve => setTimeout(resolve, 1000))
// Create imported document
const doc: ImportedDocument = {
id: file.id,
name: file.file.name,
type: file.type,
fileUrl: URL.createObjectURL(file.file),
uploadedAt: new Date(),
analyzedAt: new Date(),
analysisResult: {
detectedType: file.type,
confidence: 0.85 + Math.random() * 0.15,
extractedEntities: ['DSGVO', 'AI Act', 'Personenbezogene Daten'],
gaps: [],
recommendations: ['KI-spezifische Klauseln ergaenzen', 'AI Act Anforderungen pruefen'],
},
}
addImportedDocument(doc)
// Update to complete
setFiles(prev => prev.map(f => (f.id === file.id ? { ...f, status: 'complete' as const } : f)))
}
// Generate mock gap analysis
const gaps: GapItem[] = [
{
id: 'gap-1',
category: 'AI Act Compliance',
description: 'Keine Risikoklassifizierung fuer KI-Systeme vorhanden',
severity: 'CRITICAL',
regulation: 'EU AI Act Art. 6',
requiredAction: 'Risikoklassifizierung durchfuehren',
relatedStepId: 'ai-act',
},
{
id: 'gap-2',
category: 'Transparenz',
description: 'Informationspflichten bei automatisierten Entscheidungen fehlen',
severity: 'HIGH',
regulation: 'DSGVO Art. 13, 14, 22',
requiredAction: 'Datenschutzerklaerung erweitern',
relatedStepId: 'einwilligungen',
},
{
id: 'gap-3',
category: 'TOMs',
description: 'KI-spezifische technische Massnahmen nicht dokumentiert',
severity: 'MEDIUM',
regulation: 'DSGVO Art. 32',
requiredAction: 'TOMs um KI-Aspekte erweitern',
relatedStepId: 'tom',
},
{
id: 'gap-4',
category: 'VVT',
description: 'KI-basierte Verarbeitungstaetigkeiten nicht erfasst',
severity: 'HIGH',
regulation: 'DSGVO Art. 30',
requiredAction: 'VVT aktualisieren',
relatedStepId: 'vvt',
},
{
id: 'gap-5',
category: 'Aufsicht',
description: 'Menschliche Aufsicht nicht definiert',
severity: 'MEDIUM',
regulation: 'EU AI Act Art. 14',
requiredAction: 'Aufsichtsprozesse definieren',
relatedStepId: 'controls',
},
]
// Build gap analysis summary
const gapAnalysis: GapAnalysis = {
id: `analysis-${Date.now()}`,
createdAt: new Date(),
totalGaps: gaps.length,
criticalGaps: gaps.filter(g => g.severity === 'CRITICAL').length,
highGaps: gaps.filter(g => g.severity === 'HIGH').length,
mediumGaps: gaps.filter(g => g.severity === 'MEDIUM').length,
lowGaps: gaps.filter(g => g.severity === 'LOW').length,
gaps,
recommendedPackages: ['analyse', 'dokumentation'],
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)