'use client' import React, { useState, useEffect } from 'react' import type { Incident, IncidentStatus, IncidentSeverity } from './types' import { INCIDENT_STATUS_LABELS, INCIDENT_STATUS_COLORS, SEVERITY_LABELS, SEVERITY_COLORS, } from './types' function CountdownTimer({ detectedAt }: { detectedAt: string }) { const [remaining, setRemaining] = useState('') const [overdue, setOverdue] = useState(false) useEffect(() => { function update() { const detected = new Date(detectedAt).getTime() const deadline = detected + 72 * 60 * 60 * 1000 const now = Date.now() const diff = deadline - now if (diff <= 0) { const overdueMs = Math.abs(diff) const hours = Math.floor(overdueMs / (1000 * 60 * 60)) const mins = Math.floor((overdueMs % (1000 * 60 * 60)) / (1000 * 60)) setRemaining(`${hours}h ${mins}min ueberfaellig`) setOverdue(true) } else { const hours = Math.floor(diff / (1000 * 60 * 60)) const mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)) setRemaining(`${hours}h ${mins}min verbleibend`) setOverdue(false) } } update() const interval = setInterval(update, 60000) return () => clearInterval(interval) }, [detectedAt]) return ( 72h-Frist: {remaining} ) } export function IncidentsTab({ incidents, setIncidents, showAdd, setShowAdd, onAdd, onStatusChange, onDelete, }: { incidents: Incident[] setIncidents: React.Dispatch> showAdd: boolean setShowAdd: (v: boolean) => void onAdd?: (incident: Partial) => Promise onStatusChange?: (id: string, status: IncidentStatus) => Promise onDelete?: (id: string) => Promise }) { const [newIncident, setNewIncident] = useState>({ title: '', description: '', severity: 'medium', affectedDataCategories: [], estimatedAffectedPersons: 0, measures: [], art34Required: false, art34Justification: '', }) async function addIncident() { if (!newIncident.title) return if (onAdd) { await onAdd(newIncident) } else { const incident: Incident = { id: `INC-${Date.now()}`, title: newIncident.title || '', description: newIncident.description || '', detectedAt: new Date().toISOString(), detectedBy: 'Admin', status: 'detected', severity: newIncident.severity as IncidentSeverity || 'medium', affectedDataCategories: newIncident.affectedDataCategories || [], estimatedAffectedPersons: newIncident.estimatedAffectedPersons || 0, measures: newIncident.measures || [], art34Required: newIncident.art34Required || false, art34Justification: newIncident.art34Justification || '', } setIncidents(prev => [incident, ...prev]) } setShowAdd(false) setNewIncident({ title: '', description: '', severity: 'medium', affectedDataCategories: [], estimatedAffectedPersons: 0, measures: [], art34Required: false, art34Justification: '', }) } async function updateStatus(id: string, status: IncidentStatus) { if (onStatusChange) { await onStatusChange(id, status) } else { setIncidents(prev => prev.map(inc => inc.id === id ? { ...inc, status, ...(status === 'reported' ? { reportedToAuthorityAt: new Date().toISOString() } : {}), ...(status === 'closed' ? { closedAt: new Date().toISOString(), closedBy: 'Admin' } : {}), } : inc )) } } return (

Incident-Register (Art. 33 Abs. 5)

Alle Datenpannen dokumentieren — auch nicht-meldepflichtige.

{/* Add Incident Form */} {showAdd && (

Neue Datenpanne erfassen

setNewIncident(prev => ({ ...prev, title: e.target.value }))} placeholder="Kurzbeschreibung der Datenpanne" className="w-full border rounded px-3 py-2 text-sm" />