Files
breakpilot-compliance/admin-compliance/app/sdk/compliance-hub/page.tsx
Sharang Parnerkar 2ade65431a refactor(admin): split compliance-hub, obligations, document-generator pages
Each page.tsx was >1000 LOC; extract components to _components/ and hooks
to _hooks/ so page files stay under 500 LOC (164 / 255 / 243 respectively).
Zero behavior changes — logic relocated verbatim.

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

165 lines
6.2 KiB
TypeScript

'use client'
/**
* Compliance Hub Page (SDK Version - Zusatzmodul)
*
* Central compliance management dashboard with tabs:
* - Uebersicht: Score, Stats, Quick Access, Findings
* - Roadmap: 4-column Kanban (Quick Wins / Must Have / Should Have / Nice to Have)
* - Module: Grid with module cards + progress bars
* - Trend: Score history chart
* - Traceability: Evidence traceability matrix
*/
import { useComplianceHub } from './_hooks/useComplianceHub'
import { OverviewTab } from './_components/OverviewTab'
import { RoadmapTab } from './_components/RoadmapTab'
import { ModulesTab } from './_components/ModulesTab'
import { TrendTab } from './_components/TrendTab'
import { TraceabilityTab } from './_components/TraceabilityTab'
import type { TabKey } from './_components/types'
const tabs: { key: TabKey; label: string }[] = [
{ key: 'overview', label: 'Uebersicht' },
{ key: 'roadmap', label: 'Roadmap' },
{ key: 'modules', label: 'Module' },
{ key: 'trend', label: 'Trend' },
{ key: 'traceability', label: 'Traceability' },
]
export default function ComplianceHubPage() {
const hub = useComplianceHub()
const score = hub.dashboard?.compliance_score || 0
const scoreColor = score >= 80 ? 'text-green-600' : score >= 60 ? 'text-yellow-600' : 'text-red-600'
const scoreBgColor = score >= 80 ? 'bg-green-500' : score >= 60 ? 'bg-yellow-500' : 'bg-red-500'
return (
<div className="space-y-6">
{/* Title Card */}
<div className="bg-white rounded-xl shadow-sm border p-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-slate-900">Compliance Hub</h1>
<p className="text-slate-500 mt-1">
Zentrale Verwaltung aller Compliance-Anforderungen nach DSGVO, AI Act, BSI TR-03161 und weiteren Regulierungen.
</p>
</div>
<button
onClick={hub.saveSnapshot}
disabled={hub.savingSnapshot}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50 text-sm"
>
{hub.savingSnapshot ? 'Speichere...' : 'Score-Snapshot speichern'}
</button>
</div>
</div>
{/* Tabs */}
<div className="bg-white rounded-xl shadow-sm border">
<div className="flex border-b">
{tabs.map(tab => (
<button
key={tab.key}
onClick={() => hub.setActiveTab(tab.key)}
className={`px-6 py-3 text-sm font-medium transition-colors ${
hub.activeTab === tab.key
? 'text-purple-600 border-b-2 border-purple-600'
: 'text-slate-500 hover:text-slate-700'
}`}
>
{tab.label}
</button>
))}
</div>
</div>
{/* Error Banner */}
{hub.error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 flex items-center gap-3">
<svg className="w-5 h-5 text-red-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-red-700">{hub.error}</span>
<button onClick={hub.loadData} className="ml-auto text-red-600 hover:text-red-800 text-sm font-medium">
Erneut versuchen
</button>
</div>
)}
{/* Seed Button if no data */}
{!hub.loading && (hub.dashboard?.total_controls || 0) === 0 && (
<div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<div className="flex items-center justify-between">
<div>
<p className="font-medium text-yellow-800">Keine Compliance-Daten vorhanden</p>
<p className="text-sm text-yellow-700">Initialisieren Sie die Datenbank mit den Seed-Daten.</p>
</div>
<button
onClick={hub.seedDatabase}
disabled={hub.seeding}
className="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 disabled:opacity-50"
>
{hub.seeding ? 'Initialisiere...' : 'Datenbank initialisieren'}
</button>
</div>
</div>
)}
{hub.loading ? (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-600" />
</div>
) : (
<>
{hub.activeTab === 'overview' && (
<OverviewTab
dashboard={hub.dashboard}
mappings={hub.mappings}
findings={hub.findings}
nextActions={hub.nextActions}
evidenceDistribution={hub.evidenceDistribution}
score={score}
scoreColor={scoreColor}
scoreBgColor={scoreBgColor}
loadData={hub.loadData}
regulations={hub.regulations}
/>
)}
{hub.activeTab === 'roadmap' && (
<RoadmapTab roadmap={hub.roadmap} />
)}
{hub.activeTab === 'modules' && (
<ModulesTab moduleStatus={hub.moduleStatus} />
)}
{hub.activeTab === 'trend' && (
<TrendTab
scoreHistory={hub.scoreHistory}
savingSnapshot={hub.savingSnapshot}
saveSnapshot={hub.saveSnapshot}
/>
)}
{hub.activeTab === 'traceability' && (
<TraceabilityTab
traceabilityMatrix={hub.traceabilityMatrix}
traceabilityLoading={hub.traceabilityLoading}
traceabilityFilter={hub.traceabilityFilter}
setTraceabilityFilter={hub.setTraceabilityFilter}
traceabilityDomainFilter={hub.traceabilityDomainFilter}
setTraceabilityDomainFilter={hub.setTraceabilityDomainFilter}
expandedControls={hub.expandedControls}
expandedEvidence={hub.expandedEvidence}
toggleControlExpanded={hub.toggleControlExpanded}
toggleEvidenceExpanded={hub.toggleEvidenceExpanded}
/>
)}
</>
)}
</div>
)
}