101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import { cn, formatRelativeTime } from '@/lib/utils'
|
|
|
|
export type AlertSeverity = 'CRITICAL' | 'WARNING' | 'INFO'
|
|
|
|
export type AlertStatus = 'ACTIVA' | 'RECONOCIDA' | 'RESUELTA'
|
|
|
|
export interface AlertCardData {
|
|
id: string
|
|
title: string
|
|
device: string
|
|
description: string
|
|
severity: AlertSeverity
|
|
timestamp: Date | string
|
|
status: AlertStatus
|
|
}
|
|
|
|
interface AlertCardProps {
|
|
alert: AlertCardData
|
|
onAcknowledge?: (id: string) => void
|
|
onResolve?: (id: string) => void
|
|
}
|
|
|
|
const severityStyles = {
|
|
CRITICAL: {
|
|
bar: 'bg-red-500',
|
|
badge: 'bg-red-500/20 text-red-400 border-red-500/40',
|
|
label: 'CRÍTICO',
|
|
},
|
|
WARNING: {
|
|
bar: 'bg-amber-500',
|
|
badge: 'bg-amber-500/20 text-amber-400 border-amber-500/40',
|
|
label: 'ADVERTENCIA',
|
|
},
|
|
INFO: {
|
|
bar: 'bg-blue-500',
|
|
badge: 'bg-blue-500/20 text-blue-400 border-blue-500/40',
|
|
label: 'INFO',
|
|
},
|
|
}
|
|
|
|
export default function AlertCard({ alert, onAcknowledge, onResolve }: AlertCardProps) {
|
|
const style = severityStyles[alert.severity]
|
|
const ts = typeof alert.timestamp === 'string' ? new Date(alert.timestamp) : alert.timestamp
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'rounded-xl border border-slate-700/60 bg-slate-800/50 shadow-sm',
|
|
'p-5 transition-all duration-200 hover:border-slate-600 hover:shadow-md',
|
|
'flex gap-4'
|
|
)}
|
|
>
|
|
<div className={cn('w-1 shrink-0 rounded-full', style.bar)} aria-hidden />
|
|
<div className="min-w-0 flex-1">
|
|
<h4 className="font-semibold text-slate-100 text-base leading-tight">
|
|
{alert.title}
|
|
</h4>
|
|
<p className="mt-1 text-sm text-slate-400">{alert.device}</p>
|
|
<p className="mt-2 text-sm text-slate-500 line-clamp-2">{alert.description}</p>
|
|
<p className="mt-2 text-xs text-slate-600">{formatRelativeTime(ts)}</p>
|
|
</div>
|
|
|
|
<div className="flex shrink-0 flex-col items-end justify-between gap-3">
|
|
<span
|
|
className={cn(
|
|
'rounded-md border px-2 py-0.5 text-xs font-medium',
|
|
style.badge
|
|
)}
|
|
>
|
|
{style.label}
|
|
</span>
|
|
{alert.status === 'ACTIVA' && (
|
|
<div className="flex gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => onAcknowledge?.(alert.id)}
|
|
className="btn btn-secondary btn-sm"
|
|
>
|
|
Marcar leído
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => onResolve?.(alert.id)}
|
|
className="btn btn-ghost btn-sm"
|
|
>
|
|
Descartar
|
|
</button>
|
|
</div>
|
|
)}
|
|
{alert.status !== 'ACTIVA' && (
|
|
<span className="text-xs text-slate-500">
|
|
{alert.status === 'RECONOCIDA' ? 'Leída' : 'Resuelta'}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|