fix: Map tab initialization (H1, H2)

- Added global let map/heatLayer/heatVisible declarations
- Leaflet map initializes on first visit to Map tab (OSM tiles)
- Implemented loadAssetPins() - fetches assets with coords from API,
  creates Leaflet markers with popups, auto-fits bounds
- Guard toggleHeatmap() with proper declared globals
- Closes #31 H1, #32 H2

Also confirmed M1 (empty asset list) is not a bug: API returns [] because
DB has no assets yet. Empty state renders correctly.
This commit is contained in:
2026-05-21 21:46:59 -04:00
parent 5822dfc1ab
commit e9f6af0590
+53 -3
View File
@@ -1595,10 +1595,22 @@
// Map tab lifecycle
if (tabId === 'tabMap') {
if (map) {
setTimeout(() => map.invalidateSize(), 150);
loadAssetPins(); // Refresh pins on every tab visit
if (!map) {
const tileUrl = document.querySelector('meta[name="map-tile-url"]')?.content
|| 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
const tileAttr = document.querySelector('meta[name="map-tile-attribution"]')?.content
|| '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>';
map = L.map('mapContainer', {
center: [39.8283, -98.5795],
zoom: 4,
zoomControl: true,
});
L.tileLayer(tileUrl, { attribution: tileAttr, maxZoom: 19 }).addTo(map);
// Restore heatmap layer if it was active
if (heatVisible) setTimeout(() => loadHeatmapData(), 300);
}
setTimeout(() => map.invalidateSize(), 150);
loadAssetPins(); // Refresh pins on every tab visit
startVisitTracking();
} else {
stopVisitTracking();
@@ -1681,6 +1693,9 @@
let manualPhotoBlob = null;
let manKeysCounter = 0;
let settingsCache = {};
let map = null;
let heatLayer = null;
let heatVisible = false;
// ── Mode Switching ──────────────────────────────────────────────────────
function setAddAssetMode(mode) {
@@ -3405,6 +3420,41 @@
showToast('Import complete: ' + created + ' created, ' + skipped + ' skipped, ' + errors.length + ' errors');
}
// ═══════════════════════════════════════════════════════════════════════
// MAP PINS
// ═══════════════════════════════════════════════════════════════════════
async function loadAssetPins() {
// Store markers on a namespace so we can clear them
if (!window._assetPins) window._assetPins = [];
// Clear existing markers
window._assetPins.forEach(function(m) { if (map) map.removeLayer(m); });
window._assetPins = [];
try {
const assets = await api('/api/assets?limit=1000');
assets.forEach(function(a) {
if (a.latitude != null && a.longitude != null) {
var icon = catIcon(a.category);
var popupHtml = '<b>' + esc(a.name) + '</b><br>' + esc(a.machine_id || '') +
'<br><button class="btn btn-outline btn-sm" style="margin-top:6px;" ' +
'onclick="switchTab(\'tabAssets\');viewAsset(' + a.id + ');">View Details</button>';
var marker = L.marker([a.latitude, a.longitude])
.addTo(map)
.bindPopup(popupHtml);
window._assetPins.push(marker);
}
});
// If we have assets with coords, zoom to fit them
if (window._assetPins.length > 0) {
var group = L.featureGroup(window._assetPins);
map.fitBounds(group.getBounds().pad(0.1));
}
} catch (e) {
console.error('Failed to load asset pins:', e);
}
}
// ═══════════════════════════════════════════════════════════════════════
// HEATMAP
// ═══════════════════════════════════════════════════════════════════════