Fix jwt-token

This commit is contained in:
2025-09-19 15:11:42 +02:00
parent 5d21bb5b0a
commit 2b4a7bf09e
2 changed files with 96 additions and 32 deletions

View File

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

View File

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