415 lines
14 KiB
JavaScript
415 lines
14 KiB
JavaScript
// 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;
|
||
|
||
// Add logout function
|
||
async function logout() {
|
||
try {
|
||
const response = await fetch('/api/auth/logout', {
|
||
method: 'POST',
|
||
credentials: 'include'
|
||
});
|
||
|
||
if (response.ok) {
|
||
// Clear any local storage or session storage
|
||
localStorage.clear();
|
||
sessionStorage.clear();
|
||
|
||
// Redirect to login page
|
||
window.location.href = '/login.html';
|
||
} else {
|
||
throw new Error('Logout failed');
|
||
}
|
||
} catch (error) {
|
||
console.error('Logout error:', error);
|
||
showStatus('Logout failed, redirecting anyway...', 'warning');
|
||
// Force redirect even if logout request fails
|
||
setTimeout(() => {
|
||
window.location.href = '/login.html';
|
||
}, 1000);
|
||
}
|
||
}
|
||
|
||
// Add logout handler function
|
||
function handleLogout(e) {
|
||
e.preventDefault();
|
||
if (confirm('Are you sure you want to logout?')) {
|
||
logout();
|
||
}
|
||
}
|
||
|
||
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('<strong>Your Location</strong>');
|
||
}
|
||
|
||
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 = '<span class="btn-icon">✕</span><span class="btn-text">Cancel</span>';
|
||
}
|
||
|
||
// 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 = '<span class="btn-icon">➕</span><span class="btn-text">Add Location Here</span>';
|
||
}
|
||
|
||
// 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 = '<span class="btn-icon">◱</span><span class="btn-text">Exit Fullscreen</span>';
|
||
}
|
||
|
||
// 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 = '<span class="btn-icon">⛶</span><span class="btn-text">Fullscreen</span>';
|
||
}
|
||
|
||
// 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);
|
||
|
||
// Add logout button event listeners
|
||
document.getElementById('logout-btn')?.addEventListener('click', handleLogout);
|
||
document.getElementById('mobile-logout-btn')?.addEventListener('click', handleLogout);
|
||
|
||
// 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');
|
||
});
|
||
|
||
// Auto address lookup event listener
|
||
document.addEventListener('autoAddressLookup', (e) => {
|
||
const { mode } = e.detail;
|
||
if (mode === 'add') {
|
||
// Add a small delay to ensure the form is fully rendered
|
||
setTimeout(() => {
|
||
lookupAddress('add');
|
||
}, 200);
|
||
}
|
||
});
|
||
|
||
// 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');
|
||
}
|
||
}
|
||
});
|
||
} |