diff --git a/static/index.html b/static/index.html
index fc4cbe3..5699739 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1103,6 +1103,9 @@
βοΈ Settings
+
@@ -1465,6 +1468,42 @@
+
+
+
@@ -1824,6 +1863,9 @@
+
@@ -2297,6 +2339,14 @@
// TAB SWITCHING
// =========================================================================
function switchTab(tabId) {
+ // Track previous tab for back navigation
+ if (AppState.currentTab !== tabId && AppState.currentTab !== 'tabNavigate') {
+ AppState._prevTab = AppState.currentTab;
+ }
+ // Cleanup nav page when leaving
+ if (AppState.currentTab === 'tabNavigate' && tabId !== 'tabNavigate') {
+ closeNavigate();
+ }
AppState.currentTab = tabId;
// Update tab panels
@@ -2309,6 +2359,7 @@
tabAddAsset: 'tabAddAsset',
tabAssets: 'tabAssets',
tabMap: 'tabMap',
+ tabNavigate: 'tabNavigate',
tabDashboard: 'tabDashboard',
};
const bottomTab = tabMap[tabId] || 'tabMore';
@@ -3528,94 +3579,112 @@
const nav = await api('/api/assets/' + AppState.currentAssetId +
'/navigation?lat=' + AppState.gpsLat + '&lng=' + AppState.gpsLng);
- // Switch to map tab
- switchTab('tabMap');
-
- // Initialize map if needed
- if (!map) { initMap(); }
-
- // Wait for map to be ready
- setTimeout(() => {
- if (!map) return;
-
- // Clear previous route line
- clearRouteLine();
-
- // Draw route line from origin (user GPS) to destination (asset)
- const routeCoords = [
- [nav.origin_lat, nav.origin_lng],
- [nav.asset_lat, nav.asset_lng]
- ];
-
- map._routeLine = L.polyline(routeCoords, {
- color: '#5b6ef7',
- weight: 4,
- opacity: 0.8,
- dashArray: '10, 6',
- }).addTo(map);
-
- // Add arrow markers at both ends
- const originIcon = L.divIcon({
- html: '',
- className: '',
- iconSize: [16, 16],
- iconAnchor: [8, 8],
- });
-
- const destIcon = L.divIcon({
- html: 'π
',
- className: '',
- iconSize: [20, 20],
- iconAnchor: [10, 10],
- });
-
- if (map._routeOriginMarker) map.removeLayer(map._routeOriginMarker);
- map._routeOriginMarker = L.marker([nav.origin_lat, nav.origin_lng], { icon: originIcon }).addTo(map);
-
- if (map._routeDestMarker) map.removeLayer(map._routeDestMarker);
- map._routeDestMarker = L.marker([nav.asset_lat, nav.asset_lng], { icon: destIcon }).addTo(map);
-
- // Distance label at midpoint
- const midLat = (nav.origin_lat + nav.asset_lat) / 2;
- const midLng = (nav.origin_lng + nav.asset_lng) / 2;
- const distText = nav.distance_km >= 1
- ? nav.distance_km + ' km'
- : nav.distance_meters + ' m';
-
- if (map._routeDistLabel) map.removeLayer(map._routeDistLabel);
- map._routeDistLabel = L.marker([midLat, midLng], {
- icon: L.divIcon({
- html: '' + distText + ' ' + nav.cardinal + '
',
- className: '',
- iconSize: null,
- iconAnchor: null,
- }),
- }).addTo(map);
-
- // Fit bounds to show the whole route
- const bounds = L.latLngBounds(routeCoords);
- map.fitBounds(bounds, { padding: [60, 60], maxZoom: 16 });
-
- // Show info toast
- showToast('π ' + distText + ' ' + nav.cardinal + ' to ' + nav.asset_name);
-
- // If asset has address, add popup to destination marker
- if (nav.asset_address) {
- map._routeDestMarker.bindPopup(
- ''
- ).openPopup();
- }
- }, 300);
+ // Store nav data for dedicated page
+ AppState._navData = nav;
+ // Switch to dedicated navigate tab
+ switchTab('tabNavigate');
+ renderNavPage();
} catch (e) {
showToast(e.message, true);
}
}
+ let navMap = null;
+
+ function initNavMap(lat, lng) {
+ if (navMap) { navMap.remove(); navMap = null; }
+ const container = document.getElementById('navMapContainer');
+ navMap = L.map(container, {
+ attributionControl: false,
+ zoomControl: false,
+ dragging: true,
+ tap: true,
+ }).setView([lat, lng], 15);
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ maxZoom: 19,
+ }).addTo(navMap);
+ // Invalidate size after container is visible
+ setTimeout(() => { if (navMap) navMap.invalidateSize(); }, 100);
+ }
+
+ function renderNavPage() {
+ const nav = AppState._navData;
+ if (!nav) return;
+
+ // Set header info
+ document.getElementById('navDestName').textContent = nav.asset_name || 'Navigate';
+ document.getElementById('navDestAddr').textContent = nav.asset_address || '';
+
+ // Init dedicated map centered on route midpoint
+ const midLat = (nav.origin_lat + nav.asset_lat) / 2;
+ const midLng = (nav.origin_lng + nav.asset_lng) / 2;
+ initNavMap(midLat, midLng);
+
+ // Give map time to init, then draw
+ setTimeout(() => {
+ if (!navMap) return;
+
+ // Origin (user) marker β blue
+ const userIcon = L.divIcon({
+ html: '',
+ className: '', iconSize: [14, 14], iconAnchor: [7, 7],
+ });
+ L.marker([nav.origin_lat, nav.origin_lng], { icon: userIcon })
+ .addTo(navMap).bindPopup('You are here');
+
+ // Destination marker β green pin
+ const destIcon = L.divIcon({
+ html: 'π
',
+ className: '', iconSize: [20, 20], iconAnchor: [10, 10],
+ });
+ const destMarker = L.marker([nav.asset_lat, nav.asset_lng], { icon: destIcon }).addTo(navMap);
+ if (nav.asset_address) {
+ destMarker.bindPopup('' + esc(nav.asset_name) + '
π ' + esc(nav.asset_address) + '
');
+ }
+
+ // Route line
+ L.polyline([[nav.origin_lat, nav.origin_lng], [nav.asset_lat, nav.asset_lng]], {
+ color: '#5b6ef7', weight: 4, opacity: 0.8, dashArray: '10, 6',
+ }).addTo(navMap);
+
+ // Fit bounds
+ navMap.fitBounds([[nav.origin_lat, nav.origin_lng], [nav.asset_lat, nav.asset_lng]], { padding: [40, 40], maxZoom: 16 });
+ }, 200);
+
+ // Populate info card
+ const distText = nav.distance_km >= 1
+ ? nav.distance_km + ' km' : nav.distance_meters + ' m';
+ document.getElementById('navDistance').textContent = distText;
+ document.getElementById('navCardinal').textContent = nav.cardinal || 'β';
+ document.getElementById('navBearing').textContent = nav.bearing ? nav.bearing + 'Β°' : 'β';
+ document.getElementById('navInfoCard').style.display = '';
+
+ // Walking directions
+ if (nav.walking_directions) {
+ document.getElementById('navDirections').textContent = nav.walking_directions;
+ document.getElementById('navDirectionsCard').style.display = '';
+ } else {
+ document.getElementById('navDirectionsCard').style.display = 'none';
+ }
+
+ // Google Maps link
+ if (nav.google_maps_url) {
+ const link = document.getElementById('navGoogleMapsLink');
+ link.href = nav.google_maps_url;
+ link.style.display = '';
+ } else {
+ document.getElementById('navGoogleMapsLink').style.display = 'none';
+ }
+ }
+
+ function closeNavigate() {
+ if (navMap) { navMap.remove(); navMap = null; }
+ AppState._navData = null;
+ // Go back to previous tab or asset list
+ switchTab(AppState._prevTab || 'tabAssets');
+ }
+
function clearRouteLine() {
if (!map) return;
if (map._routeLine) { map.removeLayer(map._routeLine); map._routeLine = null; }