'use client' import { useMemo } from 'react' import Link from 'next/link' import { useVendorCompliance, getRiskLevelFromScore, getRiskLevelColor, generateRiskMatrix, SEVERITY_DEFINITIONS, countFindingsBySeverity, } from '@/lib/sdk/vendor-compliance' export default function RisksPage() { const { vendors, findings, riskOverview, isLoading } = useVendorCompliance() const riskMatrix = useMemo(() => generateRiskMatrix(), []) const findingsBySeverity = useMemo(() => { return countFindingsBySeverity(findings) }, [findings]) const openFindings = useMemo(() => { return findings.filter((f) => f.status === 'OPEN' || f.status === 'IN_PROGRESS') }, [findings]) const highRiskVendors = useMemo(() => { return vendors.filter((v) => v.residualRiskScore >= 60) }, [vendors]) if (isLoading) { return (
) } return (
{/* Header */}

Risiko-Dashboard

Übersicht über Vendor-Risiken und offene Findings

{/* Risk Overview Cards */}
{/* Risk Matrix */}

Risikomatrix

{[1, 2, 3, 4, 5].map((impact) => ( ))} {[5, 4, 3, 2, 1].map((likelihood) => ( {[1, 2, 3, 4, 5].map((impact) => { const cell = riskMatrix[likelihood - 1]?.[impact - 1] const colors = cell ? getRiskLevelColor(cell.level) : { bg: '', text: '' } const vendorCount = vendors.filter((v) => { const vLikelihood = Math.ceil(v.residualRiskScore / 20) const vImpact = Math.ceil(v.inherentRiskScore / 20) return vLikelihood === likelihood && vImpact === impact }).length return ( ) })} ))}
{impact}
{likelihood}
{vendorCount > 0 ? vendorCount : '-'}
Eintrittswahrscheinlichkeit ↑ Auswirkung →
{/* Findings by Severity */}

Findings nach Schweregrad

{(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const).map((severity) => { const count = findingsBySeverity[severity] || 0 const total = findings.length const percentage = total > 0 ? (count / total) * 100 : 0 const def = SEVERITY_DEFINITIONS[severity] return (
{def.label.de} {count}

{def.responseTime.de}

) })}
{/* High Risk Vendors */}

High-Risk Vendors

{highRiskVendors.map((vendor) => { const riskLevel = getRiskLevelFromScore(vendor.residualRiskScore / 4) const colors = getRiskLevelColor(riskLevel) return (

{vendor.name}

{vendor.serviceDescription}

Residual Risk

{vendor.residualRiskScore}%

{riskLevel}
) })} {highRiskVendors.length === 0 && (
Keine High-Risk Vendors vorhanden
)}
{/* Open Findings */}

Offene Findings ({openFindings.length})

{openFindings.slice(0, 10).map((finding) => { const vendor = vendors.find((v) => v.id === finding.vendorId) const severityColors = { LOW: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200', MEDIUM: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200', HIGH: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200', CRITICAL: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200', } return (
{finding.severity} {finding.category}

{finding.title.de}

{finding.description.de}

{vendor && (

Vendor: {vendor.name}

)}
Details
) })} {openFindings.length === 0 && (
Keine offenen Findings
)}
) } function RiskCard({ title, value, suffix, color, }: { title: string value: number suffix?: string color: 'purple' | 'blue' | 'red' | 'orange' }) { const colors = { purple: 'bg-purple-50 dark:bg-purple-900/20 text-purple-600 dark:text-purple-400', blue: 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400', red: 'bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400', orange: 'bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400', } return (

{title}

{value} {suffix}

) }