'use client' /** * Service Module Registry Page * * Features: * - List all Breakpilot services with regulation mappings * - Filter by type, criticality, PII, AI * - Detail panel with regulations * - Seed functionality */ import { useState, useEffect } from 'react' import Link from 'next/link' import { PagePurpose } from '@/components/common/PagePurpose' interface ServiceModule { id: string name: string display_name: string description: string | null service_type: string port: number | null technology_stack: string[] repository_path: string | null docker_image: string | null data_categories: string[] processes_pii: boolean processes_health_data: boolean ai_components: boolean criticality: string owner_team: string | null is_active: boolean compliance_score: number | null regulation_count: number risk_count: number created_at: string regulations?: Array<{ code: string name: string relevance_level: string notes: string | null }> } interface ModulesOverview { total_modules: number modules_by_type: Record modules_by_criticality: Record modules_processing_pii: number modules_with_ai: number average_compliance_score: number | null regulations_coverage: Record } const SERVICE_TYPE_CONFIG: Record = { backend: { icon: '⚙️', color: 'text-blue-700', bgColor: 'bg-blue-100' }, database: { icon: '🗄️', color: 'text-purple-700', bgColor: 'bg-purple-100' }, ai: { icon: '🤖', color: 'text-pink-700', bgColor: 'bg-pink-100' }, communication: { icon: '💬', color: 'text-green-700', bgColor: 'bg-green-100' }, storage: { icon: '📦', color: 'text-orange-700', bgColor: 'bg-orange-100' }, infrastructure: { icon: '🌐', color: 'text-slate-700', bgColor: 'bg-slate-100' }, monitoring: { icon: '📊', color: 'text-cyan-700', bgColor: 'bg-cyan-100' }, security: { icon: '🔒', color: 'text-red-700', bgColor: 'bg-red-100' }, } const CRITICALITY_CONFIG: Record = { critical: { color: 'text-red-700', bgColor: 'bg-red-100' }, high: { color: 'text-orange-700', bgColor: 'bg-orange-100' }, medium: { color: 'text-yellow-700', bgColor: 'bg-yellow-100' }, low: { color: 'text-green-700', bgColor: 'bg-green-100' }, } const RELEVANCE_CONFIG: Record = { critical: { color: 'text-red-700', bgColor: 'bg-red-100' }, high: { color: 'text-orange-700', bgColor: 'bg-orange-100' }, medium: { color: 'text-yellow-700', bgColor: 'bg-yellow-100' }, low: { color: 'text-green-700', bgColor: 'bg-green-100' }, } export default function ModulesPage() { const [modules, setModules] = useState([]) const [overview, setOverview] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [typeFilter, setTypeFilter] = useState('all') const [criticalityFilter, setCriticalityFilter] = useState('all') const [piiFilter, setPiiFilter] = useState(null) const [aiFilter, setAiFilter] = useState(null) const [searchTerm, setSearchTerm] = useState('') const [selectedModule, setSelectedModule] = useState(null) const [loadingDetail, setLoadingDetail] = useState(false) useEffect(() => { fetchModules() fetchOverview() }, []) const fetchModules = async () => { try { setLoading(true) const params = new URLSearchParams() if (typeFilter !== 'all') params.append('service_type', typeFilter) if (criticalityFilter !== 'all') params.append('criticality', criticalityFilter) if (piiFilter !== null) params.append('processes_pii', String(piiFilter)) if (aiFilter !== null) params.append('ai_components', String(aiFilter)) const url = `/api/admin/compliance/modules${params.toString() ? '?' + params.toString() : ''}` const res = await fetch(url) if (!res.ok) throw new Error('Failed to fetch modules') const data = await res.json() setModules(data.modules || []) } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error') } finally { setLoading(false) } } const fetchOverview = async () => { try { const res = await fetch(`/api/admin/compliance/modules/overview`) if (!res.ok) throw new Error('Failed to fetch overview') const data = await res.json() setOverview(data) } catch (err) { console.error('Failed to fetch overview:', err) } } const fetchModuleDetail = async (moduleId: string) => { try { setLoadingDetail(true) const res = await fetch(`/api/admin/compliance/modules/${moduleId}`) if (!res.ok) throw new Error('Failed to fetch module details') const data = await res.json() setSelectedModule(data) } catch (err) { console.error('Failed to fetch module details:', err) } finally { setLoadingDetail(false) } } const seedModules = async (force: boolean = false) => { try { const res = await fetch(`/api/admin/compliance/modules/seed`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ force }), }) if (!res.ok) throw new Error('Failed to seed modules') const data = await res.json() alert(`Seeded ${data.modules_created} modules with ${data.mappings_created} regulation mappings`) fetchModules() fetchOverview() } catch (err) { alert('Failed to seed modules: ' + (err instanceof Error ? err.message : 'Unknown error')) } } const filteredModules = modules.filter(m => { if (!searchTerm) return true const term = searchTerm.toLowerCase() return ( m.name.toLowerCase().includes(term) || m.display_name.toLowerCase().includes(term) || (m.description && m.description.toLowerCase().includes(term)) || m.technology_stack.some(t => t.toLowerCase().includes(term)) ) }) const modulesByType = filteredModules.reduce((acc, m) => { const type = m.service_type || 'unknown' if (!acc[type]) acc[type] = [] acc[type].push(m) return acc }, {} as Record) return (
{/* Header */}

Service Module Registry

Alle {overview?.total_modules || 0} Breakpilot-Services mit Regulation-Mappings

Compliance Hub
{/* Page Purpose */} {/* Overview Stats */} {overview && (

{overview.total_modules}

Services

{overview.modules_by_criticality?.critical || 0}

Critical

{overview.modules_processing_pii}

PII-Processing

{overview.modules_with_ai}

AI-Komponenten

{Object.keys(overview.regulations_coverage || {}).length}

Regulations

{overview.average_compliance_score !== null ? `${overview.average_compliance_score}%` : 'N/A'}

Avg. Score

)} {/* Filters */}
setSearchTerm(e.target.value)} className="border rounded-lg px-3 py-2 text-sm w-full" />
{/* Error */} {error && (
{error}
)} {/* Main Content */} {loading ? (
) : (
{/* Module List */}
{Object.entries(modulesByType).map(([type, typeModules]) => (
{SERVICE_TYPE_CONFIG[type]?.icon || '📁'} {type.charAt(0).toUpperCase() + type.slice(1)} ({typeModules.length})
{typeModules.map((module) => (
fetchModuleDetail(module.name)} className={`p-4 cursor-pointer hover:bg-slate-50 transition ${ selectedModule?.id === module.id ? 'bg-blue-50' : '' }`} >
{module.display_name} {module.port && ( :{module.port} )}
{module.name}
{module.description && (
{module.description}
)}
{module.technology_stack.slice(0, 4).map((tech, i) => ( {tech} ))} {module.technology_stack.length > 4 && ( +{module.technology_stack.length - 4} )}
{module.criticality}
{module.processes_pii && ( PII )} {module.ai_components && ( AI )}
{module.regulation_count} Regulations
))}
))} {filteredModules.length === 0 && !loading && (
Keine Module gefunden.
)}
{/* Detail Panel */} {selectedModule && (
{SERVICE_TYPE_CONFIG[selectedModule.service_type]?.icon || '📁'}

{selectedModule.display_name}

{selectedModule.name}
{loadingDetail ? (
Lade Details...
) : (
{selectedModule.description && (
Beschreibung
{selectedModule.description}
)}
{selectedModule.port && (
Port: {selectedModule.port}
)}
Criticality: {selectedModule.criticality}
Tech Stack
{selectedModule.technology_stack.map((tech, i) => ( {tech} ))}
{selectedModule.data_categories.length > 0 && (
Daten-Kategorien
{selectedModule.data_categories.map((cat, i) => ( {cat} ))}
)}
{selectedModule.processes_pii && ( Verarbeitet PII )} {selectedModule.ai_components && ( AI-Komponenten )} {selectedModule.processes_health_data && ( Gesundheitsdaten )}
{selectedModule.regulations && selectedModule.regulations.length > 0 && (
Applicable Regulations ({selectedModule.regulations.length})
{selectedModule.regulations.map((reg, i) => (
{reg.code} {reg.relevance_level}
{reg.name}
{reg.notes && (
{reg.notes}
)}
))}
)} {selectedModule.owner_team && (
Owner
{selectedModule.owner_team}
)} {selectedModule.repository_path && (
Repository
{selectedModule.repository_path}
)}
)}
)}
)} {/* Regulations Coverage Overview */} {overview && overview.regulations_coverage && Object.keys(overview.regulations_coverage).length > 0 && (

Regulation Coverage

{Object.entries(overview.regulations_coverage) .sort(([, a], [, b]) => b - a) .map(([code, count]) => (
{count}
{code}
))}
)}
) }