// Global configuration
const CONFIG = {
DEFAULT_LAT: parseFloat(document.querySelector('meta[name="default-lat"]')?.content) || 53.5461,
DEFAULT_LNG: parseFloat(document.querySelector('meta[name="default-lng"]')?.content) || -113.4938,
DEFAULT_ZOOM: parseInt(document.querySelector('meta[name="default-zoom"]')?.content) || 11,
REFRESH_INTERVAL: 30000, // 30 seconds
MAX_ZOOM: 19,
MIN_ZOOM: 2
};
// Application state
let map = null;
let markers = [];
let userLocationMarker = null;
let isAddingLocation = false;
let refreshInterval = null;
let currentEditingLocation = null;
let currentUser = null; // Add current user state
// Initialize application when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
initializeMap();
checkAuthentication(); // Add authentication check
loadLocations();
setupEventListeners();
checkConfiguration();
// Set up auto-refresh
refreshInterval = setInterval(loadLocations, CONFIG.REFRESH_INTERVAL);
// Add event delegation for dynamically created edit buttons
document.addEventListener('click', function(e) {
if (e.target.classList.contains('edit-location-btn')) {
const locationId = e.target.getAttribute('data-location-id');
editLocation(locationId);
}
});
});
// Initialize Leaflet map
function initializeMap() {
// Create map instance
map = L.map('map', {
center: [CONFIG.DEFAULT_LAT, CONFIG.DEFAULT_LNG],
zoom: CONFIG.DEFAULT_ZOOM,
zoomControl: true,
attributionControl: true
});
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: CONFIG.MAX_ZOOM,
minZoom: CONFIG.MIN_ZOOM
}).addTo(map);
// Add scale control
L.control.scale({
position: 'bottomleft',
metric: true,
imperial: false
}).addTo(map);
// Hide loading overlay
document.getElementById('loading').classList.add('hidden');
}
// Set up event listeners
function setupEventListeners() {
// Geolocation button
document.getElementById('geolocate-btn').addEventListener('click', handleGeolocation);
// Add location button
document.getElementById('add-location-btn').addEventListener('click', toggleAddLocation);
// Refresh button
document.getElementById('refresh-btn').addEventListener('click', () => {
showStatus('Refreshing locations...', 'info');
loadLocations();
});
// Fullscreen button
document.getElementById('fullscreen-btn').addEventListener('click', toggleFullscreen);
// Form submission
document.getElementById('location-form').addEventListener('submit', handleLocationSubmit);
// Edit form submission
document.getElementById('edit-location-form').addEventListener('submit', handleEditLocationSubmit);
// Map click handler for adding locations
map.on('click', handleMapClick);
// Set up geo field synchronization
setupGeoFieldSync();
// Add event listeners for buttons that were using inline onclick
document.getElementById('close-edit-footer-btn').addEventListener('click', closeEditFooter);
document.getElementById('lookup-address-edit-btn').addEventListener('click', lookupAddressForEdit);
document.getElementById('delete-location-btn').addEventListener('click', deleteLocation);
document.getElementById('close-modal-btn').addEventListener('click', closeModal);
document.getElementById('lookup-address-add-btn').addEventListener('click', lookupAddressForAdd);
document.getElementById('cancel-modal-btn').addEventListener('click', closeModal);
}
// Helper function to get color based on support level
function getSupportColor(supportLevel) {
const level = parseInt(supportLevel);
switch(level) {
case 1: return '#27ae60'; // Green - Strong support
case 2: return '#f1c40f'; // Yellow - Moderate support
case 3: return '#e67e22'; // Orange - Low support
case 4: return '#e74c3c'; // Red - No support
default: return '#95a5a6'; // Grey - Unknown/null
}
}
// Helper function to get support level text
function getSupportLevelText(level) {
const levelNum = parseInt(level);
switch(levelNum) {
case 1: return '1 - Strong Support';
case 2: return '2 - Moderate Support';
case 3: return '3 - Low Support';
case 4: return '4 - No Support';
default: return 'Not Specified';
}
}
// Set up geo field synchronization
function setupGeoFieldSync() {
const latInput = document.getElementById('location-lat');
const lngInput = document.getElementById('location-lng');
const geoLocationInput = document.getElementById('geo-location');
// Validate geo-location format
function validateGeoLocation(value) {
if (!value) return false;
// Check both formats
const patterns = [
/^-?\d+\.?\d*\s*,\s*-?\d+\.?\d*$/, // comma-separated
/^-?\d+\.?\d*\s*;\s*-?\d+\.?\d*$/ // semicolon-separated
];
return patterns.some(pattern => pattern.test(value));
}
// When lat/lng change, update geo-location
function updateGeoLocation() {
const lat = parseFloat(latInput.value);
const lng = parseFloat(lngInput.value);
if (!isNaN(lat) && !isNaN(lng)) {
geoLocationInput.value = `${lat};${lng}`; // Use semicolon format for NocoDB
geoLocationInput.classList.remove('invalid');
geoLocationInput.classList.add('valid');
}
}
// When geo-location changes, parse and update lat/lng
function parseGeoLocation() {
const geoValue = geoLocationInput.value.trim();
if (!geoValue) {
geoLocationInput.classList.remove('valid', 'invalid');
return;
}
if (!validateGeoLocation(geoValue)) {
geoLocationInput.classList.add('invalid');
geoLocationInput.classList.remove('valid');
return;
}
// Try semicolon-separated first
let parts = geoValue.split(';');
if (parts.length === 2) {
const lat = parseFloat(parts[0].trim());
const lng = parseFloat(parts[1].trim());
if (!isNaN(lat) && !isNaN(lng)) {
latInput.value = lat.toFixed(8);
lngInput.value = lng.toFixed(8);
// Keep semicolon format for NocoDB GeoData
geoLocationInput.value = `${lat};${lng}`;
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
return;
}
}
// Try comma-separated
parts = geoValue.split(',');
if (parts.length === 2) {
const lat = parseFloat(parts[0].trim());
const lng = parseFloat(parts[1].trim());
if (!isNaN(lat) && !isNaN(lng)) {
latInput.value = lat.toFixed(8);
lngInput.value = lng.toFixed(8);
// Normalize to semicolon format for NocoDB GeoData
geoLocationInput.value = `${lat};${lng}`;
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
}
}
}
// Add event listeners
latInput.addEventListener('input', updateGeoLocation);
lngInput.addEventListener('input', updateGeoLocation);
geoLocationInput.addEventListener('blur', parseGeoLocation);
geoLocationInput.addEventListener('input', () => {
// Clear validation classes on input to allow real-time feedback
const geoValue = geoLocationInput.value.trim();
if (geoValue && validateGeoLocation(geoValue)) {
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
} else if (geoValue) {
geoLocationInput.classList.add('invalid');
geoLocationInput.classList.remove('valid');
} else {
geoLocationInput.classList.remove('valid', 'invalid');
}
});
}
// Check authentication and display user info
async function checkAuthentication() {
try {
const response = await fetch('/api/auth/check');
const data = await response.json();
if (data.authenticated && data.user) {
currentUser = data.user;
displayUserInfo();
}
} catch (error) {
console.error('Failed to check authentication:', error);
}
}
// Display user info in header
function displayUserInfo() {
const headerActions = document.querySelector('.header-actions');
// Create user info element
const userInfo = document.createElement('div');
userInfo.className = 'user-info';
userInfo.innerHTML = `
${escapeHtml(currentUser.email)}
`;
// Insert before the location count
const locationCount = document.getElementById('location-count');
headerActions.insertBefore(userInfo, locationCount);
// Add logout event listener
document.getElementById('logout-btn').addEventListener('click', handleLogout);
}
// Handle logout
async function handleLogout() {
if (!confirm('Are you sure you want to logout?')) {
return;
}
try {
const response = await fetch('/api/auth/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
window.location.href = '/login.html';
} else {
showStatus('Logout failed. Please try again.', 'error');
}
} catch (error) {
console.error('Logout error:', error);
showStatus('Logout failed. Please try again.', 'error');
}
}
// Check API configuration
async function checkConfiguration() {
try {
const response = await fetch('/api/config-check');
const data = await response.json();
if (!data.configured) {
showStatus('Warning: API not fully configured. Check your .env file.', 'warning');
}
} catch (error) {
console.error('Configuration check failed:', error);
}
}
// Load locations from API
async function loadLocations() {
try {
const response = await fetch('/api/locations');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success) {
displayLocations(data.locations);
updateLocationCount(data.count);
} else {
throw new Error(data.error || 'Failed to load locations');
}
} catch (error) {
console.error('Error loading locations:', error);
showStatus('Failed to load locations. Check your connection.', 'error');
updateLocationCount(0);
}
}
// Display locations on map
function displayLocations(locations) {
// Clear existing markers
markers.forEach(marker => map.removeLayer(marker));
markers = [];
// Add new markers
locations.forEach(location => {
if (location.latitude && location.longitude) {
const marker = createLocationMarker(location);
markers.push(marker);
}
});
// Fit map to show all markers if there are any
if (markers.length > 0) {
const group = new L.featureGroup(markers);
map.fitBounds(group.getBounds().pad(0.1));
}
}
// Create marker for location (updated to use circle markers)
function createLocationMarker(location) {
console.log('Creating marker for location:', location);
// Get color based on support level
const supportColor = getSupportColor(location['Support Level']);
// Create circle marker instead of default marker
const marker = L.circleMarker([location.latitude, location.longitude], {
radius: 8,
fillColor: supportColor,
color: '#fff',
weight: 2,
opacity: 1,
fillOpacity: 0.8,
title: location.title || 'Location',
riseOnHover: true,
locationData: location // Store location data in marker options
}).addTo(map);
// Add larger radius on hover
marker.on('mouseover', function() {
this.setRadius(10);
});
marker.on('mouseout', function() {
this.setRadius(8);
});
// Create popup content
const popupContent = createPopupContent(location);
marker.bindPopup(popupContent);
return marker;
}
// Create popup content for marker
function createPopupContent(location) {
console.log('Creating popup for location:', location);
let content = '
';
return content;
}
// Handle geolocation
function handleGeolocation() {
if (!navigator.geolocation) {
showStatus('Geolocation is not supported by your browser', 'error');
return;
}
showStatus('Getting your location...', 'info');
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude, accuracy } = position.coords;
// Center map on user location
map.setView([latitude, longitude], 15);
// Remove existing user marker
if (userLocationMarker) {
map.removeLayer(userLocationMarker);
}
// Add user location marker
userLocationMarker = L.marker([latitude, longitude], {
icon: L.divIcon({
html: '',
className: 'user-location-marker',
iconSize: [20, 20],
iconAnchor: [10, 10]
}),
title: 'Your location'
}).addTo(map);
// Add accuracy circle
L.circle([latitude, longitude], {
radius: accuracy,
color: '#2c5aa0',
fillColor: '#2c5aa0',
fillOpacity: 0.1,
weight: 1
}).addTo(map);
showStatus(`Location found (±${Math.round(accuracy)}m accuracy)`, '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
}
);
}
// Toggle add location mode
function toggleAddLocation() {
isAddingLocation = !isAddingLocation;
const btn = document.getElementById('add-location-btn');
const crosshair = document.getElementById('crosshair');
if (isAddingLocation) {
btn.innerHTML = '✕Cancel';
btn.classList.remove('btn-success');
btn.classList.add('btn-secondary');
crosshair.classList.remove('hidden');
map.getContainer().style.cursor = 'crosshair';
} else {
btn.innerHTML = '➕Add Location Here';
btn.classList.remove('btn-secondary');
btn.classList.add('btn-success');
crosshair.classList.add('hidden');
map.getContainer().style.cursor = '';
}
}
// Handle map click
function handleMapClick(e) {
if (!isAddingLocation) return;
const { lat, lng } = e.latlng;
// Toggle off add location mode
toggleAddLocation();
// Show modal with coordinates
showAddLocationModal(lat, lng);
}
// Show add location modal
function showAddLocationModal(lat, lng) {
const modal = document.getElementById('add-modal');
const latInput = document.getElementById('location-lat');
const lngInput = document.getElementById('location-lng');
const geoLocationInput = document.getElementById('geo-location');
// Set coordinates
latInput.value = lat.toFixed(8);
lngInput.value = lng.toFixed(8);
// Set geo-location field
geoLocationInput.value = `${lat.toFixed(8)};${lng.toFixed(8)}`; // Use semicolon format for NocoDB
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
// Clear other fields
document.getElementById('first-name').value = '';
document.getElementById('last-name').value = '';
document.getElementById('location-email').value = '';
document.getElementById('location-unit').value = '';
document.getElementById('support-level').value = '';
const addressInput = document.getElementById('location-address');
addressInput.value = 'Looking up address...'; // Show loading message
document.getElementById('sign').checked = false;
document.getElementById('sign-size').value = '';
// Show modal
modal.classList.remove('hidden');
// Fetch address asynchronously
reverseGeocode(lat, lng).then(result => {
if (result) {
addressInput.value = result.formattedAddress || result.fullAddress;
} else {
addressInput.value = ''; // Clear if lookup fails
// Don't show warning for automatic lookups
}
}).catch(error => {
console.error('Address lookup failed:', error);
addressInput.value = '';
});
// Focus on first name input
setTimeout(() => {
document.getElementById('first-name').focus();
}, 100);
}
// Close modal
function closeModal() {
document.getElementById('add-modal').classList.add('hidden');
}
// Edit location function
function editLocation(locationId) {
// Find the location in markers data
const location = markers.find(m => {
const data = m.options.locationData;
return String(data.id || data.Id) === String(locationId);
})?.options.locationData;
if (!location) {
console.error('Location not found for ID:', locationId);
console.log('Available locations:', markers.map(m => ({
id: m.options.locationData.id || m.options.locationData.Id,
name: m.options.locationData['First Name'] + ' ' + m.options.locationData['Last Name']
})));
showStatus('Location not found', 'error');
return;
}
currentEditingLocation = location;
// Populate all the edit form fields
document.getElementById('edit-location-id').value = location.id || location.Id || '';
document.getElementById('edit-first-name').value = location['First Name'] || '';
document.getElementById('edit-last-name').value = location['Last Name'] || '';
document.getElementById('edit-location-email').value = location['Email'] || '';
document.getElementById('edit-location-unit').value = location['Unit Number'] || '';
document.getElementById('edit-support-level').value = location['Support Level'] || '';
const addressInput = document.getElementById('edit-location-address');
addressInput.value = location['Address'] || '';
// If no address exists, try to fetch it
if (!location['Address'] && location.latitude && location.longitude) {
addressInput.value = 'Looking up address...';
reverseGeocode(location.latitude, location.longitude).then(result => {
if (result && !location['Address']) {
addressInput.value = result.formattedAddress || result.fullAddress;
} else if (!location['Address']) {
addressInput.value = '';
// Don't show error - just silently fail
}
}).catch(error => {
// Handle any unexpected errors
console.error('Address lookup failed:', error);
addressInput.value = '';
});
}
// Handle checkbox
document.getElementById('edit-sign').checked = location['Sign'] === true || location['Sign'] === 'true' || location['Sign'] === 1;
document.getElementById('edit-sign-size').value = location['Sign Size'] || '';
document.getElementById('edit-location-lat').value = location.latitude || '';
document.getElementById('edit-location-lng').value = location.longitude || '';
document.getElementById('edit-geo-location').value = location['Geo-Location'] || `${location.latitude};${location.longitude}`;
// Show the edit footer
document.getElementById('edit-footer').classList.remove('hidden');
document.getElementById('map-container').classList.add('edit-mode');
// Invalidate map size after showing footer
setTimeout(() => map.invalidateSize(), 300);
// Setup geo field sync for edit form
setupEditGeoFieldSync();
}
// Close edit footer
function closeEditFooter() {
document.getElementById('edit-footer').classList.add('hidden');
document.getElementById('map-container').classList.remove('edit-mode');
currentEditingLocation = null;
// Invalidate map size after hiding footer
setTimeout(() => map.invalidateSize(), 300);
}
// Setup geo field sync for edit form
function setupEditGeoFieldSync() {
const latInput = document.getElementById('edit-location-lat');
const lngInput = document.getElementById('edit-location-lng');
const geoLocationInput = document.getElementById('edit-geo-location');
// Similar to setupGeoFieldSync but for edit form
function updateGeoLocation() {
const lat = parseFloat(latInput.value);
const lng = parseFloat(lngInput.value);
if (!isNaN(lat) && !isNaN(lng)) {
geoLocationInput.value = `${lat};${lng}`;
geoLocationInput.classList.remove('invalid');
geoLocationInput.classList.add('valid');
}
}
function parseGeoLocation() {
const geoValue = geoLocationInput.value.trim();
if (!geoValue) {
geoLocationInput.classList.remove('valid', 'invalid');
return;
}
// Try semicolon-separated first
let parts = geoValue.split(';');
if (parts.length === 2) {
const lat = parseFloat(parts[0].trim());
const lng = parseFloat(parts[1].trim());
if (!isNaN(lat) && !isNaN(lng)) {
latInput.value = lat.toFixed(8);
lngInput.value = lng.toFixed(8);
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
return;
}
}
// Try comma-separated
parts = geoValue.split(',');
if (parts.length === 2) {
const lat = parseFloat(parts[0].trim());
const lng = parseFloat(parts[1].trim());
if (!isNaN(lat) && !isNaN(lng)) {
latInput.value = lat.toFixed(8);
lngInput.value = lng.toFixed(8);
geoLocationInput.value = `${lat};${lng}`;
geoLocationInput.classList.add('valid');
geoLocationInput.classList.remove('invalid');
}
}
}
latInput.addEventListener('input', updateGeoLocation);
lngInput.addEventListener('input', updateGeoLocation);
geoLocationInput.addEventListener('blur', parseGeoLocation);
}
// Handle edit form submission
async function handleEditLocationSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
const locationId = data.id;
// Ensure Geo-Location field is included
const geoLocationInput = document.getElementById('edit-geo-location');
if (geoLocationInput.value) {
data['Geo-Location'] = geoLocationInput.value;
}
try {
const response = await fetch(`/api/locations/${locationId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok && result.success) {
showStatus('Location updated successfully!', 'success');
closeEditFooter();
// Reload locations
loadLocations();
} else {
throw new Error(result.error || 'Failed to update location');
}
} catch (error) {
console.error('Error updating location:', error);
showStatus(error.message, 'error');
}
}
// Delete location
async function deleteLocation() {
if (!currentEditingLocation) return;
const locationId = currentEditingLocation.id || currentEditingLocation.Id;
if (!confirm('Are you sure you want to delete this location?')) {
return;
}
try {
const response = await fetch(`/api/locations/${locationId}`, {
method: 'DELETE'
});
const result = await response.json();
if (response.ok && result.success) {
showStatus('Location deleted successfully!', 'success');
closeEditFooter();
// Reload locations
loadLocations();
} else {
throw new Error(result.error || 'Failed to delete location');
}
} catch (error) {
console.error('Error deleting location:', error);
showStatus(error.message, 'error');
}
}
// Handle location form submission
async function handleLocationSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
// Validate required fields - either first name or last name should be provided
if ((!data['First Name'] || !data['First Name'].trim()) &&
(!data['Last Name'] || !data['Last Name'].trim())) {
showStatus('Either First Name or Last Name is required', 'error');
return;
}
// Ensure Geo-Location field is included
const geoLocationInput = document.getElementById('geo-location');
if (geoLocationInput.value) {
data['Geo-Location'] = geoLocationInput.value;
}
try {
const response = await fetch('/api/locations', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok && result.success) {
showStatus('Location added successfully!', 'success');
closeModal();
// Reload locations
loadLocations();
// Center map on new location
map.setView([data.latitude, data.longitude], map.getZoom());
} else {
throw new Error(result.error || 'Failed to add location');
}
} catch (error) {
console.error('Error adding location:', error);
showStatus(error.message, 'error');
}
}
// Toggle fullscreen
function toggleFullscreen() {
const app = document.getElementById('app');
const btn = document.getElementById('fullscreen-btn');
if (!document.fullscreenElement) {
app.requestFullscreen().then(() => {
app.classList.add('fullscreen');
btn.innerHTML = '✕Exit Fullscreen';
// Invalidate map size after transition
setTimeout(() => map.invalidateSize(), 300);
}).catch(err => {
showStatus('Unable to enter fullscreen', 'error');
});
} else {
document.exitFullscreen().then(() => {
app.classList.remove('fullscreen');
btn.innerHTML = '⛶Fullscreen';
// Invalidate map size after transition
setTimeout(() => map.invalidateSize(), 300);
});
}
}
// Update location count
function updateLocationCount(count) {
const countElement = document.getElementById('location-count');
countElement.textContent = `${count} location${count !== 1 ? 's' : ''}`;
}
// Show status message
function showStatus(message, type = 'info') {
const container = document.getElementById('status-container');
const messageDiv = document.createElement('div');
messageDiv.className = `status-message ${type}`;
messageDiv.textContent = message;
container.appendChild(messageDiv);
// Auto-remove after 5 seconds
setTimeout(() => {
messageDiv.remove();
}, 5000);
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
if (text === null || text === undefined) {
return '';
}
const div = document.createElement('div');
div.textContent = String(text);
return div.innerHTML;
}
// Handle window resize
window.addEventListener('resize', () => {
map.invalidateSize();
});
// Clean up on page unload
window.addEventListener('beforeunload', () => {
if (refreshInterval) {
clearInterval(refreshInterval);
}
});
// Reverse geocode to get address from coordinates
async function reverseGeocode(lat, lng) {
try {
const response = await fetch(`/api/geocode/reverse?lat=${lat}&lng=${lng}`);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Geocoding service unavailable');
}
const result = await response.json();
if (!result.success || !result.data) {
throw new Error('Geocoding failed');
}
return result.data;
} catch (error) {
console.error('Reverse geocoding error:', error);
return null;
}
}
// Add a new function for forward geocoding (address to coordinates)
async function forwardGeocode(address) {
try {
const response = await fetch(`/api/geocode/forward?address=${encodeURIComponent(address)}`);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Geocoding service unavailable');
}
const result = await response.json();
if (!result.success || !result.data) {
throw new Error('Geocoding failed');
}
return result.data;
} catch (error) {
console.error('Forward geocoding error:', error);
return null;
}
}
// Manual address lookup for add form
async function lookupAddressForAdd() {
const latInput = document.getElementById('location-lat');
const lngInput = document.getElementById('location-lng');
const addressInput = document.getElementById('location-address');
const lat = parseFloat(latInput.value);
const lng = parseFloat(lngInput.value);
if (!isNaN(lat) && !isNaN(lng)) {
addressInput.value = 'Looking up address...';
const result = await reverseGeocode(lat, lng);
if (result) {
addressInput.value = result.formattedAddress || result.fullAddress;
showStatus('Address found!', 'success');
} else {
addressInput.value = '';
showStatus('Could not find address for these coordinates', 'warning');
}
} else {
showStatus('Please enter valid coordinates first', 'warning');
}
}
// Manual address lookup for edit form
async function lookupAddressForEdit() {
const latInput = document.getElementById('edit-location-lat');
const lngInput = document.getElementById('edit-location-lng');
const addressInput = document.getElementById('edit-location-address');
const lat = parseFloat(latInput.value);
const lng = parseFloat(lngInput.value);
if (!isNaN(lat) && !isNaN(lng)) {
addressInput.value = 'Looking up address...';
const result = await reverseGeocode(lat, lng);
if (result) {
addressInput.value = result.formattedAddress || result.fullAddress;
showStatus('Address found!', 'success');
} else {
addressInput.value = '';
showStatus('Could not find address for these coordinates', 'warning');
}
} else {
showStatus('Please enter valid coordinates first', 'warning');
}
}