Fix jwt-token

This commit is contained in:
2025-09-22 09:59:22 +02:00
parent 8b6aab6cf4
commit 90f2c8fefb

View File

@@ -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>