/** * Map Search Module * Handles address search functionality for the map */ import { map } from './map-manager.js'; import { openAddModal } from './location-manager.js'; export class MapSearch { constructor() { this.searchCache = new Map(); this.tempMarker = null; } /** * Search for addresses using the geocoding API * @param {string} query - The search query * @returns {Promise} Array of search results */ async search(query) { if (!query || query.trim().length < 2) { return []; } const trimmedQuery = query.trim(); // Check cache first if (this.searchCache.has(trimmedQuery)) { return this.searchCache.get(trimmedQuery); } try { const response = await fetch(`/api/geocode/search?query=${encodeURIComponent(trimmedQuery)}&limit=5`); if (!response.ok) { throw new Error(`Search failed: ${response.statusText}`); } const data = await response.json(); if (!data.success) { throw new Error(data.error || 'Search failed'); } const results = data.data || []; // Cache the results this.searchCache.set(trimmedQuery, results); // Clean up cache if it gets too large if (this.searchCache.size > 100) { const firstKey = this.searchCache.keys().next().value; this.searchCache.delete(firstKey); } return results; } catch (error) { console.error('Map search error:', error); throw error; } } /** * Create HTML for a search result * @param {Object} result - Search result object * @returns {HTMLElement} Result element */ createResultElement(result) { const resultEl = document.createElement('div'); resultEl.className = 'search-result-item search-result-map'; // Handle both coordinate formats for compatibility const lat = result.coordinates?.lat || result.latitude || 0; const lng = result.coordinates?.lng || result.longitude || 0; // Debugging - log result structure if coordinates are missing if (!lat && !lng) { console.warn('Search result missing coordinates:', result); } resultEl.innerHTML = `
${result.formattedAddress || 'Unknown Address'}
${result.fullAddress || ''}
${lat.toFixed(6)}, ${lng.toFixed(6)}
`; resultEl.addEventListener('click', () => { this.selectResult(result); }); return resultEl; } /** * Handle selection of a search result * @param {Object} result - Selected result */ selectResult(result) { if (!map) { console.error('Map not initialized'); return; } // Handle both coordinate formats for compatibility const lat = parseFloat(result.coordinates?.lat || result.latitude || 0); const lng = parseFloat(result.coordinates?.lng || result.longitude || 0); if (isNaN(lat) || isNaN(lng)) { console.error('Invalid coordinates:', result); return; } // Pan and zoom to the location map.setView([lat, lng], 16); // Remove any existing temporary marker this.clearTempMarker(); // Add a temporary marker this.tempMarker = L.marker([lat, lng], { icon: L.divIcon({ className: 'temp-search-marker', html: '
', iconSize: [30, 30], iconAnchor: [15, 30] }) }).addTo(map); // Create popup content without inline handlers const popupContent = document.createElement('div'); popupContent.className = 'search-result-popup'; popupContent.innerHTML = `

${result.formattedAddress || 'Search Result'}

${result.fullAddress || ''}

`; // Bind the popup this.tempMarker.bindPopup(popupContent).openPopup(); // Add event listener after popup is opened setTimeout(() => { const addBtn = document.querySelector('.search-add-location-btn'); if (addBtn) { addBtn.addEventListener('click', (e) => { const btnLat = parseFloat(e.target.dataset.lat); const btnLng = parseFloat(e.target.dataset.lng); this.openAddLocationModal(btnLat, btnLng); }); } }, 100); } /** * Open the add location modal at specified coordinates * @param {number} lat - Latitude * @param {number} lng - Longitude */ openAddLocationModal(lat, lng) { this.clearTempMarker(); if (typeof openAddModal === 'function') { openAddModal(lat, lng); } else { // Fallback: trigger the add location button click const addBtn = document.getElementById('add-location-btn'); if (addBtn) { // Set a temporary flag for the coordinates window.tempSearchCoordinates = { lat, lng }; addBtn.click(); } } } /** * Clear the temporary search marker */ clearTempMarker() { if (this.tempMarker && map) { map.removeLayer(this.tempMarker); this.tempMarker = null; } } /** * Clear the search cache */ clearCache() { this.searchCache.clear(); } } // Create a global instance for use in popup buttons window.mapSearchInstance = new MapSearch(); export default window.mapSearchInstance;