import React, { useState, useEffect } from 'react'; import api from '../services/api'; import { format } from 'date-fns'; import { PlusIcon, BellIcon, CheckCircleIcon, XCircleIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'; const Alerts = () => { const [alertRules, setAlertRules] = useState([]); const [alertLogs, setAlertLogs] = useState([]); const [alertStats, setAlertStats] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState('rules'); const [showCreateModal, setShowCreateModal] = useState(false); useEffect(() => { fetchAlertData(); }, []); const fetchAlertData = async () => { try { const [rulesRes, logsRes, statsRes] = await Promise.all([ api.get('/alerts/rules'), api.get('/alerts/logs?limit=50'), api.get('/alerts/stats?hours=24') ]); setAlertRules(rulesRes.data.data); setAlertLogs(logsRes.data.data); setAlertStats(statsRes.data.data); } catch (error) { console.error('Error fetching alert data:', error); } finally { setLoading(false); } }; const handleDeleteRule = async (ruleId) => { if (window.confirm('Are you sure you want to delete this alert rule?')) { try { await api.delete(`/alerts/rules/${ruleId}`); fetchAlertData(); } catch (error) { console.error('Error deleting alert rule:', error); } } }; const getStatusIcon = (status) => { switch (status) { case 'sent': return ; case 'failed': return ; case 'pending': return ; default: return ; } }; const getPriorityColor = (priority) => { switch (priority) { case 'critical': return 'bg-red-100 text-red-800'; case 'high': return 'bg-orange-100 text-orange-800'; case 'medium': return 'bg-yellow-100 text-yellow-800'; case 'low': return 'bg-green-100 text-green-800'; default: return 'bg-gray-100 text-gray-800'; } }; if (loading) { return (
); } return (

Alert Management

Configure and monitor alert rules for drone detections

{/* Alert Stats */} {alertStats && (
{alertStats.total_alerts}
Total Alerts (24h)
{alertStats.sent_alerts}
Sent Successfully
{alertStats.failed_alerts}
Failed
{alertStats.pending_alerts}
Pending
)} {/* Tabs */}
{/* Alert Rules Tab */} {activeTab === 'rules' && (
{alertRules.length === 0 ? (

No alert rules

Get started by creating your first alert rule.

) : (
{alertRules.map((rule) => ( ))}
Name Priority Channels Conditions Status Created Actions
{rule.name}
{rule.description && (
{rule.description}
)}
{rule.priority}
{rule.alert_channels.map((channel, index) => ( {channel} ))}
{rule.min_detections > 1 && (
Min detections: {rule.min_detections}
)} {rule.time_window && (
Time window: {rule.time_window}s
)} {rule.cooldown_period && (
Cooldown: {rule.cooldown_period}s
)}
{rule.is_active ? 'Active' : 'Inactive'}
{format(new Date(rule.created_at), 'MMM dd, yyyy')}
)}
)} {/* Alert Logs Tab */} {activeTab === 'logs' && (
{alertLogs.length === 0 ? (

No alert logs

Alert logs will appear here when alerts are triggered.

) : (
{alertLogs.map((log) => ( ))}
Status Type Recipient Rule Message Sent At
{getStatusIcon(log.status)} {log.status}
{log.alert_type}
{log.recipient}
{log.rule?.name || 'Unknown Rule'}
{log.message}
{log.sent_at ? format(new Date(log.sent_at), 'MMM dd, HH:mm') : 'Not sent' }
)}
)} {/* Create Alert Rule Modal */} {showCreateModal && ( setShowCreateModal(false)} onSave={() => { setShowCreateModal(false); fetchAlertData(); }} /> )}
); }; const CreateAlertRuleModal = ({ onClose, onSave }) => { const [formData, setFormData] = useState({ name: '', description: '', priority: 'medium', alert_channels: ['sms'], min_detections: 1, time_window: 300, cooldown_period: 600, device_ids: null, drone_types: null, min_rssi: '', max_rssi: '', frequency_ranges: [] }); const [saving, setSaving] = useState(false); const handleSubmit = async (e) => { e.preventDefault(); setSaving(true); try { const payload = { ...formData }; // Clean up empty values if (!payload.min_rssi) delete payload.min_rssi; if (!payload.max_rssi) delete payload.max_rssi; if (!payload.device_ids || payload.device_ids.length === 0) payload.device_ids = null; if (!payload.drone_types || payload.drone_types.length === 0) payload.drone_types = null; if (!payload.frequency_ranges || payload.frequency_ranges.length === 0) payload.frequency_ranges = null; await api.post('/alerts/rules', payload); onSave(); } catch (error) { console.error('Error creating alert rule:', error); } finally { setSaving(false); } }; const handleChange = (e) => { const { name, value, type } = e.target; setFormData(prev => ({ ...prev, [name]: type === 'number' ? parseInt(value) || 0 : value })); }; const handleChannelChange = (channel, checked) => { setFormData(prev => ({ ...prev, alert_channels: checked ? [...prev.alert_channels, channel] : prev.alert_channels.filter(c => c !== channel) })); }; return (

Create Alert Rule