Finish green pool report submission

Also start the pattern of breaking out pool pages together in their own
file. I think its easier to read this way.
This commit is contained in:
Eli Ribble 2026-01-09 19:43:19 +00:00
parent 9680fb6a68
commit 01ed2d6086
No known key found for this signature in database
31 changed files with 5925 additions and 375 deletions

View file

@ -66,12 +66,16 @@ function displaySuggestions(suggestions) {
}
// Handle click on a suggestion
item.addEventListener('click', function() {
addressInput.value = suggestion.properties.name || suggestion.properties.full_address;
// Hide the suggestions container
setLocationInputs(suggestion);
suggestionsContainer.classList.add('d-none');
// Display the selected location details
displaySelectedLocation(suggestion);
setMapMarker(suggestion.geometry.coordinates);
const locationSelected = new CustomEvent("locationselected", {
detail: {
coordinates: suggestion.geometry.coordinates,
},
});
suggestionsContainer.dispatchEvent(locationSelected);
});
suggestionsContainer.appendChild(item);
@ -155,18 +159,45 @@ function onAddressInput() {
}, 300);
}
function setLocationInputs(suggestion) {
let address = document.getElementById('address');
let country = document.getElementById('address-country');
let latitude = document.getElementById('latitude');
let longitude = document.getElementById('longitude');
let latlngAccuracyType = document.getElementById('latlng-accuracy-type');
let postcode = document.getElementById('address-postcode');
let place = document.getElementById('address-place');
let region = document.getElementById('address-region');
let street = document.getElementById('address-street');
// Extract context data from properties
const props = suggestion.properties;
const context = props.context || {};
// Populate structured fields
address.value = props.full_address;
country.value = context.country.name;
latitude.value = props.coordinates.latitude;
longitude.value = props.coordinates.longitude;
latlngAccuracyType.value = props.coordinates.accuracy;
postcode.value = context.postcode.name;
place.value = context.place.name;
region.value = context.region.name;
street.value = context.country.name;
}
document.addEventListener('DOMContentLoaded', function() {
const addressInput = document.getElementById('addressInput');
const address = document.getElementById('address');
const suggestionsContainer = document.getElementById('suggestions');
const locationDetails = document.getElementById('locationDetails');
// Listen for input changes
addressInput.addEventListener('input', onAddressInput);
address.addEventListener('input', onAddressInput);
// Close suggestions when clicking outside
document.addEventListener('click', function(event) {
if (!addressInput.contains(event.target) && !suggestionsContainer.contains(event.target)) {
if (!address.contains(event.target) && !suggestionsContainer.contains(event.target)) {
suggestionsContainer.classList.add('d-none');
}
});

View file

@ -1,9 +1,18 @@
{{define "location-geocode"}}
<!-- Hidden fields for location data -->
<input type="hidden" id="address-country" name="address-country"/>
<input type="hidden" id="address-postcode" name="address-postcode"/>
<input type="hidden" id="address-place" name="address-place"/>
<input type="hidden" id="address-region" name="address-region"/>
<input type="hidden" id="address-street" name="address-street"/>
<input type="hidden" id="latitude" name="latitude"/>
<input type="hidden" id="longitude" name="longitude"/>
<input type="hidden" id="latlng-accuracy-type" name="latlng-accuracy-type"/>
<input type="hidden" id="latlng-accuracy-value" name="latlng-accuracy-value"/>
<div class="col-md-6">
<h2 class="mb-4">Address Search</h2>
<div class="mb-3 position-relative">
<label for="addressInput" class="form-label">Enter address</label>
<input type="text" class="form-control" id="addressInput"
<input type="text" class="form-control" id="address" name="address"
placeholder="Start typing an address (min 3 characters)">
<div id="suggestions" class="suggestions-container list-group d-none"></div>
</div>

View file

@ -1,144 +1,2 @@
{{define "map-header"}}
<script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
<script>
var map = null;
var markers = [];
function setMapMarker(coords) {
console.log("Setting map marker", coords);
map.jumpTo({
center: coords,
zoom: 14,
});
markers.forEach((marker) => marker.remove());
addMarker(coords);
}
function addMarker(coords) {
const marker = new mapboxgl.Marker({
color: "#FF0000",
draggable: true
}).setLngLat(coords).addTo(map);
marker.on('dragend', onMapMarkerDragEnd(marker));
markers.push(marker);
}
function onMapMarkerDragEnd(marker) {
return function() {
const lngLat = marker.getLngLat();
displaySelectedCoordinates(lngLat);
reverseGeocode(lngLat);
}
}
function displaySelectedCoordinates(lngLat) {
const gpsDisplay = document.getElementById("gps-display");
gpsDisplay.classList.remove('d-none');
longitude.textContent = lngLat.lng;
latitude.textContent = lngLat.lat;
}
async function reverseGeocode(lngLat) {
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
const url = `https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lngLat.lng}&latitude=${lngLat.lat}&access_token=${MAPBOX_ACCESS_TOKEN}`
const response = await fetch(url);
const data = await response.json();
console.log("reverse geocoded to", data);
if (data.features.length == 0) {
console.warn("No results for reverse geocode");
return;
}
const match = data.features[0];
displaySelectedLocation(match);
}
function onLoadMap() {
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
console.log("Setting up the map...");
mapboxgl.accessToken = MAPBOX_ACCESS_TOKEN;
map = new mapboxgl.Map({
container: "map",
center: {
lat: 36.2,
lng: -119.2
},
style: 'mapbox://styles/mapbox/streets-v12', // style URL
zoom: 15,
});
map.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true,
showUserHeading: true
}));
map.on("load", function() {
console.log("Map post-load...");
updateMapWithLocation(map);
console.log("Map post-load done.");
});
console.log("Map init done.");
}
function updateMapWithLocation(map) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
// on success
function(position) {
console.log("Got location", position);
map.jumpTo({
center: {
lng: position.coords.longitude,
lat: position.coords.latitude,
},
zoom: 14,
});
addMarker([
position.coords.longitude,
position.coords.latitude,
]);
},
// on error
function(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log("permission denied");
break;
case error.POSITION_UNAVAILABLE:
console.log("location unavailable");
break;
case error.TIMEOUT:
console.log("request timed out");
break;
}
},
// options
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
} else {
console.log("location is not supported");
}
}
window.addEventListener("load", onLoadMap);
</script>
<style>
.map-container {
background-color: #e9ecef;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
height: 500px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
}
#map {
height: 500px;
width:100%;
margin-bottom: 10px;
}
#map img {
max-width: none;
min-width: 0px;
height: auto;
}
</style>
{{end}}

View file

@ -1,5 +1,4 @@
{{define "photo-upload"}}
<label for="photos" class="form-label fw-bold">Photos (Optional)</label>
<div class="photo-upload-area">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-camera mb-2" viewBox="0 0 16 16">
<path d="M15 12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.172a3 3 0 0 0 2.12-.879l.83-.828A1 1 0 0 1 6.827 3h2.344a1 1 0 0 1 .707.293l.828.828A3 3 0 0 0 12.828 5H14a1 1 0 0 1 1 1v6zM2 4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-1.172a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 9.172 2H6.828a2 2 0 0 0-1.414.586l-.828.828A2 2 0 0 1 3.172 4H2z"/>