153 lines
6.2 KiB
TypeScript
153 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
// =============================================================================
|
|
// Step 6: Review & Export
|
|
// Summary, derived TOMs table, gap analysis, and export
|
|
// =============================================================================
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
|
import { SummaryCard, TOMsTable, GapAnalysisPanel, ExportPanel } from './ReviewExportPanels'
|
|
|
|
export function ReviewExportStep() {
|
|
const { state, deriveTOMs, completeCurrentStep } = useTOMGenerator()
|
|
const [activeTab, setActiveTab] = useState<'summary' | 'toms' | 'gaps' | 'export'>('summary')
|
|
|
|
// Derive TOMs if not already done
|
|
useEffect(() => {
|
|
if (state.derivedTOMs.length === 0 && state.companyProfile && state.dataProfile) {
|
|
deriveTOMs()
|
|
}
|
|
}, [state, deriveTOMs])
|
|
|
|
// Mark step as complete when viewing
|
|
useEffect(() => {
|
|
completeCurrentStep({ reviewed: true })
|
|
}, [completeCurrentStep])
|
|
|
|
// Statistics
|
|
const stats = {
|
|
totalTOMs: state.derivedTOMs.length,
|
|
required: state.derivedTOMs.filter((t) => t.applicability === 'REQUIRED').length,
|
|
implemented: state.derivedTOMs.filter((t) => t.implementationStatus === 'IMPLEMENTED').length,
|
|
partial: state.derivedTOMs.filter((t) => t.implementationStatus === 'PARTIAL').length,
|
|
documents: state.documents.length,
|
|
score: state.gapAnalysis?.overallScore ?? 0,
|
|
}
|
|
|
|
const tabs = [
|
|
{ id: 'summary', label: 'Zusammenfassung' },
|
|
{ id: 'toms', label: 'TOMs-Tabelle' },
|
|
{ id: 'gaps', label: 'Lückenanalyse' },
|
|
{ id: 'export', label: 'Export' },
|
|
]
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Tabs */}
|
|
<div className="border-b">
|
|
<nav className="flex space-x-8">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id as typeof activeTab)}
|
|
className={`py-3 px-1 border-b-2 font-medium text-sm transition-all ${
|
|
activeTab === tab.id
|
|
? 'border-blue-500 text-blue-600'
|
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Tab Content */}
|
|
<div className="min-h-[400px]">
|
|
{activeTab === 'summary' && (
|
|
<div className="space-y-6">
|
|
{/* Stats Grid */}
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
|
|
<SummaryCard title="Gesamt TOMs" value={stats.totalTOMs} variant="default" />
|
|
<SummaryCard title="Erforderlich" value={stats.required} variant="danger" />
|
|
<SummaryCard title="Umgesetzt" value={stats.implemented} variant="success" />
|
|
<SummaryCard title="Teilweise" value={stats.partial} variant="warning" />
|
|
<SummaryCard title="Dokumente" value={stats.documents} variant="default" />
|
|
<SummaryCard
|
|
title="Score"
|
|
value={`${stats.score}%`}
|
|
variant={stats.score >= 80 ? 'success' : stats.score >= 50 ? 'warning' : 'danger'}
|
|
/>
|
|
</div>
|
|
|
|
{/* Profile Summaries */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* Company */}
|
|
{state.companyProfile && (
|
|
<div className="bg-white border rounded-lg p-4">
|
|
<h4 className="font-medium text-gray-900 mb-3">Unternehmen</h4>
|
|
<dl className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">Name:</dt>
|
|
<dd className="text-gray-900">{state.companyProfile.name}</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">Branche:</dt>
|
|
<dd className="text-gray-900">{state.companyProfile.industry}</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">Rolle:</dt>
|
|
<dd className="text-gray-900">{state.companyProfile.role}</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
)}
|
|
|
|
{/* Risk */}
|
|
{state.riskProfile && (
|
|
<div className="bg-white border rounded-lg p-4">
|
|
<h4 className="font-medium text-gray-900 mb-3">Schutzbedarf</h4>
|
|
<dl className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">Level:</dt>
|
|
<dd className={`font-medium ${
|
|
state.riskProfile.protectionLevel === 'VERY_HIGH' ? 'text-red-600' :
|
|
state.riskProfile.protectionLevel === 'HIGH' ? 'text-yellow-600' : 'text-green-600'
|
|
}`}>
|
|
{state.riskProfile.protectionLevel}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">DSFA erforderlich:</dt>
|
|
<dd className={state.riskProfile.dsfaRequired ? 'text-red-600 font-medium' : 'text-gray-900'}>
|
|
{state.riskProfile.dsfaRequired ? 'Ja' : 'Nein'}
|
|
</dd>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<dt className="text-gray-500">CIA (V/I/V):</dt>
|
|
<dd className="text-gray-900">
|
|
{state.riskProfile.ciaAssessment.confidentiality}/
|
|
{state.riskProfile.ciaAssessment.integrity}/
|
|
{state.riskProfile.ciaAssessment.availability}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'toms' && <TOMsTable />}
|
|
|
|
{activeTab === 'gaps' && <GapAnalysisPanel />}
|
|
|
|
{activeTab === 'export' && <ExportPanel />}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ReviewExportStep
|