'use client' import { useState, useEffect } from 'react' import { useSDK, Control as SDKControl, ControlType, ImplementationStatus } from '@/lib/sdk' import { mapControlTypeToDisplay, mapStatusToDisplay } from '../_types' import type { DisplayControl, RAGControlSuggestion } from '../_types' export function useControlsData() { const { state, dispatch } = useSDK() const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [effectivenessMap, setEffectivenessMap] = useState>({}) const [evidenceMap, setEvidenceMap] = useState>({}) const [transitionError, setTransitionError] = useState<{ controlId: string; violations: string[] } | null>(null) const fetchEvidenceForControls = async (_controlIds: string[]) => { try { const res = await fetch('/api/sdk/v1/compliance/evidence') if (res.ok) { const data = await res.json() const allEvidence = data.evidence || data if (Array.isArray(allEvidence)) { const map: Record = {} for (const ev of allEvidence) { const ctrlId = ev.control_id || '' if (!map[ctrlId]) map[ctrlId] = [] map[ctrlId].push({ id: ev.id, title: ev.title || ev.name || 'Nachweis', status: ev.status || 'pending', confidenceLevel: ev.confidence_level || undefined, }) } setEvidenceMap(map) } } } catch { // Silently fail } } useEffect(() => { const fetchControls = async () => { try { setLoading(true) const res = await fetch('/api/sdk/v1/compliance/controls') if (res.ok) { const data = await res.json() const backendControls = data.controls || data if (Array.isArray(backendControls) && backendControls.length > 0) { const mapped: SDKControl[] = backendControls.map((c: Record) => ({ id: (c.control_id || c.id) as string, name: (c.name || c.title || '') as string, description: (c.description || '') as string, type: ((c.type || c.control_type || 'TECHNICAL') as string).toUpperCase() as ControlType, category: (c.category || '') as string, implementationStatus: ((c.implementation_status || c.status || 'NOT_IMPLEMENTED') as string).toUpperCase() as ImplementationStatus, effectiveness: (c.effectiveness || 'LOW') as 'LOW' | 'MEDIUM' | 'HIGH', evidence: (c.evidence || []) as string[], owner: (c.owner || null) as string | null, dueDate: c.due_date ? new Date(c.due_date as string) : null, })) dispatch({ type: 'SET_STATE', payload: { controls: mapped } }) setError(null) fetchEvidenceForControls(mapped.map(c => c.id)) return } } } catch { // API not available — show empty state } finally { setLoading(false) } } fetchControls() }, []) // eslint-disable-line react-hooks/exhaustive-deps const displayControls: DisplayControl[] = state.controls.map(ctrl => { const effectivenessPercent = effectivenessMap[ctrl.id] ?? (ctrl.implementationStatus === 'IMPLEMENTED' ? 85 : ctrl.implementationStatus === 'PARTIAL' ? 50 : 0) return { id: ctrl.id, name: ctrl.name, description: ctrl.description, type: ctrl.type, category: ctrl.category, implementationStatus: ctrl.implementationStatus, evidence: ctrl.evidence, owner: ctrl.owner, dueDate: ctrl.dueDate, code: ctrl.id, displayType: 'preventive' as const, displayCategory: mapControlTypeToDisplay(ctrl.type), displayStatus: mapStatusToDisplay(ctrl.implementationStatus), effectivenessPercent, linkedRequirements: [], linkedEvidence: evidenceMap[ctrl.id] || [], lastReview: new Date(), } }) const handleStatusChange = async (controlId: string, newStatus: ImplementationStatus) => { const oldControl = state.controls.find(c => c.id === controlId) const oldStatus = oldControl?.implementationStatus dispatch({ type: 'UPDATE_CONTROL', payload: { id: controlId, data: { implementationStatus: newStatus } } }) try { const res = await fetch(`/api/sdk/v1/compliance/controls/${controlId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ implementation_status: newStatus }), }) if (!res.ok) { if (oldStatus) { dispatch({ type: 'UPDATE_CONTROL', payload: { id: controlId, data: { implementationStatus: oldStatus } } }) } const err = await res.json().catch(() => ({ detail: 'Status-Aenderung fehlgeschlagen' })) if (res.status === 409 && err.detail?.violations) { setTransitionError({ controlId, violations: err.detail.violations }) } else { const msg = typeof err.detail === 'string' ? err.detail : err.detail?.error || 'Status-Aenderung fehlgeschlagen' setError(msg) } } else { setTransitionError(prev => prev?.controlId === controlId ? null : prev) } } catch { if (oldStatus) { dispatch({ type: 'UPDATE_CONTROL', payload: { id: controlId, data: { implementationStatus: oldStatus } } }) } setError('Netzwerkfehler bei Status-Aenderung') } } const handleEffectivenessChange = async (controlId: string, effectiveness: number) => { setEffectivenessMap(prev => ({ ...prev, [controlId]: effectiveness })) try { await fetch(`/api/sdk/v1/compliance/controls/${controlId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ effectiveness_score: effectiveness }), }) } catch { // Silently fail } } const handleAddControl = (data: { name: string; description: string; type: ControlType; category: string; owner: string }) => { const newControl: SDKControl = { id: `ctrl-${Date.now()}`, name: data.name, description: data.description, type: data.type, category: data.category, implementationStatus: 'NOT_IMPLEMENTED', effectiveness: 'LOW', evidence: [], owner: data.owner || null, dueDate: null, } dispatch({ type: 'ADD_CONTROL', payload: newControl }) } const addSuggestedControl = (suggestion: RAGControlSuggestion) => { const newControl: SDKControl = { id: `rag-${suggestion.control_id}-${Date.now()}`, name: suggestion.title, description: suggestion.description, type: 'TECHNICAL', category: suggestion.domain, implementationStatus: 'NOT_IMPLEMENTED', effectiveness: 'LOW', evidence: [], owner: null, dueDate: null, } dispatch({ type: 'ADD_CONTROL', payload: newControl }) } return { state, loading, error, setError, effectivenessMap, evidenceMap, displayControls, transitionError, setTransitionError, handleStatusChange, handleEffectivenessChange, handleAddControl, addSuggestedControl, } }