Agent-completed splits committed after agents hit rate limits before committing their work. All 4 pages now under 500 LOC: - consent-management: 1303 -> 193 LOC (+ 7 _components, _hooks, _data, _types) - control-library: 1210 -> 298 LOC (+ _components, _types) - incidents: 1150 -> 373 LOC (+ _components) - training: 1127 -> 366 LOC (+ _components) Verification: next build clean (142 pages generated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import {
|
|
Incident,
|
|
getHoursUntil72hDeadline,
|
|
is72hDeadlineExpired
|
|
} from '@/lib/sdk/incidents/types'
|
|
|
|
/**
|
|
* 72h-Countdown-Anzeige mit visueller Farbkodierung
|
|
* Gruen > 48h, Gelb > 24h, Orange > 12h, Rot < 12h oder abgelaufen
|
|
*/
|
|
export function CountdownTimer({ incident }: { incident: Incident }) {
|
|
const hoursRemaining = getHoursUntil72hDeadline(incident.detectedAt)
|
|
const expired = is72hDeadlineExpired(incident.detectedAt)
|
|
|
|
// Nicht relevant fuer abgeschlossene Vorfaelle
|
|
if (incident.status === 'closed') return null
|
|
|
|
// Bereits gemeldet
|
|
if (incident.authorityNotification && (incident.authorityNotification.status === 'submitted' || incident.authorityNotification.status === 'acknowledged')) {
|
|
return (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium bg-green-100 text-green-700 rounded-full">
|
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
Gemeldet
|
|
</span>
|
|
)
|
|
}
|
|
|
|
// Keine Meldepflicht festgestellt
|
|
if (incident.riskAssessment && !incident.riskAssessment.notificationRequired) {
|
|
return (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded-full">
|
|
Keine Meldepflicht
|
|
</span>
|
|
)
|
|
}
|
|
|
|
// Abgelaufen
|
|
if (expired) {
|
|
const overdueHours = Math.abs(hoursRemaining)
|
|
return (
|
|
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-bold bg-red-100 text-red-700 rounded-full animate-pulse">
|
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
{overdueHours.toFixed(0)}h ueberfaellig
|
|
</span>
|
|
)
|
|
}
|
|
|
|
// Farbkodierung: gruen > 48h, gelb > 24h, orange > 12h, rot < 12h
|
|
let colorClass: string
|
|
if (hoursRemaining > 48) {
|
|
colorClass = 'bg-green-100 text-green-700'
|
|
} else if (hoursRemaining > 24) {
|
|
colorClass = 'bg-yellow-100 text-yellow-700'
|
|
} else if (hoursRemaining > 12) {
|
|
colorClass = 'bg-orange-100 text-orange-700'
|
|
} else {
|
|
colorClass = 'bg-red-100 text-red-700'
|
|
}
|
|
|
|
return (
|
|
<span className={`inline-flex items-center gap-1 px-2 py-1 text-xs font-bold rounded-full ${colorClass}`}>
|
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
{hoursRemaining.toFixed(0)}h verbleibend
|
|
</span>
|
|
)
|
|
}
|