'use client' import React, { useState, useEffect, useMemo, useCallback } from 'react' import { useSDK } from '@/lib/sdk' import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader' import { Incident, IncidentSeverity, IncidentStatus, IncidentCategory, IncidentStatistics, getHoursUntil72hDeadline, is72hDeadlineExpired } from '@/lib/sdk/incidents/types' import { fetchSDKIncidentList, createMockIncidents, createMockStatistics } from '@/lib/sdk/incidents/api' import { TabNavigation, type Tab, type TabId } from './_components/TabNavigation' import { StatCard } from './_components/StatCard' import { FilterBar } from './_components/FilterBar' import { IncidentCard } from './_components/IncidentCard' import { IncidentCreateModal } from './_components/IncidentCreateModal' import { IncidentDetailDrawer } from './_components/IncidentDetailDrawer' // ============================================================================= // MAIN PAGE // ============================================================================= export default function IncidentsPage() { const { state } = useSDK() const [activeTab, setActiveTab] = useState('overview') const [incidents, setIncidents] = useState([]) const [statistics, setStatistics] = useState(null) const [isLoading, setIsLoading] = useState(true) const [showCreateModal, setShowCreateModal] = useState(false) const [selectedIncident, setSelectedIncident] = useState(null) // Filters const [selectedSeverity, setSelectedSeverity] = useState('all') const [selectedStatus, setSelectedStatus] = useState('all') const [selectedCategory, setSelectedCategory] = useState('all') // Load data (extracted as useCallback so it can be called from modals) const loadData = useCallback(async () => { setIsLoading(true) try { const { incidents: loadedIncidents, statistics: loadedStats } = await fetchSDKIncidentList() setIncidents(loadedIncidents) setStatistics(loadedStats) } catch (error) { console.error('Fehler beim Laden der Incident-Daten:', error) // Fallback auf Mock-Daten setIncidents(createMockIncidents()) setStatistics(createMockStatistics()) } finally { setIsLoading(false) } }, []) useEffect(() => { loadData() }, [loadData]) // Calculate tab counts const tabCounts = useMemo(() => { return { active: incidents.filter(i => i.status === 'detected' || i.status === 'assessment' || i.status === 'containment' || i.status === 'remediation' ).length, notification: incidents.filter(i => i.status === 'notification_required' || i.status === 'notification_sent' || (i.authorityNotification !== null && i.authorityNotification.status === 'pending') ).length, closed: incidents.filter(i => i.status === 'closed').length, deadlineExpired: incidents.filter(i => { if (i.status === 'closed') return false if (i.authorityNotification && (i.authorityNotification.status === 'submitted' || i.authorityNotification.status === 'acknowledged')) return false if (i.riskAssessment && !i.riskAssessment.notificationRequired) return false return is72hDeadlineExpired(i.detectedAt) }).length, deadlineApproaching: incidents.filter(i => { if (i.status === 'closed') return false if (i.authorityNotification && (i.authorityNotification.status === 'submitted' || i.authorityNotification.status === 'acknowledged')) return false const hours = getHoursUntil72hDeadline(i.detectedAt) return hours > 0 && hours <= 24 }).length } }, [incidents]) // Filter incidents based on active tab and filters const filteredIncidents = useMemo(() => { let filtered = [...incidents] // Tab-based filtering if (activeTab === 'active') { filtered = filtered.filter(i => i.status === 'detected' || i.status === 'assessment' || i.status === 'containment' || i.status === 'remediation' ) } else if (activeTab === 'notification') { filtered = filtered.filter(i => i.status === 'notification_required' || i.status === 'notification_sent' || (i.authorityNotification !== null && i.authorityNotification.status === 'pending') ) } else if (activeTab === 'closed') { filtered = filtered.filter(i => i.status === 'closed') } // Severity filter if (selectedSeverity !== 'all') { filtered = filtered.filter(i => i.severity === selectedSeverity) } // Status filter if (selectedStatus !== 'all') { filtered = filtered.filter(i => i.status === selectedStatus) } // Category filter if (selectedCategory !== 'all') { filtered = filtered.filter(i => i.category === selectedCategory) } // Sort: most urgent first (overdue > deadline approaching > severity > detected time) const severityOrder: Record = { critical: 0, high: 1, medium: 2, low: 3 } return filtered.sort((a, b) => { // Closed always at the end if (a.status === 'closed' !== (b.status === 'closed')) return a.status === 'closed' ? 1 : -1 // Overdue first const aExpired = is72hDeadlineExpired(a.detectedAt) const bExpired = is72hDeadlineExpired(b.detectedAt) if (aExpired !== bExpired) return aExpired ? -1 : 1 // Then by severity if (severityOrder[a.severity] !== severityOrder[b.severity]) { return severityOrder[a.severity] - severityOrder[b.severity] } // Then by deadline urgency return getHoursUntil72hDeadline(a.detectedAt) - getHoursUntil72hDeadline(b.detectedAt) }) }, [incidents, activeTab, selectedSeverity, selectedStatus, selectedCategory]) const tabs: Tab[] = [ { id: 'overview', label: 'Uebersicht' }, { id: 'active', label: 'Aktiv', count: tabCounts.active, countColor: 'bg-orange-100 text-orange-600' }, { id: 'notification', label: 'Meldepflichtig', count: tabCounts.notification, countColor: 'bg-red-100 text-red-600' }, { id: 'closed', label: 'Abgeschlossen', count: tabCounts.closed, countColor: 'bg-green-100 text-green-600' }, { id: 'settings', label: 'Einstellungen' } ] const stepInfo = STEP_EXPLANATIONS['incidents'] const clearFilters = () => { setSelectedSeverity('all') setSelectedStatus('all') setSelectedCategory('all') } return (
{/* Step Header */} {/* Tab Navigation */} {/* Loading State */} {isLoading ? (
) : activeTab === 'settings' ? ( /* Settings Tab */

Einstellungen

Incident-Management-Einstellungen, Eskalationswege und Meldevorlagen werden in einer spaeteren Version verfuegbar sein.

) : ( <> {/* Statistics (Overview Tab) */} {activeTab === 'overview' && statistics && (
0 ? 'red' : 'green'} />
)} {/* Critical Alert: 72h deadline approaching or expired */} {(tabCounts.deadlineExpired > 0 || tabCounts.deadlineApproaching > 0) && (

{tabCounts.deadlineExpired > 0 ? `Achtung: ${tabCounts.deadlineExpired} ueberfaellige Meldung(en) - 72-Stunden-Frist ueberschritten!` : `Warnung: ${tabCounts.deadlineApproaching} Meldung(en) mit ablaufender 72-Stunden-Frist` }

{tabCounts.deadlineExpired > 0 ? 'Die gesetzliche Meldefrist nach Art. 33 DSGVO ist abgelaufen. Handeln Sie umgehend, um Bussgelder zu vermeiden. Verspaetete Meldungen muessen begruendet werden.' : 'Die 72-Stunden-Meldefrist nach Art. 33 DSGVO laeuft in Kuerze ab. Fuehren Sie eine Risikobewertung durch und entscheiden Sie ueber die Meldepflicht.' }

)} {/* Info Box (Overview Tab) */} {activeTab === 'overview' && (

Art. 33/34 DSGVO - 72-Stunden-Meldepflicht

Nach Art. 33 DSGVO muessen Datenschutzverletzungen innerhalb von 72 Stunden an die zustaendige Aufsichtsbehoerde gemeldet werden, sofern ein Risiko fuer die Rechte und Freiheiten der betroffenen Personen besteht. Bei hohem Risiko muessen gemaess Art. 34 DSGVO auch die betroffenen Personen benachrichtigt werden. Alle Vorfaelle sind unabhaengig von der Meldepflicht zu dokumentieren (Art. 33 Abs. 5).

)} {/* Filters */} {/* Incidents List */}
{filteredIncidents.map(incident => ( setSelectedIncident(incident)} /> ))}
{/* Empty State */} {filteredIncidents.length === 0 && (

Keine Vorfaelle gefunden

{selectedSeverity !== 'all' || selectedStatus !== 'all' || selectedCategory !== 'all' ? 'Passen Sie die Filter an oder' : 'Es sind noch keine Vorfaelle erfasst worden.' }

{(selectedSeverity !== 'all' || selectedStatus !== 'all' || selectedCategory !== 'all') ? ( ) : ( )}
)} )} {/* Create Modal */} {showCreateModal && ( setShowCreateModal(false)} onSuccess={() => { setShowCreateModal(false); loadData() }} /> )} {/* Detail Drawer */} {selectedIncident && ( setSelectedIncident(null)} onStatusChange={() => { setSelectedIncident(null); loadData() }} onDeleted={() => { setSelectedIncident(null); loadData() }} /> )}
) }