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
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:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useSDK } from '@/lib/sdk'
|
||||
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
|
||||
|
||||
@@ -21,73 +21,6 @@ interface Obligation {
|
||||
linkedSystems: string[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MOCK DATA
|
||||
// =============================================================================
|
||||
|
||||
const mockObligations: Obligation[] = [
|
||||
{
|
||||
id: 'obl-1',
|
||||
title: 'Risikomanagementsystem implementieren',
|
||||
description: 'Ein Risikomanagementsystem fuer das Hochrisiko-KI-System muss implementiert werden.',
|
||||
source: 'AI Act',
|
||||
sourceArticle: 'Art. 9',
|
||||
deadline: new Date('2024-06-01'),
|
||||
status: 'in-progress',
|
||||
priority: 'critical',
|
||||
responsible: 'IT Security',
|
||||
linkedSystems: ['Bewerber-Screening'],
|
||||
},
|
||||
{
|
||||
id: 'obl-2',
|
||||
title: 'Technische Dokumentation erstellen',
|
||||
description: 'Umfassende technische Dokumentation fuer alle Hochrisiko-KI-Systeme.',
|
||||
source: 'AI Act',
|
||||
sourceArticle: 'Art. 11',
|
||||
deadline: new Date('2024-05-15'),
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
responsible: 'Entwicklung',
|
||||
linkedSystems: ['Bewerber-Screening'],
|
||||
},
|
||||
{
|
||||
id: 'obl-3',
|
||||
title: 'Datenschutzerklaerung aktualisieren',
|
||||
description: 'Die Datenschutzerklaerung muss an die neuen KI-Verarbeitungen angepasst werden.',
|
||||
source: 'DSGVO',
|
||||
sourceArticle: 'Art. 13/14',
|
||||
deadline: new Date('2024-02-01'),
|
||||
status: 'overdue',
|
||||
priority: 'high',
|
||||
responsible: 'Datenschutz',
|
||||
linkedSystems: ['Kundenservice Chatbot', 'Empfehlungsalgorithmus'],
|
||||
},
|
||||
{
|
||||
id: 'obl-4',
|
||||
title: 'KI-Kennzeichnung implementieren',
|
||||
description: 'Nutzer muessen informiert werden, dass sie mit einem KI-System interagieren.',
|
||||
source: 'AI Act',
|
||||
sourceArticle: 'Art. 52',
|
||||
deadline: new Date('2024-03-01'),
|
||||
status: 'completed',
|
||||
priority: 'medium',
|
||||
responsible: 'UX Team',
|
||||
linkedSystems: ['Kundenservice Chatbot'],
|
||||
},
|
||||
{
|
||||
id: 'obl-5',
|
||||
title: 'Menschliche Aufsicht sicherstellen',
|
||||
description: 'Prozesse fuer menschliche Aufsicht bei automatisierten Entscheidungen.',
|
||||
source: 'AI Act',
|
||||
sourceArticle: 'Art. 14',
|
||||
deadline: new Date('2024-04-01'),
|
||||
status: 'pending',
|
||||
priority: 'critical',
|
||||
responsible: 'Operations',
|
||||
linkedSystems: ['Bewerber-Screening'],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// COMPONENTS
|
||||
// =============================================================================
|
||||
@@ -188,14 +121,124 @@ function ObligationCard({ obligation }: { obligation: Obligation }) {
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
function mapControlsToObligations(assessments: Array<{
|
||||
id: string
|
||||
title?: string
|
||||
domain?: string
|
||||
result?: {
|
||||
required_controls?: Array<{
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
gdpr_ref?: string
|
||||
effort?: string
|
||||
}>
|
||||
triggered_rules?: Array<{
|
||||
rule_code: string
|
||||
title: string
|
||||
severity: string
|
||||
gdpr_ref: string
|
||||
}>
|
||||
risk_level?: string
|
||||
}
|
||||
}>): Obligation[] {
|
||||
const obligations: Obligation[] = []
|
||||
|
||||
for (const assessment of assessments) {
|
||||
// Map triggered rules to obligations
|
||||
const rules = assessment.result?.triggered_rules || []
|
||||
for (const rule of rules) {
|
||||
const severity = rule.severity
|
||||
obligations.push({
|
||||
id: `${assessment.id}-${rule.rule_code}`,
|
||||
title: rule.title,
|
||||
description: `Aus Assessment: ${assessment.title || assessment.id.slice(0, 8)}`,
|
||||
source: rule.gdpr_ref?.includes('AI Act') ? 'AI Act' : 'DSGVO',
|
||||
sourceArticle: rule.gdpr_ref || '',
|
||||
deadline: null,
|
||||
status: 'pending',
|
||||
priority: severity === 'BLOCK' ? 'critical' : severity === 'WARN' ? 'high' : 'medium',
|
||||
responsible: 'Compliance Team',
|
||||
linkedSystems: assessment.title ? [assessment.title] : [],
|
||||
})
|
||||
}
|
||||
|
||||
// Map required controls to obligations
|
||||
const controls = assessment.result?.required_controls || []
|
||||
for (const control of controls) {
|
||||
obligations.push({
|
||||
id: `${assessment.id}-ctrl-${control.id}`,
|
||||
title: control.title,
|
||||
description: control.description,
|
||||
source: control.gdpr_ref?.includes('AI Act') ? 'AI Act' : 'DSGVO',
|
||||
sourceArticle: control.gdpr_ref || '',
|
||||
deadline: null,
|
||||
status: 'pending',
|
||||
priority: assessment.result?.risk_level === 'HIGH' || assessment.result?.risk_level === 'UNACCEPTABLE' ? 'high' : 'medium',
|
||||
responsible: 'IT / Compliance',
|
||||
linkedSystems: assessment.title ? [assessment.title] : [],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return obligations
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAIN PAGE
|
||||
// =============================================================================
|
||||
|
||||
export default function ObligationsPage() {
|
||||
const { state } = useSDK()
|
||||
const [obligations] = useState<Obligation[]>(mockObligations)
|
||||
const [obligations, setObligations] = useState<Obligation[]>([])
|
||||
const [filter, setFilter] = useState<string>('all')
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [backendAvailable, setBackendAvailable] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
async function loadObligations() {
|
||||
try {
|
||||
const response = await fetch('/api/sdk/v1/ucca/assessments')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
const assessments = data.assessments || []
|
||||
if (assessments.length > 0) {
|
||||
const mapped = mapControlsToObligations(assessments)
|
||||
setObligations(mapped)
|
||||
setBackendAvailable(true)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Backend unavailable, use SDK state obligations
|
||||
}
|
||||
|
||||
// Fallback: use obligations from SDK state
|
||||
if (state.obligations && state.obligations.length > 0) {
|
||||
setObligations(state.obligations.map(o => ({
|
||||
id: o.id,
|
||||
title: o.title,
|
||||
description: o.description || '',
|
||||
source: o.source || 'DSGVO',
|
||||
sourceArticle: o.sourceArticle || '',
|
||||
deadline: o.deadline ? new Date(o.deadline) : null,
|
||||
status: (o.status as Obligation['status']) || 'pending',
|
||||
priority: (o.priority as Obligation['priority']) || 'medium',
|
||||
responsible: o.responsible || 'Compliance Team',
|
||||
linkedSystems: o.linkedSystems || [],
|
||||
})))
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
loadObligations()
|
||||
}, [state.obligations])
|
||||
|
||||
const filteredObligations = filter === 'all'
|
||||
? obligations
|
||||
@@ -226,6 +269,18 @@ export default function ObligationsPage() {
|
||||
</button>
|
||||
</StepHeader>
|
||||
|
||||
{/* Backend Status */}
|
||||
{backendAvailable && (
|
||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3 text-sm text-green-700">
|
||||
Pflichten aus UCCA-Assessments geladen (Live-Daten)
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Loading */}
|
||||
{loading && (
|
||||
<div className="text-center py-8 text-gray-500">Lade Pflichten...</div>
|
||||
)}
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
@@ -288,7 +343,6 @@ export default function ObligationsPage() {
|
||||
<div className="space-y-4">
|
||||
{filteredObligations
|
||||
.sort((a, b) => {
|
||||
// Sort by status priority: overdue > in-progress > pending > completed
|
||||
const statusOrder = { overdue: 0, 'in-progress': 1, pending: 2, completed: 3 }
|
||||
return statusOrder[a.status] - statusOrder[b.status]
|
||||
})
|
||||
@@ -297,7 +351,7 @@ export default function ObligationsPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredObligations.length === 0 && (
|
||||
{filteredObligations.length === 0 && !loading && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-12 text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-gray-100 rounded-full flex items-center justify-center mb-4">
|
||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -305,7 +359,9 @@ export default function ObligationsPage() {
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">Keine Pflichten gefunden</h3>
|
||||
<p className="mt-2 text-gray-500">Passen Sie den Filter an oder fuegen Sie neue Pflichten hinzu.</p>
|
||||
<p className="mt-2 text-gray-500">
|
||||
Erstellen Sie zuerst ein Use Case Assessment, um automatisch Pflichten abzuleiten.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user