Compare commits

...

2 commits

Author SHA1 Message Date
d73ecc2b20 Fix JavaScript error when switching between view tabs
Problem: Clicking on tabs other than Map caused JavaScript errors due to
incorrect DOM element ID resolution in switchView() method.

Root cause: The viewId extraction from button IDs didn't match the expected
view element IDs, causing null reference errors when trying to add classes.

Solution:
- Fixed switchView() to correctly handle view IDs (e.g., "map-view")
- Added null checks for DOM elements to prevent crashes
- Updated this.currentView initialization and comparisons to match full IDs
- Ensured view-specific initialization works with correct baseName extraction

Now all tabs (Map, Table, Statistics, Coverage, 3D Radar) work without errors.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 00:09:25 +02:00
5a4f9e2e20 Fix source popup closing immediately after opening
Problem: Source markers were being recreated on every WebSocket update
(every second), which destroyed any open popups.

Solution: Modified updateSourceMarkers() to:
- Only remove markers for sources that no longer exist
- Update existing markers in-place instead of recreating them
- Preserve open popups by updating content instead of recreating markers
- Only create new markers when sources are actually added

This ensures that clicking on a source marker opens a popup that stays
open for user interaction, similar to aircraft popups.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 00:06:59 +02:00

View file

@ -22,7 +22,7 @@ class SkyView {
this.heatmapLayer = null; this.heatmapLayer = null;
// UI state // UI state
this.currentView = 'map'; this.currentView = 'map-view';
this.showTrails = false; this.showTrails = false;
this.showRange = false; this.showRange = false;
this.showSources = true; this.showSources = true;
@ -70,16 +70,26 @@ class SkyView {
switchView(viewId) { switchView(viewId) {
// Update buttons // Update buttons
document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active'));
document.getElementById(`${viewId}-btn`).classList.add('active'); const activeBtn = document.getElementById(`${viewId}-btn`);
if (activeBtn) {
activeBtn.classList.add('active');
}
// Update views // Update views (viewId already includes the full view ID like "map-view")
document.querySelectorAll('.view').forEach(view => view.classList.remove('active')); document.querySelectorAll('.view').forEach(view => view.classList.remove('active'));
document.getElementById(`${viewId}-view`).classList.add('active'); const activeView = document.getElementById(viewId);
if (activeView) {
activeView.classList.add('active');
} else {
console.warn(`View element not found: ${viewId}`);
return;
}
this.currentView = viewId; this.currentView = viewId;
// Handle view-specific initialization // Handle view-specific initialization (extract base name for switch)
switch (viewId) { const baseName = viewId.replace('-view', '');
switch (baseName) {
case 'coverage': case 'coverage':
this.initializeCoverageMap(); this.initializeCoverageMap();
break; break;
@ -208,7 +218,7 @@ class SkyView {
this.updateStatistics(); this.updateStatistics();
this.updateHeaderInfo(); this.updateHeaderInfo();
if (this.currentView === 'radar3d') { if (this.currentView === 'radar3d-view') {
this.update3DRadar(); this.update3DRadar();
} }
} }
@ -333,27 +343,50 @@ class SkyView {
} }
updateSourceMarkers() { updateSourceMarkers() {
// Clear existing source markers // Remove markers for sources that no longer exist
this.sourceMarkers.forEach(marker => this.map.removeLayer(marker)); const currentSourceIds = new Set(this.sourcesData.keys());
this.sourceMarkers.clear(); for (const [id, marker] of this.sourceMarkers) {
if (!currentSourceIds.has(id)) {
this.map.removeLayer(marker);
this.sourceMarkers.delete(id);
}
}
// Add source markers // Update or create markers for current sources
for (const [id, source] of this.sourcesData) { for (const [id, source] of this.sourcesData) {
if (source.latitude && source.longitude) { if (source.latitude && source.longitude) {
const marker = L.circleMarker([source.latitude, source.longitude], { if (this.sourceMarkers.has(id)) {
radius: source.active ? 10 : 6, // Update existing marker
fillColor: source.active ? '#00d4ff' : '#666666', const marker = this.sourceMarkers.get(id);
color: '#ffffff',
weight: 2, // Update marker style if status changed
fillOpacity: 0.8, marker.setStyle({
className: 'source-marker' radius: source.active ? 10 : 6,
}).addTo(this.map); fillColor: source.active ? '#00d4ff' : '#666666',
fillOpacity: 0.8
marker.bindPopup(this.createSourcePopupContent(source), { });
maxWidth: 300
}); // Update popup content if it's open
if (marker.isPopupOpen()) {
this.sourceMarkers.set(id, marker); marker.setPopupContent(this.createSourcePopupContent(source));
}
} else {
// Create new marker
const marker = L.circleMarker([source.latitude, source.longitude], {
radius: source.active ? 10 : 6,
fillColor: source.active ? '#00d4ff' : '#666666',
color: '#ffffff',
weight: 2,
fillOpacity: 0.8,
className: 'source-marker'
}).addTo(this.map);
marker.bindPopup(this.createSourcePopupContent(source), {
maxWidth: 300
});
this.sourceMarkers.set(id, marker);
}
} }
} }