// UI interaction handlers import { showStatus, parseGeoLocation } from './utils.js'; import { map, toggleStartLocationVisibility } from './map-manager.js'; import { loadLocations, handleAddLocation, handleEditLocation, handleDeleteLocation, openEditForm, closeEditForm, closeAddModal, openAddModal } from './location-manager.js'; export let userLocationMarker = null; export let isAddingLocation = false; export function getUserLocation() { if (!navigator.geolocation) { showStatus('Geolocation is not supported by your browser', 'error'); return; } showStatus('Getting your location...', 'info'); navigator.geolocation.getCurrentPosition( (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; // Center map on user location map.setView([lat, lng], 19); // Add or update user location marker if (userLocationMarker) { userLocationMarker.setLatLng([lat, lng]); } else { userLocationMarker = L.circleMarker([lat, lng], { radius: 10, fillColor: '#2196F3', color: '#fff', weight: 3, opacity: 1, fillOpacity: 0.8 }).addTo(map); userLocationMarker.bindPopup('Your Location'); } showStatus('Location found!', 'success'); }, (error) => { let message = 'Unable to get your location'; switch(error.code) { case error.PERMISSION_DENIED: message = 'Location permission denied'; break; case error.POSITION_UNAVAILABLE: message = 'Location information unavailable'; break; case error.TIMEOUT: message = 'Location request timed out'; break; } showStatus(message, 'error'); }, { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 } ); } export function toggleAddLocationMode() { isAddingLocation = !isAddingLocation; const crosshair = document.getElementById('crosshair'); const addBtn = document.getElementById('add-location-btn'); const mobileAddBtn = document.getElementById('mobile-add-location-btn'); if (isAddingLocation) { crosshair.classList.remove('hidden'); // Update desktop button if (addBtn) { addBtn.classList.add('active'); addBtn.innerHTML = 'Cancel'; } // Update mobile button if (mobileAddBtn) { mobileAddBtn.classList.add('active'); mobileAddBtn.innerHTML = '✕'; mobileAddBtn.title = 'Cancel'; } map.on('click', handleMapClick); } else { crosshair.classList.add('hidden'); // Update desktop button if (addBtn) { addBtn.classList.remove('active'); addBtn.innerHTML = 'Add Location Here'; } // Update mobile button if (mobileAddBtn) { mobileAddBtn.classList.remove('active'); mobileAddBtn.innerHTML = '➕'; mobileAddBtn.title = 'Add Location'; } map.off('click', handleMapClick); } } function handleMapClick(e) { if (!isAddingLocation) return; const { lat, lng } = e.latlng; openAddModal(lat, lng); toggleAddLocationMode(); } export function toggleFullscreen() { const app = document.getElementById('app'); const btn = document.getElementById('fullscreen-btn'); const mobileBtn = document.getElementById('mobile-fullscreen-btn'); if (!document.fullscreenElement) { app.requestFullscreen().then(() => { app.classList.add('fullscreen'); // Update desktop button if (btn) { btn.innerHTML = 'Exit Fullscreen'; } // Update mobile button if (mobileBtn) { mobileBtn.innerHTML = '◱'; mobileBtn.title = 'Exit Fullscreen'; } }).catch(err => { console.error('Error entering fullscreen:', err); showStatus('Unable to enter fullscreen', 'error'); }); } else { document.exitFullscreen().then(() => { app.classList.remove('fullscreen'); // Update desktop button if (btn) { btn.innerHTML = 'Fullscreen'; } // Update mobile button if (mobileBtn) { mobileBtn.innerHTML = '⛶'; mobileBtn.title = 'Fullscreen'; } }); } } export async function lookupAddress(mode) { let latInput, lngInput, addressInput; if (mode === 'add') { latInput = document.getElementById('location-lat'); lngInput = document.getElementById('location-lng'); addressInput = document.getElementById('location-address'); } else if (mode === 'edit') { latInput = document.getElementById('edit-location-lat'); lngInput = document.getElementById('edit-location-lng'); addressInput = document.getElementById('edit-location-address'); } else { console.error('Invalid lookup mode:', mode); return; } if (!latInput || !lngInput || !addressInput) { showStatus('Form elements not found', 'error'); return; } const lat = parseFloat(latInput.value); const lng = parseFloat(lngInput.value); if (isNaN(lat) || isNaN(lng)) { showStatus('Please enter valid coordinates first', 'warning'); return; } // Show loading state const button = mode === 'add' ? document.getElementById('lookup-address-add-btn') : document.getElementById('lookup-address-edit-btn'); const originalText = button ? button.textContent : ''; if (button) { button.disabled = true; button.textContent = 'Looking up...'; } try { console.log(`Looking up address for: ${lat}, ${lng}`); const response = await fetch(`/api/geocode/reverse?lat=${lat}&lng=${lng}`); if (!response.ok) { const errorText = await response.text(); throw new Error(`Geocoding failed: ${response.status} ${errorText}`); } const data = await response.json(); if (data.success && data.data) { // Use the formatted address or full address const address = data.data.formattedAddress || data.data.fullAddress; if (address) { addressInput.value = address; showStatus('Address found!', 'success'); } else { showStatus('No address found for these coordinates', 'warning'); } } else { showStatus('Address lookup failed', 'warning'); } } catch (error) { console.error('Address lookup error:', error); showStatus(`Address lookup failed: ${error.message}`, 'error'); } finally { // Restore button state if (button) { button.disabled = false; button.textContent = originalText; } } } export function setupGeoLocationSync() { // For add form const addLatInput = document.getElementById('location-lat'); const addLngInput = document.getElementById('location-lng'); const addGeoInput = document.getElementById('geo-location'); if (addLatInput && addLngInput && addGeoInput) { [addLatInput, addLngInput].forEach(input => { input.addEventListener('input', () => { const lat = addLatInput.value; const lng = addLngInput.value; if (lat && lng) { addGeoInput.value = `${lat};${lng}`; } }); }); addGeoInput.addEventListener('input', () => { const coords = parseGeoLocation(addGeoInput.value); if (coords) { addLatInput.value = coords.lat; addLngInput.value = coords.lng; } }); } // For edit form const editLatInput = document.getElementById('edit-location-lat'); const editLngInput = document.getElementById('edit-location-lng'); const editGeoInput = document.getElementById('edit-geo-location'); if (editLatInput && editLngInput && editGeoInput) { [editLatInput, editLngInput].forEach(input => { input.addEventListener('input', () => { const lat = editLatInput.value; const lng = editLngInput.value; if (lat && lng) { editGeoInput.value = `${lat};${lng}`; } }); }); editGeoInput.addEventListener('input', () => { const coords = parseGeoLocation(editGeoInput.value); if (coords) { editLatInput.value = coords.lat; editLngInput.value = coords.lng; } }); } } export function setupEventListeners() { // Desktop controls document.getElementById('refresh-btn')?.addEventListener('click', () => { loadLocations(); showStatus('Locations refreshed', 'success'); }); document.getElementById('geolocate-btn')?.addEventListener('click', getUserLocation); document.getElementById('toggle-start-location-btn')?.addEventListener('click', toggleStartLocationVisibility); document.getElementById('add-location-btn')?.addEventListener('click', toggleAddLocationMode); document.getElementById('fullscreen-btn')?.addEventListener('click', toggleFullscreen); // Mobile controls document.getElementById('mobile-refresh-btn')?.addEventListener('click', () => { loadLocations(); showStatus('Locations refreshed', 'success'); }); document.getElementById('mobile-geolocate-btn')?.addEventListener('click', getUserLocation); document.getElementById('mobile-toggle-start-location-btn')?.addEventListener('click', toggleStartLocationVisibility); document.getElementById('mobile-add-location-btn')?.addEventListener('click', toggleAddLocationMode); document.getElementById('mobile-fullscreen-btn')?.addEventListener('click', toggleFullscreen); // Mobile dropdown toggle document.getElementById('mobile-dropdown-toggle')?.addEventListener('click', (e) => { e.stopPropagation(); const dropdown = document.getElementById('mobile-dropdown'); dropdown.classList.toggle('active'); }); // Close mobile dropdown when clicking outside document.addEventListener('click', (e) => { const dropdown = document.getElementById('mobile-dropdown'); if (!dropdown.contains(e.target)) { dropdown.classList.remove('active'); } }); // Modal controls document.getElementById('close-modal-btn')?.addEventListener('click', closeAddModal); document.getElementById('cancel-modal-btn')?.addEventListener('click', closeAddModal); // Edit footer controls document.getElementById('close-edit-footer-btn')?.addEventListener('click', closeEditForm); // Forms document.getElementById('location-form')?.addEventListener('submit', handleAddLocation); document.getElementById('edit-location-form')?.addEventListener('submit', handleEditLocation); // Delete button document.getElementById('delete-location-btn')?.addEventListener('click', handleDeleteLocation); // Address lookup buttons document.getElementById('lookup-address-add-btn')?.addEventListener('click', () => { lookupAddress('add'); }); document.getElementById('lookup-address-edit-btn')?.addEventListener('click', () => { lookupAddress('edit'); }); // Geo-location field sync setupGeoLocationSync(); // Add event delegation for popup edit buttons document.addEventListener('click', (e) => { if (e.target.classList.contains('edit-location-popup-btn')) { e.preventDefault(); try { const locationData = JSON.parse(e.target.getAttribute('data-location')); openEditForm(locationData); } catch (error) { console.error('Error parsing location data:', error); showStatus('Error opening edit form', 'error'); } } }); }