'use client' import React, { useState, useEffect, useCallback } from 'react' import { GCIResult, GCIBreakdown, GCIHistoryResponse, GCIMatrixResponse, NIS2Score, ISOGapAnalysis, WeightProfile, MaturityLevel, MATURITY_INFO, getScoreColor, getScoreRingColor, } from '@/lib/sdk/gci/types' import { getGCIScore, getGCIBreakdown, getGCIHistory, getGCIMatrix, getNIS2Score, getISOGapAnalysis, getWeightProfiles, } from '@/lib/sdk/gci/api' // ============================================================================= // TYPES // ============================================================================= type TabId = 'overview' | 'breakdown' | 'nis2' | 'iso' | 'matrix' | 'audit' interface Tab { id: TabId label: string } const TABS: Tab[] = [ { id: 'overview', label: 'Uebersicht' }, { id: 'breakdown', label: 'Breakdown' }, { id: 'nis2', label: 'NIS2' }, { id: 'iso', label: 'ISO 27001' }, { id: 'matrix', label: 'Matrix' }, { id: 'audit', label: 'Audit Trail' }, ] // ============================================================================= // HELPER COMPONENTS // ============================================================================= function TabNavigation({ tabs, activeTab, onTabChange }: { tabs: Tab[]; activeTab: TabId; onTabChange: (tab: TabId) => void }) { return (
) } function ScoreCircle({ score, size = 144, label }: { score: number; size?: number; label?: string }) { const radius = (size / 2) - 12 const circumference = 2 * Math.PI * radius const strokeDashoffset = circumference - (score / 100) * circumference return (
{score.toFixed(1)} {label && {label}}
) } function MaturityBadge({ level }: { level: MaturityLevel }) { const info = MATURITY_INFO[level] || MATURITY_INFO.HIGH_RISK return ( {info.label} ) } function AreaScoreBar({ name, score, weight }: { name: string; score: number; weight: number }) { return (
{name} {score.toFixed(1)}%
Gewichtung: {(weight * 100).toFixed(0)}%
) } function LoadingSpinner() { return (
) } function ErrorMessage({ message, onRetry }: { message: string; onRetry?: () => void }) { return (

{message}

{onRetry && ( )}
) } // ============================================================================= // TAB: OVERVIEW // ============================================================================= function OverviewTab({ gci, history, profiles, selectedProfile, onProfileChange }: { gci: GCIResult history: GCIHistoryResponse | null profiles: WeightProfile[] selectedProfile: string onProfileChange: (p: string) => void }) { return (
{/* Profile Selector */} {profiles.length > 0 && (
)} {/* Main Score */}

Gesamt-Compliance-Index

Berechnet: {new Date(gci.calculated_at).toLocaleString('de-DE')}

{MATURITY_INFO[gci.maturity_level]?.description || ''}

{/* Area Scores */}

Regulierungsbereiche

{gci.area_scores.map(area => ( ))}
{/* History Chart (simplified) */} {history && history.snapshots.length > 0 && (

Verlauf

{history.snapshots.map((snap, i) => (
{snap.score.toFixed(0)}
{new Date(snap.calculated_at).toLocaleDateString('de-DE', { month: 'short' })}
))}
)} {/* Adjustments */}
Kritikalitaets-Multiplikator
{gci.criticality_multiplier.toFixed(2)}x
Incident-Korrektur
{gci.incident_adjustment > 0 ? '+' : ''}{gci.incident_adjustment.toFixed(1)}
) } // ============================================================================= // TAB: BREAKDOWN // ============================================================================= function BreakdownTab({ breakdown }: { breakdown: GCIBreakdown | null; loading: boolean }) { if (!breakdown) return return (
{/* Level 1: Modules */}

Level 1: Modul-Scores

{breakdown.level1_modules.map(m => ( ))}
Modul Kategorie Zugewiesen Abgeschlossen Raw Score Validitaet Final
{m.module_name} {m.category} {m.assigned} {m.completed} {(m.raw_score * 100).toFixed(1)}% {(m.validity_factor * 100).toFixed(0)}% {(m.final_score * 100).toFixed(1)}%
{/* Level 2: Areas */}

Level 2: Regulierungsbereiche (risikogewichtet)

{breakdown.level2_areas.map(area => (

{area.area_name}

{area.area_score.toFixed(1)}%
{area.modules.map(m => (
{m.module_name} {(m.final_score * 100).toFixed(0)}% (w:{m.risk_weight.toFixed(1)})
))}
))}
) } // ============================================================================= // TAB: NIS2 // ============================================================================= function NIS2Tab({ nis2 }: { nis2: NIS2Score | null }) { if (!nis2) return return (
{/* NIS2 Overall */}

NIS2 Compliance Score

Network and Information Security Directive 2 (EU 2022/2555)

{/* NIS2 Areas */}

NIS2 Bereiche

