'use client' import React, { useState } from 'react' interface GapItem { control_id: string control_title: string control_domain: string status: string priority: string obligation_ids: string[] required_by_count: number } interface PriorityAction { rank: number action: string control_ids: string[] impact: string effort: string } interface DomainGap { domain_id: string domain_name: string total_controls: number implemented_controls: number compliance_percent: number } interface GapAnalysisResult { compliance_percent: number total_controls: number implemented_controls: number partial_controls: number missing_controls: number gaps: GapItem[] priority_actions: PriorityAction[] by_domain: Record } const UCCA_API = '/api/sdk/v1/ucca/obligations' const DOMAIN_LABELS: Record = { GOV: 'Governance', HR: 'Human Resources', IAM: 'Identity & Access', AC: 'Access Control', CRYPTO: 'Kryptographie', LOG: 'Logging & Monitoring', SDLC: 'Softwareentwicklung', OPS: 'Betrieb', NET: 'Netzwerk', BCP: 'Business Continuity', VENDOR: 'Lieferanten', DATA: 'Datenschutz', } const IMPACT_COLORS: Record = { critical: 'text-red-700 bg-red-50', high: 'text-orange-700 bg-orange-50', medium: 'text-yellow-700 bg-yellow-50', low: 'text-green-700 bg-green-50', } export default function GapAnalysisView() { const [result, setResult] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const runAnalysis = async () => { setLoading(true) setError(null) try { const res = await fetch(`${UCCA_API}/gap-analysis`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ control_status_map: {} }), }) if (!res.ok) throw new Error(`HTTP ${res.status}`) setResult(await res.json()) } catch (e) { setError(e instanceof Error ? e.message : 'Analyse fehlgeschlagen') } finally { setLoading(false) } } if (!result) { return (

TOM Gap-Analyse

Vergleicht erforderliche TOM Controls mit dem aktuellen Implementierungsstatus.

{error &&

{error}

}
) } const domains = Object.values(result.by_domain).sort((a, b) => a.compliance_percent - b.compliance_percent) return (
{/* Compliance Score */}

Compliance-Status

= 80 ? 'text-green-600' : result.compliance_percent >= 50 ? 'text-yellow-600' : 'text-red-600'}`}> {Math.round(result.compliance_percent)}%

Compliance

= 80 ? 'bg-green-500' : result.compliance_percent >= 50 ? 'bg-yellow-500' : 'bg-red-500'}`} style={{ width: `${Math.min(result.compliance_percent, 100)}%` }} />
{result.implemented_controls} implementiert {result.partial_controls} teilweise {result.missing_controls} fehlend
{/* Domain Breakdown */} {domains.length > 0 && (

Nach Domaene

{domains.map(d => (
{DOMAIN_LABELS[d.domain_id] || d.domain_id}
= 80 ? 'bg-green-400' : d.compliance_percent >= 50 ? 'bg-yellow-400' : 'bg-red-400'}`} style={{ width: `${Math.min(d.compliance_percent, 100)}%` }} />
{d.implemented_controls}/{d.total_controls}
))}
)} {/* Priority Actions */} {result.priority_actions.length > 0 && (

Prioritaere Massnahmen

{result.priority_actions.slice(0, 5).map(a => (
{a.rank}

{a.action}

{a.impact} Aufwand: {a.effort} {a.control_ids.length} Controls
))}
)} {/* Gap List */} {result.gaps.length > 0 && (

Offene Gaps ({result.gaps.length})

{result.gaps.map(g => (
{g.control_id} {g.control_title}
{g.status === 'NOT_IMPLEMENTED' ? 'Fehlend' : 'Teilweise'} {g.required_by_count}x
))}
)}
) }