391 lines
13 KiB
JavaScript
391 lines
13 KiB
JavaScript
// Location management (CRUD operations)
|
|
import { map } from './map-manager.js';
|
|
import { showStatus, updateLocationCount, escapeHtml } from './utils.js';
|
|
import { currentUser } from './auth.js';
|
|
import { resetAddressConfirmation } from './ui-controls.js';
|
|
|
|
export let markers = [];
|
|
export let currentEditingLocation = null;
|
|
|
|
export async function loadLocations() {
|
|
try {
|
|
const response = await fetch('/api/locations');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
displayLocations(data.locations);
|
|
updateLocationCount(data.locations.length);
|
|
} else {
|
|
throw new Error(data.error || 'Failed to load locations');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading locations:', error);
|
|
showStatus('Failed to load locations', 'error');
|
|
}
|
|
}
|
|
|
|
export function displayLocations(locations) {
|
|
// Clear existing markers
|
|
markers.forEach(marker => {
|
|
if (marker && map) {
|
|
map.removeLayer(marker);
|
|
}
|
|
});
|
|
markers = [];
|
|
|
|
// Add new markers
|
|
locations.forEach(location => {
|
|
if (location.latitude && location.longitude) {
|
|
const marker = createLocationMarker(location);
|
|
if (marker) {
|
|
markers.push(marker);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log(`Displayed ${markers.length} locations`);
|
|
}
|
|
|
|
function createLocationMarker(location) {
|
|
if (!map) {
|
|
console.warn('Map not initialized, skipping marker creation');
|
|
return null;
|
|
}
|
|
|
|
// Try to get coordinates from multiple possible sources
|
|
let lat, lng;
|
|
|
|
// First try the Geo-Location field
|
|
if (location['Geo-Location']) {
|
|
const coords = location['Geo-Location'].split(';');
|
|
if (coords.length === 2) {
|
|
lat = parseFloat(coords[0]);
|
|
lng = parseFloat(coords[1]);
|
|
}
|
|
}
|
|
|
|
// If that didn't work, try latitude/longitude fields
|
|
if ((!lat || !lng) && location.latitude && location.longitude) {
|
|
lat = parseFloat(location.latitude);
|
|
lng = parseFloat(location.longitude);
|
|
}
|
|
|
|
// Validate coordinates
|
|
if (!lat || !lng || isNaN(lat) || isNaN(lng)) {
|
|
console.warn('Invalid coordinates for location:', location);
|
|
return null;
|
|
}
|
|
|
|
// Determine marker color based on support level
|
|
let markerColor = '#3388ff'; // Default blue
|
|
if (location['Support Level']) {
|
|
const level = parseInt(location['Support Level']);
|
|
switch(level) {
|
|
case 1: markerColor = '#27ae60'; break; // Green
|
|
case 2: markerColor = '#f1c40f'; break; // Yellow
|
|
case 3: markerColor = '#e67e22'; break; // Orange
|
|
case 4: markerColor = '#e74c3c'; break; // Red
|
|
}
|
|
}
|
|
|
|
// Create circle marker with explicit styling
|
|
const marker = L.circleMarker([lat, lng], {
|
|
radius: 8,
|
|
fillColor: markerColor,
|
|
color: '#ffffff',
|
|
weight: 2,
|
|
opacity: 1,
|
|
fillOpacity: 0.8,
|
|
className: 'location-marker' // Add a class for CSS targeting
|
|
});
|
|
|
|
// Add to map
|
|
marker.addTo(map);
|
|
|
|
const popupContent = createPopupContent(location);
|
|
marker.bindPopup(popupContent);
|
|
marker._locationData = location;
|
|
|
|
console.log(`Created marker at ${lat}, ${lng} with color ${markerColor}`);
|
|
|
|
return marker;
|
|
}
|
|
|
|
function createPopupContent(location) {
|
|
const locationId = location.Id || location.id || location.ID || location._id;
|
|
|
|
const name = [location['First Name'], location['Last Name']]
|
|
.filter(Boolean).join(' ') || 'Unknown';
|
|
const address = location.Address || 'No address';
|
|
const supportLevel = location['Support Level'] ?
|
|
`Level ${location['Support Level']}` : 'Not specified';
|
|
|
|
return `
|
|
<div class="popup-content">
|
|
<h3>${escapeHtml(name)}</h3>
|
|
<p><strong>Address:</strong> ${escapeHtml(address)}</p>
|
|
<p><strong>Support:</strong> ${escapeHtml(supportLevel)}</p>
|
|
${location.Sign ? '<p>🏁 Has campaign sign</p>' : ''}
|
|
${location.Notes ? `<p><strong>Notes:</strong> ${escapeHtml(location.Notes)}</p>` : ''}
|
|
<div class="popup-meta">
|
|
<p>ID: ${locationId || 'Unknown'}</p>
|
|
</div>
|
|
${currentUser ? `
|
|
<div class="popup-actions">
|
|
<button class="btn btn-primary btn-sm edit-location-popup-btn"
|
|
data-location='${escapeHtml(JSON.stringify(location))}'>
|
|
✏️ Edit
|
|
</button>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
export async function handleAddLocation(e) {
|
|
e.preventDefault();
|
|
|
|
// Check if address is confirmed
|
|
const { getAddressConfirmationState } = await import('./ui-controls.js');
|
|
const { isAddressConfirmed } = getAddressConfirmationState();
|
|
if (!isAddressConfirmed) {
|
|
showStatus('Please confirm the address before saving the location', 'warning');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData(e.target);
|
|
const data = {};
|
|
|
|
// Convert form data to object
|
|
for (let [key, value] of formData.entries()) {
|
|
// Map form field names to NocoDB column names
|
|
if (key === 'latitude') data.latitude = value.trim();
|
|
else if (key === 'longitude') data.longitude = value.trim();
|
|
else if (key === 'Geo-Location') data['Geo-Location'] = value.trim();
|
|
else if (value.trim() !== '') {
|
|
data[key] = value.trim();
|
|
}
|
|
}
|
|
|
|
// Ensure geo-location is set
|
|
if (data.latitude && data.longitude) {
|
|
data['Geo-Location'] = `${data.latitude};${data.longitude}`;
|
|
}
|
|
|
|
// Handle checkbox
|
|
data.Sign = document.getElementById('sign').checked;
|
|
|
|
try {
|
|
const response = await fetch('/api/locations', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
showStatus('Location added successfully!', 'success');
|
|
closeAddModal();
|
|
loadLocations();
|
|
} else {
|
|
throw new Error(result.error || 'Failed to add location');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error adding location:', error);
|
|
showStatus(error.message || 'Failed to add location', 'error');
|
|
}
|
|
}
|
|
|
|
export function openEditForm(location) {
|
|
currentEditingLocation = location;
|
|
|
|
// Extract ID - check multiple possible field names
|
|
const locationId = location.Id || location.id || location.ID || location._id;
|
|
|
|
if (!locationId) {
|
|
console.error('No ID found in location object. Available fields:', Object.keys(location));
|
|
showStatus('Error: Location ID not found. Check console for details.', 'error');
|
|
return;
|
|
}
|
|
|
|
// Reset address confirmation state
|
|
resetAddressConfirmation('edit');
|
|
|
|
// Store the ID in a data attribute for later use
|
|
document.getElementById('edit-location-id').value = locationId;
|
|
document.getElementById('edit-location-id').setAttribute('data-location-id', locationId);
|
|
|
|
// Populate form fields
|
|
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-phone').value = location.Phone || '';
|
|
document.getElementById('edit-location-unit').value = location['Unit Number'] || '';
|
|
document.getElementById('edit-support-level').value = location['Support Level'] || '';
|
|
document.getElementById('edit-location-address').value = location.Address || '';
|
|
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-notes').value = location.Notes || '';
|
|
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'] || '';
|
|
|
|
// Show edit footer
|
|
document.getElementById('edit-footer').classList.remove('hidden');
|
|
}
|
|
|
|
export function closeEditForm() {
|
|
document.getElementById('edit-footer').classList.add('hidden');
|
|
currentEditingLocation = null;
|
|
}
|
|
|
|
export async function handleEditLocation(e) {
|
|
e.preventDefault();
|
|
|
|
if (!currentEditingLocation) return;
|
|
|
|
// Check if address is confirmed
|
|
const { getAddressConfirmationState } = await import('./ui-controls.js');
|
|
const { isEditAddressConfirmed } = getAddressConfirmationState();
|
|
if (!isEditAddressConfirmed) {
|
|
showStatus('Please confirm the address before saving changes', 'warning');
|
|
return;
|
|
}
|
|
|
|
// Get the stored location ID
|
|
const locationIdElement = document.getElementById('edit-location-id');
|
|
const locationId = locationIdElement.getAttribute('data-location-id') || locationIdElement.value;
|
|
|
|
if (!locationId || locationId === 'undefined') {
|
|
showStatus('Error: Location ID not found', 'error');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData(e.target);
|
|
const data = {};
|
|
|
|
// Convert form data to object
|
|
for (let [key, value] of formData.entries()) {
|
|
// Skip the ID field
|
|
if (key === 'id' || key === 'Id' || key === 'ID') continue;
|
|
|
|
if (value !== null && value !== undefined) {
|
|
data[key] = value.trim();
|
|
}
|
|
}
|
|
|
|
// Ensure geo-location is set
|
|
if (data.latitude && data.longitude) {
|
|
data['Geo-Location'] = `${data.latitude};${data.longitude}`;
|
|
}
|
|
|
|
// Handle checkbox
|
|
data.Sign = document.getElementById('edit-sign').checked;
|
|
|
|
try {
|
|
const response = await fetch(`/api/locations/${locationId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let result;
|
|
|
|
try {
|
|
result = JSON.parse(responseText);
|
|
} catch (e) {
|
|
console.error('Failed to parse response:', responseText);
|
|
throw new Error(`Server response error: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
if (result.success) {
|
|
showStatus('Location updated successfully!', 'success');
|
|
closeEditForm();
|
|
loadLocations();
|
|
} else {
|
|
throw new Error(result.error || 'Failed to update location');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating location:', error);
|
|
showStatus(`Update failed: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
export async function handleDeleteLocation() {
|
|
if (!currentEditingLocation) return;
|
|
|
|
// Get the stored location ID
|
|
const locationIdElement = document.getElementById('edit-location-id');
|
|
const locationId = locationIdElement.getAttribute('data-location-id') || locationIdElement.value;
|
|
|
|
if (!locationId || locationId === 'undefined') {
|
|
showStatus('Error: Location ID not found', 'error');
|
|
return;
|
|
}
|
|
|
|
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 (result.success) {
|
|
showStatus('Location deleted successfully!', 'success');
|
|
closeEditForm();
|
|
loadLocations();
|
|
} else {
|
|
throw new Error(result.error || 'Failed to delete location');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting location:', error);
|
|
showStatus(error.message || 'Failed to delete location', 'error');
|
|
}
|
|
}
|
|
|
|
export function closeAddModal() {
|
|
const modal = document.getElementById('add-modal');
|
|
modal.classList.add('hidden');
|
|
document.getElementById('location-form').reset();
|
|
}
|
|
|
|
export function openAddModal(lat, lng) {
|
|
const modal = document.getElementById('add-modal');
|
|
const latInput = document.getElementById('location-lat');
|
|
const lngInput = document.getElementById('location-lng');
|
|
const geoInput = document.getElementById('geo-location');
|
|
|
|
// Reset address confirmation state
|
|
resetAddressConfirmation('add');
|
|
|
|
// Set coordinates
|
|
latInput.value = lat.toFixed(8);
|
|
lngInput.value = lng.toFixed(8);
|
|
geoInput.value = `${lat.toFixed(8)};${lng.toFixed(8)}`;
|
|
|
|
// Clear other fields
|
|
document.getElementById('location-form').reset();
|
|
latInput.value = lat.toFixed(8);
|
|
lngInput.value = lng.toFixed(8);
|
|
geoInput.value = `${lat.toFixed(8)};${lng.toFixed(8)}`;
|
|
|
|
// Show modal
|
|
modal.classList.remove('hidden');
|
|
|
|
// Trigger custom event for auto address lookup
|
|
const autoLookupEvent = new CustomEvent('autoAddressLookup', {
|
|
detail: { mode: 'add', lat, lng }
|
|
});
|
|
document.dispatchEvent(autoLookupEvent);
|
|
}
|