'use client'
import React, { useState, useEffect } from 'react'
import { useSDK } from '@/lib/sdk'
import { StepHeader } from '@/components/sdk/StepHeader'
import {
ExecutiveReport,
RiskLevel,
DeadlineSeverity,
RISK_LEVEL_INFO,
DEADLINE_SEVERITY_INFO,
getScoreColor,
getScoreBgColor,
} from '@/lib/sdk/reporting/types'
import { getExecutiveReport } from '@/lib/sdk/reporting/api'
// =============================================================================
// TYPES
// =============================================================================
type TabId = 'overview' | 'risks' | 'deadlines' | 'modules' | 'activity'
interface Tab {
id: TabId
label: string
}
// =============================================================================
// HELPER COMPONENTS
// =============================================================================
function TabNavigation({
tabs,
activeTab,
onTabChange,
}: {
tabs: Tab[]
activeTab: TabId
onTabChange: (tab: TabId) => void
}) {
return (
)
}
function ScoreCircle({ score }: { score: number }) {
const circumference = 2 * Math.PI * 60
const strokeDashoffset = circumference - (score / 100) * circumference
return (
{score}%
Compliance
)
}
function StatCard({
label,
value,
color = 'gray',
subtitle,
}: {
label: string
value: number | string
color?: 'gray' | 'blue' | 'yellow' | 'red' | 'green' | 'purple' | 'orange'
subtitle?: string
}) {
const colorClasses: Record = {
gray: 'border-gray-200 text-gray-900',
blue: 'border-blue-200 text-blue-600',
yellow: 'border-yellow-200 text-yellow-600',
red: 'border-red-200 text-red-600',
green: 'border-green-200 text-green-600',
purple: 'border-purple-200 text-purple-600',
orange: 'border-orange-200 text-orange-600',
}
return (
{label}
{value}
{subtitle &&
{subtitle}
}
)
}
function RiskBadge({ level }: { level: RiskLevel }) {
const info = RISK_LEVEL_INFO[level]
return (
{info.label}
)
}
function DeadlineBadge({ severity }: { severity: DeadlineSeverity }) {
const info = DEADLINE_SEVERITY_INFO[severity]
return (
{info.label}
)
}
// =============================================================================
// TAB CONTENTS
// =============================================================================
function OverviewTab({ report }: { report: ExecutiveReport }) {
return (
{/* Score + Key Metrics */}
= 80 ? 'green' : 'yellow'} subtitle={`${report.dsgvo.completionPercent}% vollstaendig`} />
0 ? 'red' : 'green'} subtitle={report.dsgvo.overdueDSRs > 0 ? `${report.dsgvo.overdueDSRs} ueberfaellig` : 'Keine ueberfaelligen'} />
0 ? 'red' : 'green'} subtitle={report.incidents.criticalIncidents > 0 ? `${report.incidents.criticalIncidents} kritisch` : 'Keine kritischen'} />
= 80 ? 'green' : 'yellow'} subtitle={`${report.academy.overdueCount} ueberfaellig`} />
{/* Deadlines Summary */}
{report.upcomingDeadlines.length > 0 && (
Naechste Fristen
{report.upcomingDeadlines.slice(0, 5).map((dl, i) => (
{dl.description}
{dl.module} · {dl.type}
{dl.daysLeft <= 0 ? `${Math.abs(dl.daysLeft)} Tage ueberfaellig` : `${dl.daysLeft} Tage`}
))}
)}
)
}
function RisksTab({ report }: { report: ExecutiveReport }) {
return (
{/* Overall Risk */}
Gesamt-Risikobewertung
5 ? 'orange' : 'gray'} />
0 ? 'red' : 'green'} />
{/* Per-Module Risks */}
Risiken nach Modul
{report.riskOverview.moduleRisks.map((mr, i) => (
{mr.module}
{mr.score}%
{mr.issues} Issues
))}
)
}
function DeadlinesTab({ report }: { report: ExecutiveReport }) {
return (
Alle Fristen ({report.upcomingDeadlines.length})
{report.upcomingDeadlines.length === 0 ? (
Keine bevorstehenden Fristen.
) : (
| Status |
Modul |
Typ |
Beschreibung |
Faellig am |
Verbleibend |
{report.upcomingDeadlines.map((dl, i) => (
|
{dl.module} |
{dl.type} |
{dl.description} |
{new Date(dl.dueDate).toLocaleDateString('de-DE')} |
{dl.daysLeft <= 0 ? `${Math.abs(dl.daysLeft)}d ueberfaellig` : `${dl.daysLeft}d`}
|
))}
)}
)
}
function ModulesTab({ report }: { report: ExecutiveReport }) {
return (
{/* DSGVO Module */}
DSGVO-Compliance
= 80 ? 'green' : 'yellow'} />
{/* Vendors */}
Lieferanten-Compliance
0 ? 'yellow' : 'green'} />
0 ? 'red' : 'green'} />
{/* Incidents */}
Datenschutzvorfaelle
0 ? 'orange' : 'green'} />
0 ? 'red' : 'green'} />
0 ? 'red' : 'green'} />
{/* Whistleblower */}
Hinweisgebersystem
0 ? 'yellow' : 'green'} />
0 ? 'red' : 'green'} />
0 ? 'red' : 'green'} />
{/* Academy */}
Compliance Academy
= 80 ? 'green' : 'yellow'} />
0 ? 'red' : 'green'} />
)
}
function ActivityTab({ report }: { report: ExecutiveReport }) {
return (
Letzte Aktivitaeten
{report.recentActivity.length === 0 ? (
Keine Aktivitaeten vorhanden.
) : (
{report.recentActivity.map((entry, i) => (
{entry.description}
{entry.module}
·
{entry.action}
·
{new Date(entry.timestamp).toLocaleString('de-DE')}
))}
)}
)
}
// =============================================================================
// MOCK DATA (used when backend is unavailable)
// =============================================================================
function getMockReport(): ExecutiveReport {
return {
generatedAt: new Date().toISOString(),
tenantId: 'demo',
complianceScore: 72,
dsgvo: {
processingActivities: 24,
activeProcessings: 18,
tomsImplemented: 31,
tomsPlanned: 7,
tomsTotal: 42,
completionPercent: 74,
openDSRs: 3,
overdueDSRs: 1,
dsfasCompleted: 4,
retentionPolicies: 12,
},
vendors: {
totalVendors: 15,
activeVendors: 12,
byRiskLevel: { LOW: 8, MEDIUM: 4, HIGH: 2, CRITICAL: 1 },
pendingReviews: 3,
expiredContracts: 1,
},
incidents: {
totalIncidents: 7,
openIncidents: 2,
criticalIncidents: 0,
notificationsPending: 0,
avgResolutionHours: 48.5,
},
whistleblower: {
totalReports: 4,
openReports: 1,
overdueAcknowledgments: 0,
overdueFeedbacks: 0,
avgResolutionDays: 21.3,
},
academy: {
totalCourses: 5,
totalEnrollments: 47,
completionRate: 68.5,
overdueCount: 4,
avgCompletionDays: 14.2,
},
riskOverview: {
overallLevel: 'MEDIUM',
moduleRisks: [
{ module: 'DSGVO', level: 'MEDIUM', score: 74, issues: 8 },
{ module: 'Lieferanten', level: 'HIGH', score: 55, issues: 5 },
{ module: 'Vorfaelle', level: 'LOW', score: 85, issues: 2 },
{ module: 'Hinweisgeberschutz', level: 'LOW', score: 90, issues: 1 },
{ module: 'Schulungen', level: 'MEDIUM', score: 68, issues: 4 },
],
openFindings: 12,
criticalFindings: 2,
},
upcomingDeadlines: [
{ module: 'DSGVO', type: 'Betroffenenanfrage', description: 'Auskunftsersuchen Max Mustermann', dueDate: new Date(Date.now() + 2 * 86400000).toISOString(), daysLeft: 2, severity: 'URGENT' },
{ module: 'Lieferanten', type: 'Vertragspruefung', description: 'AWS AVV-Erneuerung', dueDate: new Date(Date.now() + 14 * 86400000).toISOString(), daysLeft: 14, severity: 'WARNING' },
{ module: 'Schulungen', type: 'Pflichtschulung', description: 'DSGVO-Jahresschulung Q1 2026', dueDate: new Date(Date.now() + 30 * 86400000).toISOString(), daysLeft: 30, severity: 'INFO' },
{ module: 'Vorfaelle', type: 'Aufsichtsbehoerde', description: 'Meldung Datenpanne #7 an LfDI', dueDate: new Date(Date.now() - 1 * 86400000).toISOString(), daysLeft: -1, severity: 'OVERDUE' },
],
recentActivity: [
{ timestamp: new Date(Date.now() - 3600000).toISOString(), module: 'Academy', action: 'completed', description: 'IT-Sicherheitsschulung von Anna Mueller abgeschlossen' },
{ timestamp: new Date(Date.now() - 7200000).toISOString(), module: 'Incidents', action: 'created', description: 'Neuer Vorfall: USB-Stick mit Kundendaten verloren' },
{ timestamp: new Date(Date.now() - 86400000).toISOString(), module: 'DSGVO', action: 'updated', description: 'TOM IT-05 (Firewall-Policy) als umgesetzt markiert' },
{ timestamp: new Date(Date.now() - 172800000).toISOString(), module: 'Vendors', action: 'reviewed', description: 'Lieferanten-Assessment: Mailchimp abgeschlossen' },
],
}
}
// =============================================================================
// MAIN COMPONENT
// =============================================================================
export default function ReportingPage() {
const { state } = useSDK()
const [activeTab, setActiveTab] = useState('overview')
const [report, setReport] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let cancelled = false
async function loadReport() {
setLoading(true)
setError(null)
try {
const data = await getExecutiveReport()
if (!cancelled) setReport(data)
} catch (err) {
console.warn('Backend nicht erreichbar, verwende Demo-Daten:', err)
if (!cancelled) setReport(getMockReport())
} finally {
if (!cancelled) setLoading(false)
}
}
loadReport()
return () => { cancelled = true }
}, [])
const tabs: Tab[] = [
{ id: 'overview', label: 'Uebersicht' },
{ id: 'risks', label: 'Risiken' },
{ id: 'deadlines', label: 'Fristen' },
{ id: 'modules', label: 'Module' },
{ id: 'activity', label: 'Aktivitaeten' },
]
return (
{loading ? (
Bericht wird generiert...
) : report ? (
<>
{/* Generated timestamp */}
Generiert: {new Date(report.generatedAt).toLocaleString('de-DE')}
{activeTab === 'overview' &&
}
{activeTab === 'risks' &&
}
{activeTab === 'deadlines' &&
}
{activeTab === 'modules' &&
}
{activeTab === 'activity' &&
}
>
) : (
Bericht konnte nicht geladen werden.
{error &&
{error}
}
)}
)
}