'use client'; import { useState, useEffect } from 'react'; // Types 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; } // Service Type Icons and Colors 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-gray-700', bgColor: 'bg-gray-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); // Filters const [typeFilter, setTypeFilter] = useState('all'); const [criticalityFilter, setCriticalityFilter] = useState('all'); const [piiFilter, setPiiFilter] = useState(null); const [aiFilter, setAiFilter] = useState(null); const [searchTerm, setSearchTerm] = useState(''); // Selected module for detail view const [selectedModule, setSelectedModule] = useState(null); const [loadingDetail, setLoadingDetail] = useState(false); // AI Risk Assessment const [riskAssessment, setRiskAssessment] = useState<{ overall_risk: string; risk_factors: Array<{ factor: string; severity: string; likelihood: string }>; recommendations: string[]; compliance_gaps: string[]; confidence_score: number; } | null>(null); const [loadingRisk, setLoadingRisk] = useState(false); const [showRiskPanel, setShowRiskPanel] = useState(false); const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000'; const API_BASE = `${BACKEND_URL}/api/v1/compliance`; 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_BASE}/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_BASE}/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_BASE}/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_BASE}/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 assessModuleRisk = async (moduleId: string) => { setLoadingRisk(true); setShowRiskPanel(true); setRiskAssessment(null); try { const res = await fetch(`${API_BASE}/ai/assess-risk`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ module_id: moduleId }), }); if (res.ok) { const data = await res.json(); setRiskAssessment(data); } else { alert('AI-Risikobewertung fehlgeschlagen'); } } catch (err) { alert('Netzwerkfehler bei AI-Risikobewertung'); } finally { setLoadingRisk(false); } }; // Filter modules by search term 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)) ); }); // Group by type for visualization 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

{/* 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 px-3 py-2 text-sm w-full" />
{/* Error */} {error && (
{error}
)} {/* Loading */} {loading && (
Lade Module...
)} {/* Main Content - Two Column Layout */} {!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-gray-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...
) : (
{/* Description */} {selectedModule.description && (
Beschreibung
{selectedModule.description}
)} {/* Technical Details */}
{selectedModule.port && (
Port: {selectedModule.port}
)}
Criticality: {selectedModule.criticality}
{/* Technology Stack */}
Tech Stack
{selectedModule.technology_stack.map((tech, i) => ( {tech} ))}
{/* Data Categories */} {selectedModule.data_categories.length > 0 && (
Daten-Kategorien
{selectedModule.data_categories.map((cat, i) => ( {cat} ))}
)} {/* Flags */}
{selectedModule.processes_pii && ( Verarbeitet PII )} {selectedModule.ai_components && ( AI-Komponenten )} {selectedModule.processes_health_data && ( Gesundheitsdaten )}
{/* Regulations */} {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}
)}
))}
)} {/* Owner */} {selectedModule.owner_team && (
Owner
{selectedModule.owner_team}
)} {/* Repository */} {selectedModule.repository_path && (
Repository
{selectedModule.repository_path}
)} {/* AI Risk Assessment Button */}
{/* AI Risk Assessment Panel */} {showRiskPanel && (

πŸ€– AI Risikobewertung

{loadingRisk ? (
Analysiere Compliance-Risiken...
) : riskAssessment ? (
{/* Overall Risk */}
Gesamtrisiko: {riskAssessment.overall_risk.toUpperCase()} ({Math.round(riskAssessment.confidence_score * 100)}% Konfidenz)
{/* Risk Factors */} {riskAssessment.risk_factors.length > 0 && (
Risikofaktoren
{riskAssessment.risk_factors.map((factor, i) => (
{factor.factor} {factor.severity}
))}
)} {/* Compliance Gaps */} {riskAssessment.compliance_gaps.length > 0 && (
Compliance-LΓΌcken
    {riskAssessment.compliance_gaps.map((gap, i) => (
  • ⚠ {gap}
  • ))}
)} {/* Recommendations */} {riskAssessment.recommendations.length > 0 && (
Empfehlungen
    {riskAssessment.recommendations.map((rec, i) => (
  • β†’ {rec}
  • ))}
)}
) : (
Klicken Sie auf "AI Risikobewertung" um eine Analyse zu starten.
)}
)}
)}
)}
)} {/* 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}
))}
)}
); }