'use client' import { useState, useEffect } from 'react' import { useSDK, ServiceModule } from '@/lib/sdk' // ============================================================================= // TYPES // ============================================================================= export type ModuleCategory = 'gdpr' | 'ai-act' | 'iso27001' | 'nis2' | 'custom' export type ModuleStatus = 'active' | 'inactive' | 'pending' export interface DisplayModule extends ServiceModule { category: ModuleCategory status: ModuleStatus requirementsCount: number controlsCount: number completionPercent: number } interface BackendModule { id: string name: string display_name: string description: string service_type: string | null processes_pii: boolean ai_components: boolean criticality: string is_active: boolean compliance_score: number | null regulation_count: number risk_count: number } // ============================================================================= // FALLBACK MODULES // ============================================================================= const fallbackModules: Omit[] = [ { id: 'mod-gdpr', name: 'DSGVO Compliance', description: 'Datenschutz-Grundverordnung - Vollstaendige Umsetzung aller Anforderungen', category: 'gdpr', regulations: ['DSGVO', 'BDSG'], criticality: 'HIGH', processesPersonalData: true, hasAIComponents: false, requirementsCount: 45, controlsCount: 32, }, { id: 'mod-ai-act', name: 'AI Act Compliance', description: 'EU AI Act - Klassifizierung und Anforderungen fuer KI-Systeme', category: 'ai-act', regulations: ['EU AI Act'], criticality: 'HIGH', processesPersonalData: false, hasAIComponents: true, requirementsCount: 28, controlsCount: 18, }, { id: 'mod-iso27001', name: 'ISO 27001', description: 'Informationssicherheits-Managementsystem nach ISO/IEC 27001', category: 'iso27001', regulations: ['ISO 27001', 'ISO 27002'], criticality: 'MEDIUM', processesPersonalData: false, hasAIComponents: false, requirementsCount: 114, controlsCount: 93, }, { id: 'mod-nis2', name: 'NIS2 Richtlinie', description: 'Netz- und Informationssicherheit fuer kritische Infrastrukturen', category: 'nis2', regulations: ['NIS2'], criticality: 'HIGH', processesPersonalData: false, hasAIComponents: false, requirementsCount: 36, controlsCount: 24, }, ] // ============================================================================= // HELPERS // ============================================================================= function categorizeModule(name: string): ModuleCategory { const lower = name.toLowerCase() if (lower.includes('dsgvo') || lower.includes('gdpr') || lower.includes('datenschutz')) return 'gdpr' if (lower.includes('ai act') || lower.includes('ki-verordnung')) return 'ai-act' if (lower.includes('iso 27001') || lower.includes('iso27001') || lower.includes('isms')) return 'iso27001' if (lower.includes('nis2') || lower.includes('netz- und informations')) return 'nis2' return 'custom' } function mapBackendToDisplay(m: BackendModule): Omit { return { id: m.id, name: m.display_name || m.name, description: m.description || '', category: categorizeModule(m.display_name || m.name), regulations: [], criticality: (m.criticality || 'MEDIUM').toUpperCase(), processesPersonalData: m.processes_pii, hasAIComponents: m.ai_components, requirementsCount: m.regulation_count || 0, controlsCount: m.risk_count || 0, } } // ============================================================================= // HOOK // ============================================================================= export function useModules() { const { state, dispatch } = useSDK() const [availableModules, setAvailableModules] = useState[]>(fallbackModules) const [isLoadingModules, setIsLoadingModules] = useState(true) const [backendError, setBackendError] = useState(null) const [actionError, setActionError] = useState(null) useEffect(() => { async function loadModules() { try { const response = await fetch('/api/sdk/v1/modules') if (response.ok) { const data = await response.json() if (data.modules && data.modules.length > 0) { const mapped = data.modules.map(mapBackendToDisplay) setAvailableModules(mapped) setBackendError(null) } } else { setBackendError('Backend nicht erreichbar — zeige Standard-Module') } } catch { setBackendError('Backend nicht erreichbar — zeige Standard-Module') } finally { setIsLoadingModules(false) } } loadModules() }, []) const displayModules: DisplayModule[] = availableModules.map(template => { const activeModule = state.modules.find(m => m.id === template.id) const isActive = !!activeModule const linkedRequirements = state.requirements.filter(r => r.applicableModules.includes(template.id) ) const completedRequirements = linkedRequirements.filter( r => r.status === 'IMPLEMENTED' || r.status === 'VERIFIED' ) const completionPercent = linkedRequirements.length > 0 ? Math.round((completedRequirements.length / linkedRequirements.length) * 100) : 0 return { ...template, status: isActive ? 'active' as ModuleStatus : 'inactive' as ModuleStatus, completionPercent, } }) const activeModulesCount = state.modules.length const totalRequirements = displayModules .filter(m => state.modules.some(sm => sm.id === m.id)) .reduce((sum, m) => sum + m.requirementsCount, 0) const totalControls = displayModules .filter(m => state.modules.some(sm => sm.id === m.id)) .reduce((sum, m) => sum + m.controlsCount, 0) async function handleActivateModule(module: DisplayModule) { const serviceModule: ServiceModule = { id: module.id, name: module.name, description: module.description, regulations: module.regulations, criticality: module.criticality, processesPersonalData: module.processesPersonalData, hasAIComponents: module.hasAIComponents, } dispatch({ type: 'ADD_MODULE', payload: serviceModule }) setActionError(null) try { const res = await fetch(`/api/sdk/v1/modules/${encodeURIComponent(module.id)}/activate`, { method: 'POST', }) if (!res.ok) throw new Error('Aktivierung fehlgeschlagen') } catch { const rollbackModules = state.modules.filter(m => m.id !== module.id) dispatch({ type: 'SET_STATE', payload: { modules: rollbackModules } }) setActionError(`Modul "${module.name}" konnte nicht aktiviert werden.`) setTimeout(() => setActionError(null), 5000) } } async function handleDeactivateModule(moduleId: string) { const previousModules = [...state.modules] const updatedModules = state.modules.filter(m => m.id !== moduleId) dispatch({ type: 'SET_STATE', payload: { modules: updatedModules } }) setActionError(null) try { const res = await fetch(`/api/sdk/v1/modules/${encodeURIComponent(moduleId)}/deactivate`, { method: 'POST', }) if (!res.ok) throw new Error('Deaktivierung fehlgeschlagen') } catch { dispatch({ type: 'SET_STATE', payload: { modules: previousModules } }) setActionError('Modul konnte nicht deaktiviert werden.') setTimeout(() => setActionError(null), 5000) } } async function handleCreateModule( newModuleName: string, newModuleCategory: ModuleCategory, newModuleDescription: string, ): Promise { if (!newModuleName.trim()) return false try { const res = await fetch('/api/sdk/v1/modules', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newModuleName, category: newModuleCategory, description: newModuleDescription, }), }) if (res.ok) { const data = await res.json() const newMod: Omit = { id: data.id || `custom-${Date.now()}`, name: newModuleName, description: newModuleDescription, category: newModuleCategory, regulations: [], criticality: 'MEDIUM', processesPersonalData: false, hasAIComponents: false, requirementsCount: 0, controlsCount: 0, } setAvailableModules(prev => [...prev, newMod]) return true } return false } catch { setActionError('Modul konnte nicht erstellt werden.') setTimeout(() => setActionError(null), 5000) return false } } return { state, availableModules, displayModules, isLoadingModules, backendError, actionError, activeModulesCount, totalRequirements, totalControls, handleActivateModule, handleDeactivateModule, handleCreateModule, } }