feat: 7 Analyse-Module auf 100% — Backend-Endpoints, DB-Model, Frontend-Persistenz
Alle 7 Analyse-Module (Requirements → Report) von ~80% auf 100% gebracht: - Modul 1 (Requirements): POST/DELETE Endpoints + Frontend-Anbindung + Rollback - Modul 2 (Controls): Evidence-Linking UI mit Validity-Badge - Modul 3 (Evidence): Pagination (Frontend + Backend) - Modul 4 (Risk Matrix): Mitigation-UI, Residual Risk, Status-Workflow - Modul 5 (AI Act): AISystemDB Model, 6 CRUD-Endpoints, Backend-Persistenz - Modul 6 (Audit Checklist): PDF-Download + Session-History - Modul 7 (Audit Report): Detail-Seite mit Checklist Sign-Off + Navigation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -320,42 +320,126 @@ export default function AIActPage() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// Load systems from SDK state on mount
|
||||
// Fetch systems from backend on mount
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
// Try to load from SDK state (aiSystems if available)
|
||||
const sdkState = state as unknown as Record<string, unknown>
|
||||
if (Array.isArray(sdkState.aiSystems) && sdkState.aiSystems.length > 0) {
|
||||
setSystems(sdkState.aiSystems as AISystem[])
|
||||
const fetchSystems = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await fetch('/api/sdk/v1/compliance/ai/systems')
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
const backendSystems = data.systems || []
|
||||
setSystems(backendSystems.map((s: Record<string, unknown>) => ({
|
||||
id: s.id as string,
|
||||
name: s.name as string,
|
||||
description: (s.description || '') as string,
|
||||
purpose: (s.purpose || '') as string,
|
||||
sector: (s.sector || '') as string,
|
||||
classification: (s.classification || 'unclassified') as AISystem['classification'],
|
||||
status: (s.status || 'draft') as AISystem['status'],
|
||||
obligations: (s.obligations || []) as string[],
|
||||
assessmentDate: s.assessment_date ? new Date(s.assessment_date as string) : null,
|
||||
assessmentResult: (s.assessment_result || null) as Record<string, unknown> | null,
|
||||
})))
|
||||
}
|
||||
} catch {
|
||||
// Backend unavailable — start with empty list
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
setLoading(false)
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
fetchSystems()
|
||||
}, [])
|
||||
|
||||
const handleAddSystem = (data: Omit<AISystem, 'id' | 'assessmentDate' | 'assessmentResult'>) => {
|
||||
const handleAddSystem = async (data: Omit<AISystem, 'id' | 'assessmentDate' | 'assessmentResult'>) => {
|
||||
setError(null)
|
||||
if (editingSystem) {
|
||||
// Edit existing system
|
||||
setSystems(prev => prev.map(s =>
|
||||
s.id === editingSystem.id
|
||||
? { ...s, ...data }
|
||||
: s
|
||||
))
|
||||
// Edit existing system via PUT
|
||||
try {
|
||||
const res = await fetch(`/api/sdk/v1/compliance/ai/systems/${editingSystem.id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
purpose: data.purpose,
|
||||
sector: data.sector,
|
||||
classification: data.classification,
|
||||
status: data.status,
|
||||
obligations: data.obligations,
|
||||
}),
|
||||
})
|
||||
if (res.ok) {
|
||||
const updated = await res.json()
|
||||
setSystems(prev => prev.map(s =>
|
||||
s.id === editingSystem.id
|
||||
? { ...s, ...data, id: updated.id || editingSystem.id }
|
||||
: s
|
||||
))
|
||||
} else {
|
||||
setError('Speichern fehlgeschlagen')
|
||||
}
|
||||
} catch {
|
||||
// Fallback: update locally
|
||||
setSystems(prev => prev.map(s =>
|
||||
s.id === editingSystem.id ? { ...s, ...data } : s
|
||||
))
|
||||
}
|
||||
setEditingSystem(null)
|
||||
} else {
|
||||
// Create new system
|
||||
const newSystem: AISystem = {
|
||||
...data,
|
||||
id: `ai-${Date.now()}`,
|
||||
assessmentDate: data.classification !== 'unclassified' ? new Date() : null,
|
||||
assessmentResult: null,
|
||||
// Create new system via POST
|
||||
try {
|
||||
const res = await fetch('/api/sdk/v1/compliance/ai/systems', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
purpose: data.purpose,
|
||||
sector: data.sector,
|
||||
classification: data.classification,
|
||||
status: data.status,
|
||||
obligations: data.obligations,
|
||||
}),
|
||||
})
|
||||
if (res.ok) {
|
||||
const created = await res.json()
|
||||
const newSystem: AISystem = {
|
||||
...data,
|
||||
id: created.id,
|
||||
assessmentDate: created.assessment_date ? new Date(created.assessment_date) : null,
|
||||
assessmentResult: created.assessment_result || null,
|
||||
}
|
||||
setSystems(prev => [...prev, newSystem])
|
||||
} else {
|
||||
setError('Registrierung fehlgeschlagen')
|
||||
}
|
||||
} catch {
|
||||
// Fallback: add locally
|
||||
const newSystem: AISystem = {
|
||||
...data,
|
||||
id: `ai-${Date.now()}`,
|
||||
assessmentDate: data.classification !== 'unclassified' ? new Date() : null,
|
||||
assessmentResult: null,
|
||||
}
|
||||
setSystems(prev => [...prev, newSystem])
|
||||
}
|
||||
setSystems(prev => [...prev, newSystem])
|
||||
}
|
||||
setShowAddForm(false)
|
||||
}
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!confirm('Moechten Sie dieses KI-System wirklich loeschen?')) return
|
||||
setSystems(prev => prev.filter(s => s.id !== id))
|
||||
try {
|
||||
const res = await fetch(`/api/sdk/v1/compliance/ai/systems/${id}`, { method: 'DELETE' })
|
||||
if (res.ok) {
|
||||
setSystems(prev => prev.filter(s => s.id !== id))
|
||||
} else {
|
||||
setError('Loeschen fehlgeschlagen')
|
||||
}
|
||||
} catch {
|
||||
setError('Backend nicht erreichbar')
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = (system: AISystem) => {
|
||||
@@ -364,37 +448,26 @@ export default function AIActPage() {
|
||||
}
|
||||
|
||||
const handleAssess = async (systemId: string) => {
|
||||
const system = systems.find(s => s.id === systemId)
|
||||
if (!system) return
|
||||
|
||||
setAssessingId(systemId)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/sdk/v1/compliance/ai/assess-risk', {
|
||||
const res = await fetch(`/api/sdk/v1/compliance/ai/systems/${systemId}/assess`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
system_name: system.name,
|
||||
description: system.description,
|
||||
purpose: system.purpose,
|
||||
sector: system.sector,
|
||||
current_classification: system.classification,
|
||||
}),
|
||||
})
|
||||
|
||||
if (res.ok) {
|
||||
const result = await res.json()
|
||||
|
||||
// Update system with assessment result
|
||||
setSystems(prev => prev.map(s =>
|
||||
s.id === systemId
|
||||
? {
|
||||
...s,
|
||||
assessmentDate: new Date(),
|
||||
assessmentResult: result,
|
||||
classification: result.risk_level || result.classification || s.classification,
|
||||
status: result.risk_level === 'high-risk' || result.classification === 'high-risk' ? 'non-compliant' : 'classified',
|
||||
assessmentDate: result.assessment_date ? new Date(result.assessment_date) : new Date(),
|
||||
assessmentResult: result.assessment_result || result,
|
||||
classification: (result.classification || s.classification) as AISystem['classification'],
|
||||
status: (result.status || 'classified') as AISystem['status'],
|
||||
obligations: result.obligations || s.obligations,
|
||||
}
|
||||
: s
|
||||
|
||||
Reference in New Issue
Block a user