diff --git a/map/app/public/css/style.css b/map/app/public/css/style.css
index 140053b..0ff22ea 100644
--- a/map/app/public/css/style.css
+++ b/map/app/public/css/style.css
@@ -177,6 +177,28 @@ body {
background-color: #c0392b;
}
+/* Disabled button styles */
+.btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ background-color: #6c757d;
+}
+
+.btn:disabled:hover {
+ background-color: #6c757d;
+ transform: none;
+}
+
+.btn-primary:disabled {
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-primary:disabled:hover {
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
/* Crosshair for location selection */
.crosshair {
position: absolute;
diff --git a/map/app/public/index.html b/map/app/public/index.html
index 4d08825..4785ada 100644
--- a/map/app/public/index.html
+++ b/map/app/public/index.html
@@ -171,8 +171,8 @@
-
@@ -282,8 +282,8 @@
-
- 📍 Lookup Address
+
+ 📍 Confirm Address
@@ -336,7 +336,7 @@
Cancel
-
+
Save Location
diff --git a/map/app/public/js/location-manager.js b/map/app/public/js/location-manager.js
index c091257..91218c9 100644
--- a/map/app/public/js/location-manager.js
+++ b/map/app/public/js/location-manager.js
@@ -2,6 +2,7 @@
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;
@@ -144,6 +145,14 @@ function createPopupContent(location) {
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 = {};
@@ -202,6 +211,9 @@ export function openEditForm(location) {
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);
@@ -235,6 +247,14 @@ export async function handleEditLocation(e) {
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;
@@ -345,6 +365,9 @@ export function openAddModal(lat, lng) {
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);
diff --git a/map/app/public/js/ui-controls.js b/map/app/public/js/ui-controls.js
index 28113b8..3faf1aa 100644
--- a/map/app/public/js/ui-controls.js
+++ b/map/app/public/js/ui-controls.js
@@ -5,6 +5,16 @@ import { loadLocations, handleAddLocation, handleEditLocation, handleDeleteLocat
export let userLocationMarker = null;
export let isAddingLocation = false;
+export let isAddressConfirmed = false;
+export let isEditAddressConfirmed = false;
+
+// Export function to get current confirmation states
+export function getAddressConfirmationState() {
+ return {
+ isAddressConfirmed,
+ isEditAddressConfirmed
+ };
+}
// Add logout function
async function logout() {
@@ -192,19 +202,21 @@ export function toggleFullscreen() {
}
}
-export async function lookupAddress(mode) {
- let latInput, lngInput, addressInput;
+export async function confirmAddress(mode) {
+ let latInput, lngInput, addressInput, saveButton;
if (mode === 'add') {
latInput = document.getElementById('location-lat');
lngInput = document.getElementById('location-lng');
addressInput = document.getElementById('location-address');
+ saveButton = document.getElementById('save-location-btn');
} else if (mode === 'edit') {
latInput = document.getElementById('edit-location-lat');
lngInput = document.getElementById('edit-location-lng');
addressInput = document.getElementById('edit-location-address');
+ saveButton = document.getElementById('save-edit-location-btn'); // We'll need to add this ID to edit form
} else {
- console.error('Invalid lookup mode:', mode);
+ console.error('Invalid confirm mode:', mode);
return;
}
@@ -221,12 +233,37 @@ export async function lookupAddress(mode) {
return;
}
- // Show loading state
+ // Get the confirm button
const button = mode === 'add' ?
- document.getElementById('lookup-address-add-btn') :
- document.getElementById('lookup-address-edit-btn');
+ document.getElementById('confirm-address-add-btn') :
+ document.getElementById('confirm-address-edit-btn');
const originalText = button ? button.textContent : '';
+
+ // Check if already confirmed
+ const currentlyConfirmed = mode === 'add' ? isAddressConfirmed : isEditAddressConfirmed;
+
+ // If already confirmed, just enable save
+ if (currentlyConfirmed) {
+ if (mode === 'add') {
+ isAddressConfirmed = false;
+ } else {
+ isEditAddressConfirmed = false;
+ }
+
+ // Reset button and disable save
+ if (button) {
+ button.textContent = '📍 Confirm Address';
+ button.classList.remove('btn-success');
+ button.classList.add('btn-secondary');
+ }
+ if (saveButton) {
+ saveButton.disabled = true;
+ }
+ showStatus('Please confirm address again to enable saving', 'info');
+ return;
+ }
+
if (button) {
button.disabled = true;
button.textContent = 'Looking up...';
@@ -248,7 +285,27 @@ export async function lookupAddress(mode) {
const address = data.data.formattedAddress || data.data.fullAddress;
if (address) {
addressInput.value = address;
- showStatus('Address found!', 'success');
+
+ // Mark as confirmed
+ if (mode === 'add') {
+ isAddressConfirmed = true;
+ } else {
+ isEditAddressConfirmed = true;
+ }
+
+ // Update button to show confirmed state
+ if (button) {
+ button.textContent = '✅ Address Confirmed';
+ button.classList.remove('btn-secondary');
+ button.classList.add('btn-success');
+ }
+
+ // Enable save button
+ if (saveButton) {
+ saveButton.disabled = false;
+ }
+
+ showStatus('Address confirmed! You can now save the location.', 'success');
} else {
showStatus('No address found for these coordinates', 'warning');
}
@@ -260,14 +317,47 @@ export async function lookupAddress(mode) {
console.error('Address lookup error:', error);
showStatus(`Address lookup failed: ${error.message}`, 'error');
} finally {
- // Restore button state
- if (button) {
+ // Restore button state if not confirmed
+ const finallyConfirmed = mode === 'add' ? isAddressConfirmed : isEditAddressConfirmed;
+ if (button && !finallyConfirmed) {
button.disabled = false;
button.textContent = originalText;
}
}
}
+export function resetAddressConfirmation(mode) {
+ if (mode === 'add') {
+ isAddressConfirmed = false;
+ const button = document.getElementById('confirm-address-add-btn');
+ const saveButton = document.getElementById('save-location-btn');
+
+ if (button) {
+ button.textContent = '📍 Confirm Address';
+ button.classList.remove('btn-success');
+ button.classList.add('btn-secondary');
+ button.disabled = false;
+ }
+ if (saveButton) {
+ saveButton.disabled = true;
+ }
+ } else if (mode === 'edit') {
+ isEditAddressConfirmed = false;
+ const button = document.getElementById('confirm-address-edit-btn');
+ const saveButton = document.getElementById('save-edit-location-btn');
+
+ if (button) {
+ button.textContent = '📍 Confirm Address';
+ button.classList.remove('btn-success');
+ button.classList.add('btn-secondary');
+ button.disabled = false;
+ }
+ if (saveButton) {
+ saveButton.disabled = true;
+ }
+ }
+}
+
export function setupGeoLocationSync() {
// For add form
const addLatInput = document.getElementById('location-lat');
@@ -282,6 +372,8 @@ export function setupGeoLocationSync() {
if (lat && lng) {
addGeoInput.value = `${lat};${lng}`;
}
+ // Reset address confirmation when coordinates change
+ resetAddressConfirmation('add');
});
});
@@ -291,6 +383,8 @@ export function setupGeoLocationSync() {
addLatInput.value = coords.lat;
addLngInput.value = coords.lng;
}
+ // Reset address confirmation when geo-location changes
+ resetAddressConfirmation('add');
});
}
@@ -307,6 +401,8 @@ export function setupGeoLocationSync() {
if (lat && lng) {
editGeoInput.value = `${lat};${lng}`;
}
+ // Reset address confirmation when coordinates change
+ resetAddressConfirmation('edit');
});
});
@@ -316,6 +412,8 @@ export function setupGeoLocationSync() {
editLatInput.value = coords.lat;
editLngInput.value = coords.lng;
}
+ // Reset address confirmation when geo-location changes
+ resetAddressConfirmation('edit');
});
}
}
@@ -376,13 +474,13 @@ export function setupEventListeners() {
// Delete button
document.getElementById('delete-location-btn')?.addEventListener('click', handleDeleteLocation);
- // Address lookup buttons
- document.getElementById('lookup-address-add-btn')?.addEventListener('click', () => {
- lookupAddress('add');
+ // Address confirm buttons
+ document.getElementById('confirm-address-add-btn')?.addEventListener('click', () => {
+ confirmAddress('add');
});
- document.getElementById('lookup-address-edit-btn')?.addEventListener('click', () => {
- lookupAddress('edit');
+ document.getElementById('confirm-address-edit-btn')?.addEventListener('click', () => {
+ confirmAddress('edit');
});
// Auto address lookup event listener
@@ -391,7 +489,7 @@ export function setupEventListeners() {
if (mode === 'add') {
// Add a small delay to ensure the form is fully rendered
setTimeout(() => {
- lookupAddress('add');
+ confirmAddress('add');
}, 200);
}
});
diff --git a/map/files-explainer.md b/map/files-explainer.md
index 759fd1d..efefe38 100644
--- a/map/files-explainer.md
+++ b/map/files-explainer.md
@@ -112,7 +112,7 @@ Favicon for the web application.
# app/public/index.html
-Main map viewer HTML page for the canvassing application.
+Main map viewer HTML page for the canvassing application. Features "Confirm Address" functionality that requires users to confirm the geocoded address before saving location data.
# app/public/login.html
@@ -136,7 +136,7 @@ Global configuration constants for the frontend app.
# app/public/js/location-manager.js
-JavaScript for loading, displaying, and managing map locations on the frontend.
+JavaScript for loading, displaying, and managing map locations on the frontend. Includes address confirmation validation that prevents saving locations until the geocoded address is confirmed by the user.
# app/public/js/main.js
@@ -156,7 +156,7 @@ JavaScript for volunteer shift signup, management, and UI logic with both grid a
# app/public/js/ui-controls.js
-JavaScript for UI controls, event handlers, and user interaction logic.
+JavaScript for UI controls, event handlers, and user interaction logic. Includes address confirmation functionality that manages state for ensuring users confirm geocoded addresses before saving locations.
# app/public/js/utils.js