Fix jwt-token
This commit is contained in:
@@ -148,7 +148,7 @@ const MapView = () => {
|
|||||||
10: "DJI Mavic",
|
10: "DJI Mavic",
|
||||||
11: "DJI Phantom",
|
11: "DJI Phantom",
|
||||||
20: "DJI Mini",
|
20: "DJI Mini",
|
||||||
99: "Unknown"
|
99: t('map.unknown')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -788,14 +788,14 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h4 className="font-semibold text-red-700 flex items-center space-x-1">
|
<h4 className="font-semibold text-red-700 flex items-center space-x-1">
|
||||||
<span>🚨</span>
|
<span>🚨</span>
|
||||||
<span>Drone Detection Details</span>
|
<span>{t('map.droneDetectionDetails')}</span>
|
||||||
</h4>
|
</h4>
|
||||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
age < 1 ? 'bg-red-100 text-red-800' :
|
age < 1 ? 'bg-red-100 text-red-800' :
|
||||||
age < 3 ? 'bg-orange-100 text-orange-800' :
|
age < 3 ? 'bg-orange-100 text-orange-800' :
|
||||||
'bg-gray-100 text-gray-800'
|
'bg-gray-100 text-gray-800'
|
||||||
}`}>
|
}`}>
|
||||||
{age < 1 ? 'LIVE' : `${Math.round(age)}m ago`}
|
{ age < 1 ? t('map.live') : `${Math.round(age)}m ago`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -804,11 +804,11 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
<div className="bg-gray-50 rounded-lg p-2">
|
<div className="bg-gray-50 rounded-lg p-2">
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">Drone ID:</span>
|
<span className="font-medium text-gray-700">{t('map.droneId')}:</span>
|
||||||
<div className="text-gray-900 font-mono">{detection.drone_id}</div>
|
<div className="text-gray-900 font-mono">{detection.drone_id}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">Type:</span>
|
<span className="font-medium text-gray-700">{t('map.type')}:</span>
|
||||||
<div className="text-gray-900">
|
<div className="text-gray-900">
|
||||||
{droneTypes[detection.drone_type] || `Type ${detection.drone_type}`}
|
{droneTypes[detection.drone_type] || `Type ${detection.drone_type}`}
|
||||||
</div>
|
</div>
|
||||||
@@ -822,7 +822,7 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
|
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2">
|
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">RSSI:</span>
|
<span className="font-medium text-gray-700">{t('map.rssi')}:</span>
|
||||||
<div className={`font-mono ${
|
<div className={`font-mono ${
|
||||||
detection.rssi > -50 ? 'text-red-600' :
|
detection.rssi > -50 ? 'text-red-600' :
|
||||||
detection.rssi > -70 ? 'text-orange-600' :
|
detection.rssi > -70 ? 'text-orange-600' :
|
||||||
@@ -832,7 +832,7 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">Frequency:</span>
|
<span className="font-medium text-gray-700">{t('map.frequency')}:</span>
|
||||||
<div className="text-gray-900">{detection.freq}MHz</div>
|
<div className="text-gray-900">{detection.freq}MHz</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -840,10 +840,10 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
|
|
||||||
{/* Detection Timeline */}
|
{/* Detection Timeline */}
|
||||||
<div className="border-t border-gray-200 pt-2">
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<span className="font-medium text-gray-700 block mb-2">Detection Timeline:</span>
|
<span className="font-medium text-gray-700 block mb-2">{t('map.detectionTimeline')}:</span>
|
||||||
<div className="text-xs space-y-1">
|
<div className="text-xs space-y-1">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">First detected:</span>
|
<span className="text-gray-600">{t('map.firstDetected')}:</span>
|
||||||
<span className="font-mono text-gray-900">
|
<span className="font-mono text-gray-900">
|
||||||
{(() => {
|
{(() => {
|
||||||
const timestamp = firstDetection.device_timestamp || firstDetection.timestamp || firstDetection.server_timestamp;
|
const timestamp = firstDetection.device_timestamp || firstDetection.timestamp || firstDetection.server_timestamp;
|
||||||
@@ -854,12 +854,12 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Invalid firstDetection timestamp:', timestamp, e);
|
console.warn('Invalid firstDetection timestamp:', timestamp, e);
|
||||||
}
|
}
|
||||||
return 'Unknown';
|
return t('map.unknown');
|
||||||
})()}
|
})()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Latest detection:</span>
|
<span className="text-gray-600">{t('map.latestDetection')}:</span>
|
||||||
<span className="font-mono text-gray-900">
|
<span className="font-mono text-gray-900">
|
||||||
{(() => {
|
{(() => {
|
||||||
const timestamp = detection.device_timestamp || detection.timestamp || detection.server_timestamp;
|
const timestamp = detection.device_timestamp || detection.timestamp || detection.server_timestamp;
|
||||||
@@ -870,13 +870,13 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Invalid detection timestamp:', timestamp, e);
|
console.warn('Invalid detection timestamp:', timestamp, e);
|
||||||
}
|
}
|
||||||
return 'Unknown';
|
return t('map.unknown');
|
||||||
})()}
|
})()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{droneHistory.length > 1 && (
|
{droneHistory.length > 1 && (
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Total detections:</span>
|
<span className="text-gray-600">{t('map.totalDetections')}:</span>
|
||||||
<span className="font-medium text-gray-900">{droneHistory.length}</span>
|
<span className="font-medium text-gray-900">{droneHistory.length}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -886,7 +886,7 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
{/* Movement Analysis */}
|
{/* Movement Analysis */}
|
||||||
{movementTrend && (
|
{movementTrend && (
|
||||||
<div className="border-t border-gray-200 pt-2">
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<span className="font-medium text-gray-700 block mb-2">Movement Analysis:</span>
|
<span className="font-medium text-gray-700 block mb-2">{t('map.movementAnalysis')}:</span>
|
||||||
<div className="text-xs space-y-2">
|
<div className="text-xs space-y-2">
|
||||||
<div className={`px-2 py-1 rounded ${
|
<div className={`px-2 py-1 rounded ${
|
||||||
movementTrend.trend === 'APPROACHING' ? 'bg-red-100 text-red-800' :
|
movementTrend.trend === 'APPROACHING' ? 'bg-red-100 text-red-800' :
|
||||||
@@ -894,19 +894,19 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
'bg-yellow-100 text-yellow-800'
|
'bg-yellow-100 text-yellow-800'
|
||||||
}`}>
|
}`}>
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{movementTrend.trend === 'APPROACHING' ? '⚠️ APPROACHING' :
|
{movementTrend.trend === 'APPROACHING' ? `⚠️ ${t('map.approaching')}` :
|
||||||
movementTrend.trend === 'RETREATING' ? '✅ RETREATING' :
|
movementTrend.trend === 'RETREATING' ? `✅ ${t('map.retreating')}` :
|
||||||
'➡️ STABLE POSITION'}
|
`➡️ ${t('map.stablePosition')}`}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
RSSI change: {movementTrend.change > 0 ? '+' : ''}{typeof movementTrend.change === 'number' ? movementTrend.change.toFixed(1) : 'N/A'}dB
|
{t('map.rssiChange')}: {movementTrend.change > 0 ? '+' : ''}{typeof movementTrend.change === 'number' ? movementTrend.change.toFixed(1) : 'N/A'}dB
|
||||||
over {typeof movementTrend.duration === 'number' ? movementTrend.duration.toFixed(1) : 'N/A'} minutes
|
{t('map.over')} {typeof movementTrend.duration === 'number' ? movementTrend.duration.toFixed(1) : 'N/A'} {t('map.minutes')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Signal Strength History Graph (simplified) */}
|
{/* Signal Strength History Graph (simplified) */}
|
||||||
<div className="bg-gray-50 rounded p-2">
|
<div className="bg-gray-50 rounded p-2">
|
||||||
<div className="text-gray-600 mb-1">Signal Strength Trend:</div>
|
<div className="text-gray-600 mb-1">{t('map.signalStrengthTrend')}:</div>
|
||||||
<div className="flex items-end space-x-1 h-8">
|
<div className="flex items-end space-x-1 h-8">
|
||||||
{droneHistory.slice(0, 8).reverse().map((hist, idx) => {
|
{droneHistory.slice(0, 8).reverse().map((hist, idx) => {
|
||||||
const height = Math.max(10, Math.min(32, (hist.rssi + 100) / 2)); // Scale -100 to 0 dBm to 10-32px
|
const height = Math.max(10, Math.min(32, (hist.rssi + 100) / 2)); // Scale -100 to 0 dBm to 10-32px
|
||||||
@@ -934,7 +934,7 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500 mt-1">Last 8 detections (oldest to newest)</div>
|
<div className="text-xs text-gray-500 mt-1">{t('map.lastDetections')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -942,26 +942,26 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
|
|
||||||
{/* Current Detection Details */}
|
{/* Current Detection Details */}
|
||||||
<div className="border-t border-gray-200 pt-2">
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<span className="font-medium text-gray-700 block mb-2">Current Detection:</span>
|
<span className="font-medium text-gray-700 block mb-2">{t('map.currentDetection')}:</span>
|
||||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Confidence:</span>
|
<span className="text-gray-600">{t('map.confidence')}:</span>
|
||||||
<div className="text-gray-900">
|
<div className="text-gray-900">
|
||||||
{typeof detection.confidence_level === 'number' ? (detection.confidence_level * 100).toFixed(0) : 'N/A'}%
|
{typeof detection.confidence_level === 'number' ? (detection.confidence_level * 100).toFixed(0) : 'N/A'}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Duration:</span>
|
<span className="text-gray-600">{t('map.duration')}:</span>
|
||||||
<div className="text-gray-900">
|
<div className="text-gray-900">
|
||||||
{typeof detection.signal_duration === 'number' ? (detection.signal_duration / 1000).toFixed(1) : 'N/A'}s
|
{typeof detection.signal_duration === 'number' ? (detection.signal_duration / 1000).toFixed(1) : 'N/A'}s
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Detector:</span>
|
<span className="text-gray-600">{t('map.detector')}:</span>
|
||||||
<div className="text-gray-900">Device {detection.device_id}</div>
|
<div className="text-gray-900">{t('map.deviceName')} {detection.device_id}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Location:</span>
|
<span className="text-gray-600">{t('map.location')}:</span>
|
||||||
<div className="text-gray-900 font-mono">
|
<div className="text-gray-900 font-mono">
|
||||||
{typeof detection.geo_lat === 'number' ? detection.geo_lat.toFixed(4) : 'N/A'}, {typeof detection.geo_lon === 'number' ? detection.geo_lon.toFixed(4) : 'N/A'}
|
{typeof detection.geo_lat === 'number' ? detection.geo_lat.toFixed(4) : 'N/A'}, {typeof detection.geo_lon === 'number' ? detection.geo_lon.toFixed(4) : 'N/A'}
|
||||||
</div>
|
</div>
|
||||||
@@ -972,7 +972,7 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
{/* Legacy movement analysis from detection */}
|
{/* Legacy movement analysis from detection */}
|
||||||
{detection.movement_analysis && (
|
{detection.movement_analysis && (
|
||||||
<div className="border-t border-gray-200 pt-2">
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<span className="font-medium text-gray-700 block mb-1">Real-time Analysis:</span>
|
<span className="font-medium text-gray-700 block mb-1">{t('map.realTimeAnalysis')}:</span>
|
||||||
<div className="text-xs space-y-1">
|
<div className="text-xs space-y-1">
|
||||||
<div className={`px-2 py-1 rounded ${
|
<div className={`px-2 py-1 rounded ${
|
||||||
detection.movement_analysis.alertLevel >= 3 ? 'bg-red-100 text-red-800' :
|
detection.movement_analysis.alertLevel >= 3 ? 'bg-red-100 text-red-800' :
|
||||||
@@ -985,13 +985,15 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
|
|
||||||
{detection.movement_analysis.rssiTrend && (
|
{detection.movement_analysis.rssiTrend && (
|
||||||
<div className="flex items-center space-x-2 mt-1">
|
<div className="flex items-center space-x-2 mt-1">
|
||||||
<span className="text-gray-600">Instant trend:</span>
|
<span className="text-gray-600">{t('map.instantTrend')}:</span>
|
||||||
<span className={`font-medium ${
|
<span className={`font-medium ${
|
||||||
detection.movement_analysis.rssiTrend.trend === 'STRENGTHENING' ? 'text-red-600' :
|
detection.movement_analysis.rssiTrend.trend === 'STRENGTHENING' ? 'text-red-600' :
|
||||||
detection.movement_analysis.rssiTrend.trend === 'WEAKENING' ? 'text-green-600' :
|
detection.movement_analysis.rssiTrend.trend === 'WEAKENING' ? 'text-green-600' :
|
||||||
'text-gray-600'
|
'text-gray-600'
|
||||||
}`}>
|
}`}>
|
||||||
{detection.movement_analysis.rssiTrend.trend}
|
{detection.movement_analysis.rssiTrend.trend === 'STRENGTHENING' ? t('map.strengthening') :
|
||||||
|
detection.movement_analysis.rssiTrend.trend === 'WEAKENING' ? t('map.weakening') :
|
||||||
|
detection.movement_analysis.rssiTrend.trend}
|
||||||
{detection.movement_analysis.rssiTrend.change !== 0 && (
|
{detection.movement_analysis.rssiTrend.change !== 0 && (
|
||||||
<span className="ml-1">
|
<span className="ml-1">
|
||||||
({detection.movement_analysis.rssiTrend.change > 0 ? '+' : ''}{typeof detection.movement_analysis.rssiTrend.change === 'number' ? detection.movement_analysis.rssiTrend.change.toFixed(1) : 'N/A'}dB)
|
({detection.movement_analysis.rssiTrend.change > 0 ? '+' : ''}{typeof detection.movement_analysis.rssiTrend.change === 'number' ? detection.movement_analysis.rssiTrend.change.toFixed(1) : 'N/A'}dB)
|
||||||
|
|||||||
@@ -314,7 +314,38 @@ const translations = {
|
|||||||
droneLabels: 'Drone ID labels shown',
|
droneLabels: 'Drone ID labels shown',
|
||||||
positionOffsets: 'Slight position offsets for visibility',
|
positionOffsets: 'Slight position offsets for visibility',
|
||||||
ringSize: 'Ring size = estimated distance from detector',
|
ringSize: 'Ring size = estimated distance from detector',
|
||||||
showDroneDetections: 'Show Drone Detections'
|
showDroneDetections: 'Show Drone Detections',
|
||||||
|
droneDetectionDetails: 'Drone Detection Details',
|
||||||
|
live: 'LIVE',
|
||||||
|
droneId: 'Drone ID',
|
||||||
|
type: 'Type',
|
||||||
|
rssi: 'RSSI',
|
||||||
|
frequency: 'Frequency',
|
||||||
|
detectionTimeline: 'Detection Timeline',
|
||||||
|
firstDetected: 'First detected',
|
||||||
|
latestDetection: 'Latest detection',
|
||||||
|
currentDetection: 'Current Detection',
|
||||||
|
confidence: 'Confidence',
|
||||||
|
duration: 'Duration',
|
||||||
|
detector: 'Detector',
|
||||||
|
location: 'Location',
|
||||||
|
realTimeAnalysis: 'Real-time Analysis',
|
||||||
|
firstDetection: 'First detection',
|
||||||
|
movementAnalysis: 'Movement Analysis',
|
||||||
|
totalDetections: 'Total detections',
|
||||||
|
signalStrengthTrend: 'Signal Strength Trend',
|
||||||
|
lastDetections: 'Last 8 detections (oldest to newest)',
|
||||||
|
approaching: 'APPROACHING',
|
||||||
|
retreating: 'RETREATING',
|
||||||
|
stablePosition: 'STABLE POSITION',
|
||||||
|
rssiChange: 'RSSI change',
|
||||||
|
over: 'over',
|
||||||
|
minutes: 'minutes',
|
||||||
|
instantTrend: 'Instant trend',
|
||||||
|
strengthening: 'STRENGTHENING',
|
||||||
|
weakening: 'WEAKENING',
|
||||||
|
deviceName: 'Device',
|
||||||
|
unknown: 'Unknown'
|
||||||
},
|
},
|
||||||
movementAlerts: {
|
movementAlerts: {
|
||||||
title: 'Movement Alerts',
|
title: 'Movement Alerts',
|
||||||
@@ -713,7 +744,38 @@ const translations = {
|
|||||||
droneLabels: 'Drönar-ID-etiketter visas',
|
droneLabels: 'Drönar-ID-etiketter visas',
|
||||||
positionOffsets: 'Lätta positionsförskjutningar för synlighet',
|
positionOffsets: 'Lätta positionsförskjutningar för synlighet',
|
||||||
ringSize: 'Ringstorlek = uppskattad distans från detektor',
|
ringSize: 'Ringstorlek = uppskattad distans från detektor',
|
||||||
showDroneDetections: 'Visa drönardetekteringar'
|
showDroneDetections: 'Visa drönardetekteringar',
|
||||||
|
droneDetectionDetails: 'Drönardetekteringsdetaljer',
|
||||||
|
live: 'LIVE',
|
||||||
|
droneId: 'Drönar-ID',
|
||||||
|
type: 'Typ',
|
||||||
|
rssi: 'RSSI',
|
||||||
|
frequency: 'Frekvens',
|
||||||
|
detectionTimeline: 'Detekteringstidslinje',
|
||||||
|
firstDetected: 'Först detekterad',
|
||||||
|
latestDetection: 'Senaste detektion',
|
||||||
|
currentDetection: 'Aktuell detektion',
|
||||||
|
confidence: 'Konfidensgrad',
|
||||||
|
duration: 'Varaktighet',
|
||||||
|
detector: 'Detektor',
|
||||||
|
location: 'Plats',
|
||||||
|
realTimeAnalysis: 'Realtidsanalys',
|
||||||
|
firstDetection: 'Första detektion',
|
||||||
|
movementAnalysis: 'Rörelseanalys',
|
||||||
|
totalDetections: 'Totalt antal detektioner',
|
||||||
|
signalStrengthTrend: 'Signalstyrketrend',
|
||||||
|
lastDetections: 'Senaste 8 detektioner (äldsta till nyaste)',
|
||||||
|
approaching: 'NÄRMAR SIG',
|
||||||
|
retreating: 'DRAR SIG TILLBAKA',
|
||||||
|
stablePosition: 'STABIL POSITION',
|
||||||
|
rssiChange: 'RSSI-förändring',
|
||||||
|
over: 'över',
|
||||||
|
minutes: 'minuter',
|
||||||
|
instantTrend: 'Omedelbar trend',
|
||||||
|
strengthening: 'FÖRSTÄRKNING',
|
||||||
|
weakening: 'FÖRSVAGNING',
|
||||||
|
deviceName: 'Enhet',
|
||||||
|
unknown: 'Okänd'
|
||||||
},
|
},
|
||||||
movementAlerts: {
|
movementAlerts: {
|
||||||
title: 'Rörelselarm',
|
title: 'Rörelselarm',
|
||||||
|
|||||||
Reference in New Issue
Block a user