diff --git a/client/src/components/AlertModals.jsx b/client/src/components/AlertModals.jsx
new file mode 100644
index 0000000..1998cdb
--- /dev/null
+++ b/client/src/components/AlertModals.jsx
@@ -0,0 +1,407 @@
+import React, { useState, useEffect } from 'react';
+import api from '../services/api';
+import { format } from 'date-fns';
+import { XMarkIcon } from '@heroicons/react/24/outline';
+
+// Edit Alert Rule Modal
+export const EditAlertModal = ({ rule, onClose, onSuccess }) => {
+ const [formData, setFormData] = useState({
+ name: '',
+ description: '',
+ priority: 'medium',
+ min_detections: 1,
+ time_window: 300,
+ cooldown_period: 600,
+ alert_channels: ['sms'],
+ min_threat_level: '',
+ sms_phone_number: '',
+ webhook_url: ''
+ });
+ const [saving, setSaving] = useState(false);
+
+ useEffect(() => {
+ if (rule) {
+ setFormData({
+ name: rule.name || '',
+ description: rule.description || '',
+ priority: rule.priority || 'medium',
+ min_detections: rule.min_detections || 1,
+ time_window: rule.time_window || 300,
+ cooldown_period: rule.cooldown_period || 600,
+ alert_channels: rule.alert_channels || ['sms'],
+ min_threat_level: rule.min_threat_level || '',
+ sms_phone_number: rule.sms_phone_number || '',
+ webhook_url: rule.webhook_url || ''
+ });
+ }
+ }, [rule]);
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({
+ ...prev,
+ [name]: value
+ }));
+ };
+
+ const handleChannelChange = (channel, checked) => {
+ setFormData(prev => ({
+ ...prev,
+ alert_channels: checked
+ ? [...prev.alert_channels, channel]
+ : prev.alert_channels.filter(c => c !== channel)
+ }));
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setSaving(true);
+
+ try {
+ await api.put(`/alerts/rules/${rule.id}`, formData);
+ onSuccess();
+ onClose();
+ } catch (error) {
+ console.error('Error updating alert rule:', error);
+ alert('Failed to update alert rule');
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ return (
+
+
+
+
Edit Alert Rule
+
+
+
+
+
+
+ );
+};
+
+// Detection Details Modal
+export const DetectionDetailsModal = ({ detection, onClose }) => {
+ if (!detection) return null;
+
+ const getPriorityColor = (level) => {
+ switch (level?.toLowerCase()) {
+ 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';
+ }
+ };
+
+ return (
+
+
+
+
Detection Details
+
+
+
+
+ {/* Basic Information */}
+
+
+
Basic Information
+
+
+ ID:
+ {detection.id}
+
+
+ Detected At:
+ {format(new Date(detection.detected_at), 'MMM dd, yyyy HH:mm:ss')}
+
+
+ Device:
+ {detection.device?.name || `Device ${detection.device_id}`}
+
+
+ Location:
+ {detection.device?.location || 'Unknown'}
+
+
+
+
+
+
Signal Information
+
+
+ RSSI:
+ {detection.rssi} dBm
+
+
+ Frequency:
+ {detection.frequency ? `${detection.frequency} MHz` : 'N/A'}
+
+
+ Drone Type:
+ {detection.drone_type || 'Unknown'}
+
+ {detection.estimated_distance && (
+
+ Est. Distance:
+ {detection.estimated_distance}m
+
+ )}
+
+
+
+
+ {/* Threat Assessment */}
+ {detection.threat_assessment && (
+
+
Threat Assessment
+
+
+ Threat Level:
+
+ {detection.threat_assessment.level?.toUpperCase()}
+
+
+
+
+ Priority:
+ {detection.threat_assessment.priority}
+
+
+ Immediate Action:
+
+ {detection.threat_assessment.requiresImmediateAction ? 'Required' : 'Not Required'}
+
+
+ {detection.threat_assessment.description && (
+
+
Description:
+
{detection.threat_assessment.description}
+
+ )}
+
+
+
+ )}
+
+ {/* Raw Data */}
+ {detection.raw_data && (
+
+
Raw Data
+
+
{JSON.stringify(detection.raw_data, null, 2)}
+
+
+ )}
+
+
+
+
+
+
+
+ );
+};
diff --git a/client/src/pages/Alerts.jsx b/client/src/pages/Alerts.jsx
index 6eb9c21..7d6cfed 100644
--- a/client/src/pages/Alerts.jsx
+++ b/client/src/pages/Alerts.jsx
@@ -8,6 +8,7 @@ import {
XCircleIcon,
ExclamationTriangleIcon
} from '@heroicons/react/24/outline';
+import { EditAlertModal, DetectionDetailsModal } from '../components/AlertModals';
const Alerts = () => {
const [alertRules, setAlertRules] = useState([]);
@@ -388,6 +389,27 @@ const Alerts = () => {
}}
/>
)}
+
+ {showEditModal && editingRule && (
+ {
+ setShowEditModal(false);
+ setEditingRule(null);
+ }}
+ onSuccess={fetchAlertData}
+ />
+ )}
+
+ {showDetectionModal && selectedDetection && (
+ {
+ setShowDetectionModal(false);
+ setSelectedDetection(null);
+ }}
+ />
+ )}
);
};