- -
-
- + + + + + @@ -402,10 +327,11 @@ let map; let stations = []; let selectedStation = null; - let addMode = false; let tempMarker = null; let user = null; let currentCity = null; + let contextMenuPosition = null; + let longPressTimer = null; function initDashboard() { // Get city name from URL @@ -435,7 +361,6 @@ } user = data.user; - document.getElementById('username').textContent = user.display_name || user.username || 'User'; } catch (error) { window.location.href = '/login'; } @@ -448,33 +373,99 @@ attribution: '© OpenStreetMap contributors' }).addTo(map); - map.on('click', function(e) { - if (addMode) { - setStationLocation(e.latlng); + // Handle right-click context menu + map.on('contextmenu', function(e) { + console.log('contextmenu event:', e); + e.originalEvent.preventDefault(); + const clientX = e.originalEvent.clientX || e.originalEvent.pageX; + const clientY = e.originalEvent.clientY || e.originalEvent.pageY; + console.log('Context menu coordinates:', clientX, clientY); + showContextMenu(clientX, clientY, e.latlng); + }); + + // Handle long-press for mobile + let longPressTimer = null; + let longPressStarted = false; + + map.on('mousedown', function(e) { + if (e.originalEvent.button === 0) { // Left click only + longPressStarted = true; + longPressTimer = setTimeout(() => { + if (longPressStarted) { + showContextMenu(e.originalEvent.clientX, e.originalEvent.clientY, e.latlng); + } + }, 500); // 500ms for long press } }); + + map.on('mouseup', function(e) { + if (longPressTimer) { + clearTimeout(longPressTimer); + longPressTimer = null; + } + longPressStarted = false; + }); + + map.on('mousemove', function(e) { + if (longPressTimer) { + clearTimeout(longPressTimer); + longPressTimer = null; + } + longPressStarted = false; + }); + + // Hide context menu on map click + map.on('click', function(e) { + hideContextMenu(); + }); } - function setStationLocation(latlng) { - if (tempMarker) { - map.removeLayer(tempMarker); - } + // Context menu functions + function showContextMenu(x, y, latlng) { + console.log('showContextMenu called with:', x, y, latlng); + hideContextMenu(); + const contextMenu = document.getElementById('contextMenu'); + contextMenuPosition = latlng; + console.log('contextMenuPosition set to:', contextMenuPosition); - tempMarker = L.marker(latlng).addTo(map); - document.getElementById('coordinates').value = `${latlng.lat.toFixed(6)}, ${latlng.lng.toFixed(6)}`; - stopAddMode(); + contextMenu.style.display = 'block'; + contextMenu.style.left = x + 'px'; + contextMenu.style.top = y + 'px'; + + // Adjust position if menu goes off screen + const rect = contextMenu.getBoundingClientRect(); + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + + if (rect.right > viewportWidth) { + contextMenu.style.left = (x - rect.width) + 'px'; + } + if (rect.bottom > viewportHeight) { + contextMenu.style.top = (y - rect.height) + 'px'; + } } - function startAddMode() { - addMode = true; - map.getContainer().style.cursor = 'crosshair'; + function hideContextMenu() { + document.getElementById('contextMenu').style.display = 'none'; + contextMenuPosition = null; } - function stopAddMode() { - addMode = false; - map.getContainer().style.cursor = ''; + function contextAddStation() { + console.log('contextAddStation called'); + console.log('contextMenuPosition:', contextMenuPosition); + hideContextMenu(); + if (contextMenuPosition) { + console.log('Opening modal with position:', contextMenuPosition); + openAddStationModal(contextMenuPosition); + } else { + console.log('No contextMenuPosition available'); + } } + // Expose functions to global scope for onclick handlers (for modal close buttons) + window.closeUpdateModal = closeUpdateModal; + window.closeAddModal = closeAddModal; + async function loadStations() { try { const response = await fetch(`/api/cities/${currentCity}/stations`); @@ -484,7 +475,6 @@ document.getElementById('cityName').textContent = data[0].city_name; } displayStations(); - populateStationList(); fitMapToStations(); } catch (error) { console.error('Error loading stations:', error); @@ -554,76 +544,70 @@

Estimated Empty: ${estimatedEmpty}

Last Updated: ${station.last_updated ? new Date(station.last_updated).toLocaleString() : 'Never'}

Last Updated By: ${station.updated_by_name || 'Unknown'}

+
`; } - function populateStationList() { - const stationList = document.getElementById('stationList'); - stationList.innerHTML = ''; - - stations.forEach(station => { - const item = document.createElement('div'); - item.className = 'station-item'; - item.onclick = () => selectStation(station); - - const color = getStationColor(station); - const statusText = getStatusText(station); - - item.innerHTML = ` - ${station.name} - - - ${statusText} - - `; - - stationList.appendChild(item); - }); - } - - function getStatusText(station) { - if (!station.last_refill_time) return 'Never updated'; + // Modal functions + function openUpdateModal(stationId) { + const station = stations.find(s => s.id === stationId); + if (!station) return; - const now = new Date(); - const refillTime = new Date(station.last_refill_time); - const daysSinceRefill = (now - refillTime) / (1000 * 60 * 60 * 24); - - if (daysSinceRefill > 7) return 'Old data (7+ days)'; - - if (!station.estimated_empty_time) return 'Recently refilled'; - - const emptyTime = new Date(station.estimated_empty_time); - const hoursUntilEmpty = (emptyTime - now) / (1000 * 60 * 60); - - if (hoursUntilEmpty <= 0) return 'Empty'; - if (hoursUntilEmpty <= 3) return 'Needs refill soon'; - return 'Recently refilled'; - } - - function selectStation(station) { selectedStation = station; - - // Update UI - document.querySelectorAll('.station-item').forEach(item => { - item.classList.remove('selected'); - }); - - event.target.closest('.station-item').classList.add('selected'); - - // Show update form - document.getElementById('updateStationForm').style.display = 'block'; + document.getElementById('updateModal').style.display = 'block'; // Center map on selected station map.setView([station.latitude, station.longitude], 16); } - function cancelUpdate() { + function closeUpdateModal() { selectedStation = null; - document.getElementById('updateStationForm').style.display = 'none'; - document.querySelectorAll('.station-item').forEach(item => { - item.classList.remove('selected'); - }); + document.getElementById('updateModal').style.display = 'none'; + document.getElementById('updateStationForm').reset(); + document.getElementById('update-message').innerHTML = ''; + } + + function openAddStationModal(latlng = null) { + console.log('openAddStationModal called with:', latlng); + document.getElementById('addModal').style.display = 'block'; + if (latlng) { + console.log('Setting coordinates:', latlng.lat, latlng.lng); + document.getElementById('coordinates').value = `${latlng.lat.toFixed(6)}, ${latlng.lng.toFixed(6)}`; + if (tempMarker) { + map.removeLayer(tempMarker); + } + tempMarker = L.marker(latlng).addTo(map); + console.log('Marker added to map'); + } + } + + function closeAddModal() { + document.getElementById('addModal').style.display = 'none'; + document.getElementById('addStationForm').reset(); + document.getElementById('add-message').innerHTML = ''; + document.getElementById('coordinates').value = ''; + if (tempMarker) { + map.removeLayer(tempMarker); + tempMarker = null; + } + } + + // Close modal when clicking outside + window.onclick = function(event) { + const updateModal = document.getElementById('updateModal'); + const addModal = document.getElementById('addModal'); + const contextMenu = document.getElementById('contextMenu'); + + if (event.target === updateModal) { + closeUpdateModal(); + } + if (event.target === addModal) { + closeAddModal(); + } + if (!contextMenu.contains(event.target)) { + hideContextMenu(); + } } function fitMapToStations() { @@ -641,19 +625,6 @@ fitMapToStations(); } - function switchTab(tabName) { - // Update tab buttons - document.querySelectorAll('.tab-button').forEach(btn => { - btn.classList.remove('active'); - }); - - document.querySelectorAll('.tab-panel').forEach(panel => { - panel.classList.remove('active'); - }); - - event.target.classList.add('active'); - document.getElementById(tabName + '-tab').classList.add('active'); - } function showMessage(elementId, message, type = 'success') { const messageDiv = document.getElementById(elementId); @@ -670,7 +641,7 @@ const coordinates = document.getElementById('coordinates').value; if (!coordinates) { - showMessage('add-message', 'Please select a location on the map', 'error'); + showMessage('add-message', 'Please right-click or long-press on the map to select a location', 'error'); return; } @@ -695,13 +666,10 @@ if (response.ok) { showMessage('add-message', 'Station added successfully!'); - e.target.reset(); - document.getElementById('coordinates').value = ''; - if (tempMarker) { - map.removeLayer(tempMarker); - tempMarker = null; - } - loadStations(); + setTimeout(() => { + closeAddModal(); + loadStations(); + }, 1500); } else { const result = await response.json(); showMessage('add-message', result.error || 'Failed to add station', 'error'); @@ -736,9 +704,10 @@ if (response.ok) { showMessage('update-message', 'Station updated successfully!'); - e.target.reset(); - cancelUpdate(); - loadStations(); + setTimeout(() => { + closeUpdateModal(); + loadStations(); + }, 1500); } else { const result = await response.json(); showMessage('update-message', result.error || 'Failed to update station', 'error'); @@ -758,7 +727,18 @@ } // Initialize dashboard when page loads - document.addEventListener('DOMContentLoaded', initDashboard); + document.addEventListener('DOMContentLoaded', function() { + initDashboard(); + + // Add event listener for context menu item + const menuItem = document.getElementById('addStationMenuItem'); + if (menuItem) { + menuItem.addEventListener('click', contextAddStation); + console.log('Context menu event listener attached'); + } else { + console.error('Could not find addStationMenuItem element'); + } + }); \ No newline at end of file