Fix jwt-token
This commit is contained in:
@@ -49,6 +49,33 @@ const Alerts = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Group alerts by alert_event_id to show related alerts together
|
||||||
|
const groupAlertsByEvent = (logs) => {
|
||||||
|
const grouped = {};
|
||||||
|
const ungrouped = [];
|
||||||
|
|
||||||
|
logs.forEach(log => {
|
||||||
|
if (log.alert_event_id) {
|
||||||
|
if (!grouped[log.alert_event_id]) {
|
||||||
|
grouped[log.alert_event_id] = [];
|
||||||
|
}
|
||||||
|
grouped[log.alert_event_id].push(log);
|
||||||
|
} else {
|
||||||
|
ungrouped.push(log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert grouped object to array of arrays, sorted by most recent alert in each group
|
||||||
|
const groupedArrays = Object.values(grouped).map(group =>
|
||||||
|
group.sort((a, b) => new Date(b.sent_at) - new Date(a.sent_at))
|
||||||
|
).sort((a, b) => new Date(b[0].sent_at) - new Date(a[0].sent_at));
|
||||||
|
|
||||||
|
// Add ungrouped alerts as individual groups
|
||||||
|
ungrouped.forEach(log => groupedArrays.push([log]));
|
||||||
|
|
||||||
|
return groupedArrays;
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteRule = async (ruleId) => {
|
const handleDeleteRule = async (ruleId) => {
|
||||||
if (window.confirm('Are you sure you want to delete this alert rule?')) {
|
if (window.confirm('Are you sure you want to delete this alert rule?')) {
|
||||||
try {
|
try {
|
||||||
@@ -356,72 +383,183 @@ const Alerts = () => {
|
|||||||
<th>{t('alerts.detection')}</th>
|
<th>{t('alerts.detection')}</th>
|
||||||
<th>{t('alerts.message')}</th>
|
<th>{t('alerts.message')}</th>
|
||||||
<th>{t('alerts.sentAt')}</th>
|
<th>{t('alerts.sentAt')}</th>
|
||||||
|
<th>Event ID</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{(alertLogs || []).map((log) => (
|
{groupAlertsByEvent(alertLogs || []).map((alertGroup, groupIndex) => {
|
||||||
<tr key={log.id} className="hover:bg-gray-50">
|
// Display the primary alert (first in group)
|
||||||
<td>
|
const primaryAlert = alertGroup[0];
|
||||||
<div className="flex items-center space-x-2">
|
const relatedAlerts = alertGroup.slice(1);
|
||||||
{getStatusIcon(log.status)}
|
|
||||||
<span className="text-sm text-gray-900 capitalize">
|
return (
|
||||||
{log.status}
|
<React.Fragment key={`group-${groupIndex}`}>
|
||||||
</span>
|
<tr className="hover:bg-gray-50 border-b-2 border-gray-200">
|
||||||
</div>
|
<td>
|
||||||
</td>
|
<div className="flex items-center space-x-2">
|
||||||
<td>
|
{getStatusIcon(primaryAlert.status)}
|
||||||
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">
|
<span className="text-sm text-gray-900 capitalize">
|
||||||
{log.alert_type}
|
{primaryAlert.status}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
{relatedAlerts.length > 0 && (
|
||||||
<td>
|
<span className="ml-2 px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">
|
||||||
<div className="text-sm text-gray-900">
|
+{relatedAlerts.length} more
|
||||||
{log.recipient}
|
</span>
|
||||||
</div>
|
)}
|
||||||
</td>
|
</div>
|
||||||
<td>
|
</td>
|
||||||
<div className="text-sm text-gray-900">
|
<td>
|
||||||
{log.rule?.name || t('alerts.unknownRule')}
|
<div className="flex flex-wrap gap-1">
|
||||||
</div>
|
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">
|
||||||
</td>
|
{primaryAlert.alert_type}
|
||||||
<td>
|
</span>
|
||||||
<div className="text-sm text-gray-900">
|
{relatedAlerts.map((alert, idx) => (
|
||||||
{log.detection?.drone_id ? (
|
<span key={idx} className="px-2 py-1 bg-gray-100 text-gray-600 rounded text-xs">
|
||||||
<span className="px-2 py-1 bg-purple-100 text-purple-800 rounded text-xs font-mono">
|
{alert.alert_type}
|
||||||
{log.detection.drone_id}
|
</span>
|
||||||
</span>
|
))}
|
||||||
) : (
|
</div>
|
||||||
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
</td>
|
||||||
)}
|
<td>
|
||||||
</div>
|
<div className="text-sm text-gray-900">
|
||||||
</td>
|
{primaryAlert.recipient}
|
||||||
<td>
|
{relatedAlerts.length > 0 && relatedAlerts.some(a => a.recipient !== primaryAlert.recipient) && (
|
||||||
{log.detection_id ? (
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
<button
|
+{relatedAlerts.filter(a => a.recipient !== primaryAlert.recipient).length} others
|
||||||
onClick={() => handleViewDetection(log.detection_id)}
|
</div>
|
||||||
className="text-primary-600 hover:text-primary-900 text-sm font-medium"
|
)}
|
||||||
>
|
</div>
|
||||||
{t('alerts.viewDetails')}
|
</td>
|
||||||
</button>
|
<td>
|
||||||
) : (
|
<div className="text-sm text-gray-900">
|
||||||
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
{primaryAlert.rule?.name || t('alerts.unknownRule')}
|
||||||
)}
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div className="text-sm text-gray-900 max-w-xs truncate">
|
<div className="text-sm text-gray-900">
|
||||||
{log.message}
|
{primaryAlert.detection?.drone_id ? (
|
||||||
</div>
|
<span className="px-2 py-1 bg-purple-100 text-purple-800 rounded text-xs font-mono">
|
||||||
</td>
|
{primaryAlert.detection.drone_id}
|
||||||
<td>
|
</span>
|
||||||
<div className="text-sm text-gray-900">
|
) : (
|
||||||
{log.sent_at
|
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
||||||
? format(new Date(log.sent_at), 'MMM dd, HH:mm')
|
)}
|
||||||
: 'Not sent'
|
</div>
|
||||||
}
|
</td>
|
||||||
</div>
|
<td>
|
||||||
</td>
|
{primaryAlert.detection_id ? (
|
||||||
</tr>
|
<button
|
||||||
))}
|
onClick={() => handleViewDetection(primaryAlert.detection_id)}
|
||||||
|
className="text-blue-600 hover:text-blue-800 text-sm underline"
|
||||||
|
>
|
||||||
|
{t('alerts.viewDetails')}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-900 max-w-xs overflow-hidden">
|
||||||
|
<div className="truncate" title={primaryAlert.message}>
|
||||||
|
{primaryAlert.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-900">
|
||||||
|
{format(new Date(primaryAlert.sent_at), 'MMM dd, HH:mm')}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-xs text-gray-500 font-mono">
|
||||||
|
{primaryAlert.alert_event_id ? (
|
||||||
|
<span title={primaryAlert.alert_event_id}>
|
||||||
|
{primaryAlert.alert_event_id.substring(0, 8)}...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-400">-</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Show related alerts as sub-rows if any */}
|
||||||
|
{relatedAlerts.map((alert, alertIndex) => (
|
||||||
|
<tr key={`related-${groupIndex}-${alertIndex}`} className="bg-gray-50 border-l-4 border-blue-200">
|
||||||
|
<td className="pl-8">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
{getStatusIcon(alert.status)}
|
||||||
|
<span className="text-sm text-gray-700 capitalize">
|
||||||
|
{alert.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span className="px-2 py-1 bg-gray-100 text-gray-600 rounded text-xs">
|
||||||
|
{alert.alert_type}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-700">
|
||||||
|
{alert.recipient}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-700">
|
||||||
|
{alert.rule?.name || t('alerts.unknownRule')}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-700">
|
||||||
|
{alert.detection?.drone_id ? (
|
||||||
|
<span className="px-2 py-1 bg-purple-50 text-purple-600 rounded text-xs font-mono">
|
||||||
|
{alert.detection.drone_id}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{alert.detection_id ? (
|
||||||
|
<button
|
||||||
|
onClick={() => handleViewDetection(alert.detection_id)}
|
||||||
|
className="text-blue-500 hover:text-blue-700 text-sm underline"
|
||||||
|
>
|
||||||
|
{t('alerts.viewDetails')}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-400 text-sm">{t('alerts.na')}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-700 max-w-xs overflow-hidden">
|
||||||
|
<div className="truncate" title={alert.message}>
|
||||||
|
{alert.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-sm text-gray-700">
|
||||||
|
{format(new Date(alert.sent_at), 'MMM dd, HH:mm')}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="text-xs text-gray-400 font-mono">
|
||||||
|
{alert.alert_event_id ? (
|
||||||
|
<span title={alert.alert_event_id}>
|
||||||
|
{alert.alert_event_id.substring(0, 8)}...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user