From 19599047c9a4db11bb9bb8529ab66872e9026b81 Mon Sep 17 00:00:00 2001 From: Alexander Borg Date: Mon, 22 Sep 2025 10:42:25 +0200 Subject: [PATCH] Fix jwt-token --- client/src/hooks/useDroneTypes.js | 181 ++++++++++++++++++++++++++++++ client/src/pages/Alerts.jsx | 65 +++-------- 2 files changed, 197 insertions(+), 49 deletions(-) create mode 100644 client/src/hooks/useDroneTypes.js diff --git a/client/src/hooks/useDroneTypes.js b/client/src/hooks/useDroneTypes.js new file mode 100644 index 0000000..530af0a --- /dev/null +++ b/client/src/hooks/useDroneTypes.js @@ -0,0 +1,181 @@ +import { useState, useEffect, useMemo } from 'react'; + +/** + * Custom hook for fetching and managing drone types from the API + * Provides caching, error handling, and convenient access to drone type data + */ +export const useDroneTypes = () => { + const [droneTypes, setDroneTypes] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Fetch drone types from API + useEffect(() => { + const fetchDroneTypes = async () => { + try { + setLoading(true); + setError(null); + + const response = await fetch('/api/drone-types'); + if (!response.ok) { + throw new Error(`Failed to fetch drone types: ${response.statusText}`); + } + + const result = await response.json(); + if (result.success && result.data) { + setDroneTypes(result.data); + } else { + throw new Error('Invalid response format from drone types API'); + } + } catch (err) { + console.error('Error fetching drone types:', err); + setError(err.message); + // Set fallback data on error + setDroneTypes(getFallbackDroneTypes()); + } finally { + setLoading(false); + } + }; + + fetchDroneTypes(); + }, []); + + // Create a mapping object for quick lookups by ID + const droneTypeMap = useMemo(() => { + const map = {}; + droneTypes.forEach(type => { + map[type.id] = type; + }); + return map; + }, [droneTypes]); + + // Get drone type info by ID with fallback + const getDroneTypeInfo = (droneTypeId) => { + const typeInfo = droneTypeMap[droneTypeId]; + + if (typeInfo) { + return { + ...typeInfo, + // Add visual styling based on threat level + ...getVisualStyling(typeInfo) + }; + } + + // Fallback for unknown types + return { + id: droneTypeId, + name: 'Unknown', + category: 'Unknown', + threat_level: 'medium', + description: `Unknown drone type ${droneTypeId}`, + ...getVisualStyling({ threat_level: 'medium' }) + }; + }; + + // Get drone types by category + const getDroneTypesByCategory = (category) => { + return droneTypes.filter(type => type.category === category); + }; + + // Get drone types by threat level + const getDroneTypesByThreatLevel = (threatLevel) => { + return droneTypes.filter(type => type.threat_level === threatLevel); + }; + + // Get all categories + const getCategories = () => { + const categories = new Set(droneTypes.map(type => type.category)); + return Array.from(categories); + }; + + // Get all threat levels + const getThreatLevels = () => { + const levels = new Set(droneTypes.map(type => type.threat_level)); + return Array.from(levels); + }; + + return { + droneTypes, + droneTypeMap, + loading, + error, + getDroneTypeInfo, + getDroneTypesByCategory, + getDroneTypesByThreatLevel, + getCategories, + getThreatLevels + }; +}; + +/** + * Get visual styling based on threat level and category + */ +const getVisualStyling = (typeInfo) => { + const isMilitary = typeInfo.category?.includes('Military') || + typeInfo.threat_level === 'critical'; + + const isWarning = isMilitary || typeInfo.threat_level === 'high'; + + // Base styling + let styling = { + warning: isWarning, + icon: isMilitary ? 'âš ī¸' : 'đŸ›Šī¸' + }; + + // Color scheme based on threat level + switch (typeInfo.threat_level) { + case 'critical': + styling = { + ...styling, + color: 'red', + bgColor: 'bg-red-100', + textColor: 'text-red-800', + borderColor: 'border-red-300' + }; + break; + case 'high': + styling = { + ...styling, + color: 'orange', + bgColor: 'bg-orange-100', + textColor: 'text-orange-800', + borderColor: 'border-orange-300' + }; + break; + case 'medium': + styling = { + ...styling, + color: 'yellow', + bgColor: 'bg-yellow-100', + textColor: 'text-yellow-800', + borderColor: 'border-yellow-300' + }; + break; + case 'low': + default: + styling = { + ...styling, + color: 'gray', + bgColor: 'bg-gray-100', + textColor: 'text-gray-800', + borderColor: 'border-gray-300' + }; + break; + } + + return styling; +}; + +/** + * Fallback drone types for when API is unavailable + * This ensures the app continues working even if the API is down + */ +const getFallbackDroneTypes = () => { + return [ + { id: 0, name: 'None', category: 'Unknown', threat_level: 'low' }, + { id: 1, name: 'Unknown', category: 'Unknown', threat_level: 'medium' }, + { id: 2, name: 'Orlan', category: 'Military/Reconnaissance', threat_level: 'critical' }, + { id: 3, name: 'Zala', category: 'Military/Surveillance', threat_level: 'critical' }, + { id: 13, name: 'DJI', category: 'Commercial/Professional', threat_level: 'low' } + ]; +}; \ No newline at end of file diff --git a/client/src/pages/Alerts.jsx b/client/src/pages/Alerts.jsx index 895c8d8..61f7d01 100644 --- a/client/src/pages/Alerts.jsx +++ b/client/src/pages/Alerts.jsx @@ -12,8 +12,12 @@ import { ChevronRightIcon } from '@heroicons/react/24/outline'; import { EditAlertModal, DetectionDetailsModal } from '../components/AlertModals'; +import { useDroneTypes } from '../hooks/useDroneTypes'; const Alerts = () => { + // Drone types hook for dynamic drone type data + const { getDroneTypeInfo: getDroneTypeInfoFromAPI, loading: droneTypesLoading } = useDroneTypes(); + const [alertRules, setAlertRules] = useState([]); const [alertLogs, setAlertLogs] = useState([]); const [alertStats, setAlertStats] = useState(null); @@ -52,6 +56,9 @@ const Alerts = () => { } }; + // Show loading if either alerts or drone types are loading + const isLoading = loading || droneTypesLoading; + // Group alerts by alert_event_id to show related alerts together const groupAlertsByEvent = (logs) => { const grouped = {}; @@ -89,35 +96,14 @@ const Alerts = () => { setExpandedGroups(newExpanded); }; - // Get drone type information with visual styling + // Get drone type information with visual styling (now using dynamic API data) const getDroneTypeInfo = (detection) => { if (!detection || detection.drone_type === undefined) { return { name: 'Unknown', color: 'gray', bgColor: 'bg-gray-100', textColor: 'text-gray-600', icon: '🔍' }; } - // Map drone types based on the backend droneTypes utility - const droneTypeMap = { - 0: { name: 'Unknown', color: 'gray', bgColor: 'bg-gray-100', textColor: 'text-gray-600', icon: '🔍' }, - 1: { name: 'Generic', color: 'blue', bgColor: 'bg-blue-100', textColor: 'text-blue-800', icon: '🚁' }, - 2: { name: 'Orlan', color: 'red', bgColor: 'bg-red-100', textColor: 'text-red-800', icon: 'âš ī¸', warning: 'âš ī¸ MILITARY' }, - 3: { name: 'Zala', color: 'orange', bgColor: 'bg-orange-100', textColor: 'text-orange-800', icon: 'đŸ”ļ', warning: 'âš ī¸ MILITARY' }, - 4: { name: 'Forpost', color: 'purple', bgColor: 'bg-purple-100', textColor: 'text-purple-800', icon: 'đŸŸŖ', warning: 'âš ī¸ MILITARY' }, - 5: { name: 'Inokhodets', color: 'indigo', bgColor: 'bg-indigo-100', textColor: 'text-indigo-800', icon: '🔷', warning: 'âš ī¸ MILITARY' }, - 6: { name: 'Lancet', color: 'red', bgColor: 'bg-red-200', textColor: 'text-red-900', icon: 'đŸ’Ĩ', warning: 'âš ī¸ MILITARY' }, - 7: { name: 'Shahed', color: 'yellow', bgColor: 'bg-yellow-100', textColor: 'text-yellow-800', icon: '⚡', warning: 'âš ī¸ MILITARY' }, - 8: { name: 'Geran', color: 'amber', bgColor: 'bg-amber-100', textColor: 'text-amber-800', icon: '🟨', warning: 'âš ī¸ MILITARY' }, - 9: { name: 'Kub', color: 'green', bgColor: 'bg-green-100', textColor: 'text-green-800', icon: 'đŸŸĸ', warning: 'âš ī¸ MILITARY' }, - 10: { name: 'X-UAV', color: 'teal', bgColor: 'bg-teal-100', textColor: 'text-teal-800', icon: '🔷' }, - 11: { name: 'SuperCam', color: 'cyan', bgColor: 'bg-cyan-100', textColor: 'text-cyan-800', icon: '📷' }, - 12: { name: 'Eleron', color: 'lime', bgColor: 'bg-lime-100', textColor: 'text-lime-800', icon: '🟩' }, - 13: { name: 'DJI', color: 'blue', bgColor: 'bg-blue-200', textColor: 'text-blue-900', icon: '📱' }, - 14: { name: 'Autel', color: 'violet', bgColor: 'bg-violet-100', textColor: 'text-violet-800', icon: 'đŸŸĒ' }, - 15: { name: 'Parrot', color: 'emerald', bgColor: 'bg-emerald-100', textColor: 'text-emerald-800', icon: 'đŸĻœ' }, - 16: { name: 'Skydio', color: 'sky', bgColor: 'bg-sky-100', textColor: 'text-sky-800', icon: 'â˜ī¸' }, - 17: { name: 'CryptoOrlan', color: 'red', bgColor: 'bg-red-300', textColor: 'text-red-900', icon: '🔴', warning: 'âš ī¸ MILITARY' } - }; - - return droneTypeMap[detection.drone_type] || droneTypeMap[0]; + // Use the dynamic API data from the hook + return getDroneTypeInfoFromAPI(detection.drone_type); }; const handleDeleteRule = async (ruleId) => { @@ -174,11 +160,13 @@ const Alerts = () => { } }; - if (loading) { + if (isLoading) { return (
- {t('alerts.loading')} + + {droneTypesLoading ? 'Loading drone types...' : t('alerts.loading')} +
); } @@ -338,29 +326,8 @@ const Alerts = () => {
{t('alerts.droneTypes')}:
{rule.drone_types.map((typeId, index) => { - // Use the same comprehensive drone type mapping as getDroneTypeInfo - const droneTypeMap = { - 0: { name: 'Unknown', color: 'gray' }, - 1: { name: 'Generic', color: 'blue' }, - 2: { name: 'Orlan', color: 'red', warning: true }, - 3: { name: 'Zala', color: 'orange', warning: true }, - 4: { name: 'Forpost', color: 'purple', warning: true }, - 5: { name: 'Inokhodets', color: 'indigo', warning: true }, - 6: { name: 'Lancet', color: 'red', warning: true }, - 7: { name: 'Shahed', color: 'yellow', warning: true }, - 8: { name: 'Geran', color: 'amber', warning: true }, - 9: { name: 'Kub', color: 'green', warning: true }, - 10: { name: 'X-UAV', color: 'teal' }, - 11: { name: 'SuperCam', color: 'cyan' }, - 12: { name: 'Eleron', color: 'lime' }, - 13: { name: 'DJI', color: 'blue' }, - 14: { name: 'Autel', color: 'violet' }, - 15: { name: 'Parrot', color: 'emerald' }, - 16: { name: 'Skydio', color: 'sky' }, - 17: { name: 'CryptoOrlan', color: 'red', warning: true } - }; - - const droneInfo = droneTypeMap[typeId] || droneTypeMap[0]; + // Use the dynamic API data instead of hardcoded mapping + const droneInfo = getDroneTypeInfoFromAPI(typeId); return (