Files
breakpilot-compliance/admin-compliance/app/sdk/gci/_components/ISOTab.tsx
Sharang Parnerkar 7907b3f25b refactor(admin): split evidence, import, portfolio pages
Extract components and hooks from oversized pages into colocated
_components/ and _hooks/ subdirectories to enforce the 500-LOC hard cap.
page.tsx files reduced to 205, 121, and 136 LOC respectively.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 13:07:04 +02:00

77 lines
3.6 KiB
TypeScript

'use client'
import { ISOGapAnalysis } from '@/lib/sdk/gci/types'
import { ScoreCircle, LoadingSpinner } from './GCIHelpers'
export function ISOTab({ iso }: { iso: ISOGapAnalysis | null }) {
if (!iso) return <LoadingSpinner />
return (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-gray-200 p-6">
<div className="flex items-center gap-6">
<ScoreCircle score={iso.coverage_percent} size={120} label="Abdeckung" />
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900">ISO 27001:2022 Gap-Analyse</h3>
<div className="grid grid-cols-3 gap-4 mt-3">
<div className="text-center">
<div className="text-2xl font-bold text-green-600">{iso.covered_full}</div>
<div className="text-xs text-gray-500">Voll abgedeckt</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-yellow-600">{iso.covered_partial}</div>
<div className="text-xs text-gray-500">Teilweise</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-red-600">{iso.not_covered}</div>
<div className="text-xs text-gray-500">Nicht abgedeckt</div>
</div>
</div>
</div>
</div>
</div>
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-base font-semibold text-gray-900 mb-4">Kategorien</h3>
<div className="space-y-3">
{iso.category_summaries.map(cat => (
<div key={cat.category_id} className="space-y-1">
<div className="flex justify-between text-sm">
<span className="font-medium text-gray-700">{cat.category_id}: {cat.category_name}</span>
<span className="text-gray-500">{cat.covered_full}/{cat.total_controls} Controls</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3 flex overflow-hidden">
<div className="h-3 bg-green-500" style={{ width: `${(cat.covered_full / cat.total_controls) * 100}%` }} />
<div className="h-3 bg-yellow-500" style={{ width: `${(cat.covered_partial / cat.total_controls) * 100}%` }} />
</div>
</div>
))}
</div>
</div>
{iso.gaps && iso.gaps.length > 0 && (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-base font-semibold text-gray-900 mb-4">Offene Gaps ({iso.gaps.length})</h3>
<div className="space-y-2 max-h-96 overflow-y-auto">
{iso.gaps.map(gap => (
<div key={gap.control_id} className="flex items-start gap-3 p-3 border border-gray-100 rounded-lg hover:bg-gray-50">
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${
gap.priority === 'high' ? 'bg-red-100 text-red-700' :
gap.priority === 'medium' ? 'bg-yellow-100 text-yellow-700' :
'bg-gray-100 text-gray-700'
}`}>
{gap.priority}
</span>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-gray-900">{gap.control_id}: {gap.control_name}</div>
<div className="text-xs text-gray-500 mt-0.5">{gap.recommendation}</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
)
}