Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14x 14x 14x 14x 12x 14x 1x 14x 1x 14x 14x 14x 1x 1x 1x 1x 27x 27x 27x 27x 27x 27x 27x 27x 1x 1x 1x 1x 1x 15x 15x 2x 2x 2x 2x 2x 2x 2x 2x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 12x 12x 12x 14x 1x 1x 1x 2x 1x 14x 14x 14x 14x 13x 13x 13x 13x 13x 13x 1x 1x | 'use client';
import { cn } from '@/lib/core';
import { StatusIndicator } from '../StatusIndicator';
import type { PerformanceAlert, PerformanceAlertConfig } from '@prisma/client';
export interface AlertHistoryTableProps {
/** List of alerts with their configs */
alerts: (PerformanceAlert & { config?: PerformanceAlertConfig })[];
/** Additional CSS classes */
className?: string;
}
/**
* Get metric unit for display
*/
function getMetricUnit(metricType: string): string {
switch (metricType) {
case 'response_time':
case 'p95_latency':
return 'ms';
case 'error_rate':
return '%';
case 'throughput':
return ' req/min';
default:
return '';
}
}
/**
* Format date for display
*/
function formatDate(date: Date): string {
return new Date(date).toLocaleString('en-US', {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
});
}
/**
* AlertHistoryTable displays a table of past performance alerts
* with their details and resolution status.
*/
export function AlertHistoryTable({ alerts, className }: AlertHistoryTableProps) {
if (alerts.length === 0) {
return (
<div className={cn('bg-gray-50 dark:bg-gray-800/50 rounded-lg p-6 text-center', className)}>
<p className="text-sm text-gray-500 dark:text-gray-400">
No alert history to display.
</p>
</div>
);
}
return (
<div className={cn('overflow-x-auto', className)}>
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-800">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Alert
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Value
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Threshold
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Triggered
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Resolved
</th>
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
{alerts.map((alert) => {
const unit = getMetricUnit(alert.metricType);
return (
<tr key={alert.id} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
<td className="px-4 py-3 whitespace-nowrap">
<StatusIndicator
status={alert.status as 'active' | 'acknowledged' | 'resolved'}
showLabel
size="sm"
/>
</td>
<td className="px-4 py-3">
<div className="flex items-center gap-2">
<span
className={cn(
'inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium',
alert.severity === 'critical'
? 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400'
: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400'
)}
>
{alert.severity}
</span>
<span className="text-sm font-medium text-gray-900 dark:text-white">
{alert.config?.name || 'Unknown Config'}
</span>
</div>
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
{alert.message}
</p>
</td>
<td className="px-4 py-3 whitespace-nowrap">
<span className="text-sm font-mono text-gray-900 dark:text-white">
{alert.value.toFixed(2)}{unit}
</span>
</td>
<td className="px-4 py-3 whitespace-nowrap">
<span className="text-sm font-mono text-gray-500 dark:text-gray-400">
{alert.threshold.toFixed(2)}{unit}
</span>
</td>
<td className="px-4 py-3 whitespace-nowrap">
<span className="text-sm text-gray-500 dark:text-gray-400">
{formatDate(alert.triggeredAt)}
</span>
</td>
<td className="px-4 py-3 whitespace-nowrap">
{alert.resolvedAt ? (
<span className="text-sm text-gray-500 dark:text-gray-400">
{formatDate(alert.resolvedAt)}
</span>
) : alert.acknowledgedAt ? (
<span className="text-sm text-blue-500 dark:text-blue-400">
Acknowledged {formatDate(alert.acknowledgedAt)}
</span>
) : (
<span className="text-sm text-gray-400 dark:text-gray-500">-</span>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
export default AlertHistoryTable;
|