{nis2.areas.map(area => ( ))}
{/* NIS2 Roles */} {nis2.role_scores && nis2.role_scores.length > 0 && (

Rollen-Compliance

{nis2.role_scores.map(role => (
{role.role_name}
{(role.completion_rate * 100).toFixed(0)}% {role.modules_completed}/{role.modules_required} Module
))}
)}
) } // ============================================================================= // TAB: ISO 27001 // ============================================================================= function ISOTab({ iso }: { iso: ISOGapAnalysis | null }) { if (!iso) return return (
{/* Coverage Overview */}

ISO 27001:2022 Gap-Analyse

{iso.covered_full}
Voll abgedeckt
{iso.covered_partial}
Teilweise
{iso.not_covered}
Nicht abgedeckt
{/* Category Summaries */}

Kategorien

{iso.category_summaries.map(cat => { const coveragePercent = cat.total_controls > 0 ? ((cat.covered_full + cat.covered_partial * 0.5) / cat.total_controls) * 100 : 0 return (
{cat.category_id}: {cat.category_name} {cat.covered_full}/{cat.total_controls} Controls
) })}
{/* Gaps */} {iso.gaps && iso.gaps.length > 0 && (

Offene Gaps ({iso.gaps.length})

{iso.gaps.map(gap => (
{gap.priority}
{gap.control_id}: {gap.control_name}
{gap.recommendation}
))}
)}
) } // ============================================================================= // TAB: MATRIX // ============================================================================= function MatrixTab({ matrix }: { matrix: GCIMatrixResponse | null }) { if (!matrix || !matrix.matrix) return const regulations = matrix.matrix.length > 0 ? Object.keys(matrix.matrix[0].regulations) : [] return (

Compliance-Matrix (Rollen x Regulierungen)

{regulations.map(r => ( ))} {matrix.matrix.map(entry => ( {regulations.map(r => ( ))} ))}
Rolle{r}Gesamt Module
{entry.role_name} {entry.regulations[r].toFixed(0)}% {entry.overall_score.toFixed(0)}% {entry.completed_modules}/{entry.required_modules}
) } // ============================================================================= // TAB: AUDIT TRAIL // ============================================================================= function AuditTab({ gci }: { gci: GCIResult }) { return (

Audit Trail - Berechnung GCI {gci.gci_score.toFixed(1)}

Jeder Schritt der GCI-Berechnung ist nachvollziehbar und prueffaehig dokumentiert.

{gci.audit_trail.map((entry, i) => (
{entry.factor} {entry.value > 0 ? '+' : ''}{entry.value.toFixed(2)}

{entry.description}

))}
) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function GCIPage() { const [activeTab, setActiveTab] = useState('overview') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [gci, setGCI] = useState(null) const [breakdown, setBreakdown] = useState(null) const [history, setHistory] = useState(null) const [matrix, setMatrix] = useState(null) const [nis2, setNIS2] = useState(null) const [iso, setISO] = useState(null) const [profiles, setProfiles] = useState([]) const [selectedProfile, setSelectedProfile] = useState('default') const loadData = useCallback(async (profile?: string) => { setLoading(true) setError(null) try { const [gciRes, historyRes, profilesRes] = await Promise.all([ getGCIScore(profile), getGCIHistory(), getWeightProfiles(), ]) setGCI(gciRes) setHistory(historyRes) setProfiles(profilesRes.profiles || []) } catch (err: any) { setError(err.message || 'Fehler beim Laden der GCI-Daten') } finally { setLoading(false) } }, []) useEffect(() => { loadData(selectedProfile) }, [selectedProfile, loadData]) // Lazy-load tab data useEffect(() => { if (activeTab === 'breakdown' && !breakdown && gci) { getGCIBreakdown(selectedProfile).then(setBreakdown).catch(() => {}) } if (activeTab === 'nis2' && !nis2) { getNIS2Score().then(setNIS2).catch(() => {}) } if (activeTab === 'iso' && !iso) { getISOGapAnalysis().then(setISO).catch(() => {}) } if (activeTab === 'matrix' && !matrix) { getGCIMatrix().then(setMatrix).catch(() => {}) } }, [activeTab, breakdown, nis2, iso, matrix, gci, selectedProfile]) const handleProfileChange = (profile: string) => { setSelectedProfile(profile) setBreakdown(null) // reset breakdown to reload } return (
{/* Header */}

Gesamt-Compliance-Index (GCI)

4-stufiges, mathematisch fundiertes Compliance-Scoring

{/* Tabs */} {/* Content */} {error && loadData(selectedProfile)} />} {loading && !gci ? ( ) : gci ? (
{activeTab === 'overview' && ( )} {activeTab === 'breakdown' && ( )} {activeTab === 'nis2' && } {activeTab === 'iso' && } {activeTab === 'matrix' && } {activeTab === 'audit' && }
) : null}
) }