Fix jwt-token
This commit is contained in:
@@ -233,37 +233,44 @@ const MapView = () => {
|
|||||||
|
|
||||||
// Set initial bounds and center ONLY on first load
|
// Set initial bounds and center ONLY on first load
|
||||||
if (deviceData.length > 0 && isInitialLoad) {
|
if (deviceData.length > 0 && isInitialLoad) {
|
||||||
const lats = deviceData.map(device => device.geo_lat);
|
// Filter devices that have coordinates
|
||||||
const lons = deviceData.map(device => device.geo_lon);
|
const devicesWithCoords = deviceData.filter(device =>
|
||||||
|
device.geo_lat !== null && device.geo_lon !== null
|
||||||
|
);
|
||||||
|
|
||||||
const minLat = Math.min(...lats);
|
if (devicesWithCoords.length > 0) {
|
||||||
const maxLat = Math.max(...lats);
|
const lats = devicesWithCoords.map(device => device.geo_lat);
|
||||||
const minLon = Math.min(...lons);
|
const lons = devicesWithCoords.map(device => device.geo_lon);
|
||||||
const maxLon = Math.max(...lons);
|
|
||||||
|
|
||||||
// Add padding around the bounds (15% on each side for better visibility)
|
const minLat = Math.min(...lats);
|
||||||
const latPadding = (maxLat - minLat) * 0.15;
|
const maxLat = Math.max(...lats);
|
||||||
const lonPadding = (maxLon - minLon) * 0.15;
|
const minLon = Math.min(...lons);
|
||||||
|
const maxLon = Math.max(...lons);
|
||||||
|
|
||||||
const bounds = [
|
// Add padding around the bounds (15% on each side for better visibility)
|
||||||
[minLat - latPadding, minLon - lonPadding], // Southwest
|
const latPadding = (maxLat - minLat) * 0.15;
|
||||||
[maxLat + latPadding, maxLon + lonPadding] // Northeast
|
const lonPadding = (maxLon - minLon) * 0.15;
|
||||||
];
|
|
||||||
|
|
||||||
console.log('MapView: Setting initial bounds and center for devices');
|
const bounds = [
|
||||||
setMapBounds(bounds);
|
[minLat - latPadding, minLon - lonPadding], // Southwest
|
||||||
setShouldFitBounds(true);
|
[maxLat + latPadding, maxLon + lonPadding] // Northeast
|
||||||
|
];
|
||||||
|
|
||||||
// Calculate center
|
console.log('MapView: Setting initial bounds and center for devices with coordinates');
|
||||||
const centerLat = (minLat + maxLat) / 2;
|
setMapBounds(bounds);
|
||||||
const centerLon = (minLon + maxLon) / 2;
|
setShouldFitBounds(true);
|
||||||
setMapCenter([centerLat, centerLon]);
|
|
||||||
|
|
||||||
// Disable automatic fitting after initial setup (with longer delay)
|
// Calculate center
|
||||||
setTimeout(() => {
|
const centerLat = (minLat + maxLat) / 2;
|
||||||
console.log('MapView: Disabling automatic bounds fitting - manual navigation now preserved');
|
const centerLon = (minLon + maxLon) / 2;
|
||||||
setShouldFitBounds(false);
|
setMapCenter([centerLat, centerLon]);
|
||||||
}, 2000);
|
|
||||||
|
// Disable automatic fitting after initial setup (with longer delay)
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('MapView: Disabling automatic bounds fitting - manual navigation now preserved');
|
||||||
|
setShouldFitBounds(false);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always mark initial load as complete after first fetch
|
// Always mark initial load as complete after first fetch
|
||||||
@@ -999,46 +1006,64 @@ const DroneDetectionPopup = ({ detection, age, droneTypes, droneDetectionHistory
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeviceListItem = ({ device, status, detections, onClick }) => (
|
const DeviceListItem = ({ device, status, detections, onClick }) => {
|
||||||
<div
|
const hasCoordinates = device.geo_lat !== null && device.geo_lon !== null;
|
||||||
className="px-6 py-4 hover:bg-gray-50 cursor-pointer transition-colors"
|
|
||||||
onClick={onClick}
|
return (
|
||||||
>
|
<div
|
||||||
<div className="flex items-center justify-between">
|
className="px-6 py-4 hover:bg-gray-50 cursor-pointer transition-colors"
|
||||||
<div className="flex items-center space-x-3">
|
onClick={onClick}
|
||||||
<div className={`w-3 h-3 rounded-full ${
|
>
|
||||||
status === 'online'
|
<div className="flex items-center justify-between">
|
||||||
? detections.length > 0 ? 'bg-red-400 animate-pulse' : 'bg-green-400'
|
<div className="flex items-center space-x-3">
|
||||||
: 'bg-gray-400'
|
<div className={`w-3 h-3 rounded-full ${
|
||||||
}`} />
|
status === 'online'
|
||||||
<div>
|
? detections.length > 0 ? 'bg-red-400 animate-pulse' : 'bg-green-400'
|
||||||
<div className="text-sm font-medium text-gray-900">
|
: 'bg-gray-400'
|
||||||
{device.name || `Device ${device.id}`}
|
}`} />
|
||||||
</div>
|
<div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm font-medium text-gray-900">
|
||||||
{device.location_description || `${device.geo_lat}, ${device.geo_lon}`}
|
{device.name || `Device ${device.id}`}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
{hasCoordinates
|
||||||
|
? (device.location_description || `${device.geo_lat}, ${device.geo_lon}`)
|
||||||
|
: (
|
||||||
|
<span className="text-amber-600 font-medium">
|
||||||
|
⚠️ Coordinates missing - please add location
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
{detections.length > 0 && (
|
{!hasCoordinates && (
|
||||||
<div className="flex items-center space-x-1 text-red-600">
|
<div className="flex items-center space-x-1 text-amber-600">
|
||||||
<ExclamationTriangleIcon className="h-4 w-4" />
|
<ExclamationTriangleIcon className="h-4 w-4" />
|
||||||
<span className="text-sm font-medium">{detections.length}</span>
|
<span className="text-xs font-medium">Incomplete</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
{detections.length > 0 && (
|
||||||
status === 'online'
|
<div className="flex items-center space-x-1 text-red-600">
|
||||||
? 'bg-green-100 text-green-800'
|
<ExclamationTriangleIcon className="h-4 w-4" />
|
||||||
: 'bg-red-100 text-red-800'
|
<span className="text-sm font-medium">{detections.length}</span>
|
||||||
}`}>
|
</div>
|
||||||
{status}
|
)}
|
||||||
</span>
|
|
||||||
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
status === 'online'
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-red-100 text-red-800'
|
||||||
|
}`}>
|
||||||
|
{status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
export default MapView;
|
export default MapView;
|
||||||
|
|||||||
@@ -137,11 +137,10 @@ router.get('/', authenticateToken, async (req, res) => {
|
|||||||
// GET /api/devices/map - Get devices with location data for map display
|
// GET /api/devices/map - Get devices with location data for map display
|
||||||
router.get('/map', authenticateToken, async (req, res) => {
|
router.get('/map', authenticateToken, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
// Get all active devices, including those without coordinates
|
||||||
const devices = await Device.findAll({
|
const devices = await Device.findAll({
|
||||||
where: {
|
where: {
|
||||||
is_active: true,
|
is_active: true
|
||||||
geo_lat: { [Op.ne]: null },
|
|
||||||
geo_lon: { [Op.ne]: null }
|
|
||||||
},
|
},
|
||||||
attributes: [
|
attributes: [
|
||||||
'id',
|
'id',
|
||||||
@@ -153,7 +152,7 @@ router.get('/map', authenticateToken, async (req, res) => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get recent detections for each device
|
// Get recent detections for each device and mark coordinate status
|
||||||
const devicesWithDetections = await Promise.all(devices.map(async (device) => {
|
const devicesWithDetections = await Promise.all(devices.map(async (device) => {
|
||||||
const recentDetections = await DroneDetection.count({
|
const recentDetections = await DroneDetection.count({
|
||||||
where: {
|
where: {
|
||||||
@@ -170,12 +169,15 @@ router.get('/map', authenticateToken, async (req, res) => {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const isOnline = timeSinceLastHeartbeat && timeSinceLastHeartbeat < 600; // 10 minutes
|
const isOnline = timeSinceLastHeartbeat && timeSinceLastHeartbeat < 600; // 10 minutes
|
||||||
|
const hasCoordinates = device.geo_lat !== null && device.geo_lon !== null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...device.toJSON(),
|
...device.toJSON(),
|
||||||
has_recent_detections: recentDetections > 0,
|
has_recent_detections: recentDetections > 0,
|
||||||
detection_count_10m: recentDetections,
|
detection_count_10m: recentDetections,
|
||||||
status: isOnline ? 'online' : 'offline'
|
status: isOnline ? 'online' : 'offline',
|
||||||
|
has_coordinates: hasCoordinates,
|
||||||
|
coordinate_status: hasCoordinates ? 'complete' : 'incomplete'
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user