2026-01-24 21:40:37 +00:00
{{template "base.html" .}}
2026-01-24 21:52:54 +00:00
{{define "title"}}Standing Water{{end}}
2026-01-24 21:40:37 +00:00
{{define "extraheader"}}
< script src = 'https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js' > < / script >
< script src = "/static/js/address-display.js" > < / script >
< script src = "/static/js/address-suggestion.js" > < / script >
< script src = "/static/js/geocode.js" > < / script >
< script src = "/static/js/location.js" > < / script >
< script src = "/static/js/map-locator.js" > < / script >
{{template "photo-upload-header"}}
< style >
.district-logo {
max-height: 80px;
width: auto;
}
.form-section {
margin-bottom: 2.5rem;
padding-bottom: 2rem;
border-bottom: 1px solid #dee2e6;
}
.form-section:last-child {
border-bottom: none;
margin-bottom: 1rem;
padding-bottom: 0;
}
.photo-upload-area {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 20px;
text-align: center;
margin-bottom: 20px;
background-color: #f9f9f9;
}
.photo-preview {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
}
.photo-preview img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 4px;
}
.section-heading {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
}
.section-heading i {
margin-right: 10px;
font-size: 1.5rem;
color: #0d6efd;
}
.submit-container {
background-color: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-top: 2rem;
}
< / style >
< script >
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
function handlePhotoSelection() {
const photoInput = document.getElementById('photos');
const photoPreviewContainer = document.getElementById('photoPreviewContainer');
// Clear previous previews
photoPreviewContainer.innerHTML = '';
// Check if files were selected
if (photoInput.files & & photoInput.files.length > 0) {
// Loop through selected files
Array.from(photoInput.files).forEach((file, index) => {
console.log("Handling", index, file);
if (!file.type.match('image.*')) {
console.log("Skipping non-image file", file.type);
return; // Skip non-image files
}
// Create preview container
const previewContainer = document.createElement('div');
previewContainer.className = 'position-relative m-1';
// Create image preview
const img = document.createElement('img');
img.className = 'img-thumbnail';
img.style.width = '100px';
img.style.height = '100px';
img.style.objectFit = 'cover';
// Read file and set preview
const reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result;
};
reader.readAsDataURL(file);
// Create remove button
const removeBtn = document.createElement('button');
removeBtn.type = 'button';
removeBtn.className = 'btn btn-sm btn-danger position-absolute top-0 end-0';
removeBtn.innerHTML = '× ';
removeBtn.style.fontSize = '10px';
removeBtn.style.padding = '0 5px';
// Handle remove button click
removeBtn.addEventListener('click', function() {
// Create a new FileList without this file
// Since FileList is immutable, we need to reset the input
// This is a bit tricky and requires recreating the input
previewContainer.remove();
// If this was the last image, clear the input entirely
if (photoPreviewContainer.children.length === 0) {
photoInput.value = '';
}
// Note: Unfortunately, selectively removing files from a FileList isn't straightforward
// In a real implementation, we might track selected files in an array and recreate the input
});
// Add elements to the preview container
previewContainer.appendChild(img);
previewContainer.appendChild(removeBtn);
photoPreviewContainer.appendChild(previewContainer);
});
}
}
function setLocationInputs(location) {
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 = location.properties;
const context = props.context || {};
// Populate structured fields
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;
}
2026-01-24 21:50:28 +00:00
function toggleCollapse(something) {
el = document.getElementById(something)
if (el.classList.contains('collapse')) {
el.classList.remove('collapse');
} else {
el.classList.add('collapse');
}
2026-01-30 15:55:23 +00:00
document.getElementById("toggle-additional").classList.add("visually-hidden");
2026-01-24 21:50:28 +00:00
}
2026-01-24 21:40:37 +00:00
document.addEventListener('DOMContentLoaded', function() {
// Elements
const photoInput = document.getElementById('photos');
// Handle photo selection
photoInput.addEventListener('change', handlePhotoSelection);
// Handle drag and drop
const photoDropArea = document.getElementById('photoDropArea');
photoDropArea.addEventListener('dragover', function(e) {
e.preventDefault();
photoDropArea.style.backgroundColor = '#e9ecef';
});
photoDropArea.addEventListener('dragleave', function() {
photoDropArea.style.backgroundColor = '#f8f9fa';
});
photoDropArea.addEventListener('drop', function(e) {
e.preventDefault();
photoDropArea.style.backgroundColor = '#f8f9fa';
if (e.dataTransfer.files.length) {
handleFiles(e.dataTransfer.files);
}
});
const mapLocator = document.querySelector("map-locator");
mapLocator.addEventListener("load", (event) => {
getGeolocation({
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}).then(position => {
mapLocator.jumpTo({
center: {
lng: position.coords.longitude,
lat: position.coords.latitude,
},
zoom: 14,
});
mapLocator.setMarker([
position.coords.longitude,
position.coords.latitude,
]);
geocodeReverse(MAPBOX_ACCESS_TOKEN, {
lat: position.coords.latitude,
lng: position.coords.longitude,
});
}).catch(error => {
console.log("location error", error);
})
})
let mapZoom = document.getElementById('map-zoom');
mapLocator.addEventListener("zoomend", function(e) {
mapZoom.value = e.target.getZoom();
});
mapLocator.addEventListener("markerdragend", (e) => {
const lngLat = marker.getLngLat();
//displaySelectedCoordinates(lngLat);
geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat);
});
const addressDisplay = document.querySelector("address-display");
const addressInput = document.querySelector("address-input");
addressInput.addEventListener("address-selected", (event) => {
const l = event.detail.location;
console.log("Address selected", l);
// Center map on selected address
mapSetMarker(l.geometry.coordinates);
mapJumpTo({
center: l.geometry.coordinates,
zoom: 14,
});
addressDisplay.show(l);
setLocationInputs(l);
});
});
function displaySelectedCoordinates(lngLat) {
const gpsDisplay = document.getElementById("gps-display");
gpsDisplay.classList.remove('d-none');
longitude.textContent = lngLat.lng;
latitude.textContent = lngLat.lat;
}
< / script >
{{end}}
{{define "content"}}
{{if .District}}
{{template "header" .}}
{{end}}
<!-- Main Content -->
< main class = "py-5" >
< div class = "container" >
<!-- Page Title -->
< div class = "row mb-4" >
< div class = "col-12" >
< h2 > Report Standing Water< / h2 >
< p class = "lead" > Help us locate and treat potential mosquito production sources in your area< / p >
< / div >
< / div >
<!-- Report Form -->
2026-01-24 21:47:24 +00:00
< form id = "standingWater" action = "/water-submit" method = "POST" enctype = "multipart/form-data" >
2026-01-24 21:40:37 +00:00
<!-- Photo Upload Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-camera" > < / i >
< h3 > Photos< / h3 >
< / div >
2026-01-24 21:47:24 +00:00
< p class = "mb-3" > Photos help us identify the severity of the issue and may contain location data that can help us find the production source.< / p >
2026-01-24 21:40:37 +00:00
< div class = "mb-4" >
{{template "photo-upload"}}
< / div >
< / div >
2026-01-24 21:47:24 +00:00
<!-- Additional Information Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-card-text" > < / i >
< h3 > Additional Information< / h3 >
< / div >
< p class = "mb-3" > Please provide any other information that might help us address this mosquito production source.< / p >
< div class = "row" >
< div class = "col-md-12" >
< label for = "comments" class = "form-label" > Additional Details< / label >
< textarea class = "form-control" id = "comments" name = "comments" rows = "4" placeholder = "Example: The house appears to be vacant. There is algae growth in the pool. I've noticed increased mosquito activity in the evenings." > < / textarea >
< / div >
< / div >
< / div >
2026-01-24 21:40:37 +00:00
<!-- Location Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-geo-alt" > < / i >
< h3 > Location< / h3 >
< / div >
< p class = "mb-3" > Please provide the location of the potential mosquito production source. We may be able to extract this information from your photos if they contain location data.< / p >
2026-01-30 15:55:23 +00:00
< div class = "col-md-12" >
< div class = "alert alert-info" role = "info" >
< p class = "mb-0" > You can select the location by address or by moving the marker on the map.< / p >
< / div >
< / div >
2026-01-24 21:40:37 +00:00
< div class = "row mb-3" >
<!-- 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" >
< div class = "mb-3 position-relative" >
< address-input
placeholder="Start typing an address (min 3 characters)"
api-key="{{ .MapboxToken }}">
< / address-input >
< / div >
< / div >
< / div >
< p class = "small text-muted mb-2" > You can also click on the map to mark the location precisely< / p >
< map-locator api-key = "{{ .MapboxToken }}" > < / map-locator >
< input type = "hidden" id = "map-zoom" name = "map-zoom" / >
< / div >
2026-01-30 15:55:23 +00:00
< button id = "toggle-additional" class = "btn btn-warning" type = "button" onClick = "toggleCollapse('collapse-additional-fields')" >
Click here to answer a few more questions to better help us solve your mosquito problem
2026-01-24 21:50:28 +00:00
< / button >
< div class = "collapse" id = "collapse-additional-fields" >
<!-- Source Details Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-water" > < / i >
< h3 > Source Details< / h3 >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< div class = "row mb-4" >
< div class = "col-md-6" >
< label for = "duration" class = "form-label" > How long has this production source been present?< / label >
< select class = "form-select" id = "duration" name = "source-duration" >
< option value = "none" > I don't know< / option >
< option value = "less-than-week" > Less than a week< / option >
< option value = "1-2-weeks" > 1-2 weeks< / option >
< option value = "2-4-weeks" > 2-4 weeks< / option >
< option value = "1-3-months" > 1-3 months< / option >
< option value = "more-than-3-months" > More than 3 months< / option >
< / select >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< div class = "col-md-6" >
< label class = "form-label d-block" > Have you observed any of the following? < a href = "#" data-bs-toggle = "modal" data-bs-target = "#larvaeInfoModal" > < i class = "bi bi-question-circle small ms-1" > < / i > < / a > < / label >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "larvae" name = "has-larvae" >
< label class = "form-check-label" for = "larvae" >
Larvae (wigglers) in water
< / label >
< / div >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "pupae" name = "has-pupae" >
< label class = "form-check-label" for = "pupae" >
Pupae (tumblers) in water
< / label >
< / div >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "adult" name = "has-adult" >
< label class = "form-check-label" for = "adult" >
Adult mosquitoes near the source
< / label >
< / div >
2026-01-24 21:40:37 +00:00
< / div >
< / div >
< / div >
2026-01-24 21:50:28 +00:00
<!-- Access Information Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-unlock" > < / i >
< h3 > Access Information< / h3 >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< p class = "mb-3" > Please provide any details about how to access the mosquito source. This helps our technicians when they visit the site.< / p >
< div class = "row mb-3" >
< div class = "col-md-12" >
< label for = "access-comments" class = "form-label" > How can the source be accessed?< / label >
< textarea class = "form-control" id = "access-comments" name = "access-comments" rows = "3" placeholder = "Example: The pool is in the backyard, which can be accessed through a side gate on the right side of the house." > < / textarea >
< / div >
< / div >
< div class = "row mb-3" >
< div class = "col-md-12" >
< label class = "form-label d-block" > Access obstacles (check all that apply):< / label >
< div class = "row" >
< div class = "col-md-4" >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "gate" name = "access-gate" >
< label class = "form-check-label" for = "gate" > Gate< / label >
< / div >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "fence" name = "access-fence" >
< label class = "form-check-label" for = "fence" > Fence< / label >
< / div >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< div class = "col-md-4" >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "locked" name = "access-locked" >
< label class = "form-check-label" for = "locked" > Locked entrance< / label >
< / div >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "dogs" name = "access-dog" >
< label class = "form-check-label" for = "dogs" > Dogs/pets< / label >
< / div >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< div class = "col-md-4" >
< div class = "form-check" >
< input class = "form-check-input" type = "checkbox" id = "access-other" name = "access-other" >
< label class = "form-check-label" for = "access-other" > Other obstacle< / label >
< / div >
2026-01-24 21:40:37 +00:00
< / div >
< / div >
< / div >
< / div >
< / div >
2026-01-24 21:50:28 +00:00
<!-- Contact Information Sections -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-person-lines-fill" > < / i >
2026-01-30 16:14:52 +00:00
< h3 > Property Owner Information (if known)< / h3 >
2026-01-24 21:40:37 +00:00
< / div >
2026-01-24 21:50:28 +00:00
< div class = "row mb-4" >
2026-01-30 16:14:52 +00:00
< div class = "col-md-4 mb-3" >
2026-01-24 21:50:28 +00:00
< label for = "owner-name" class = "form-label" > Owner Name< / label >
< input type = "text" class = "form-control" id = "owner-name" name = "owner-name" >
< / div >
2026-01-30 16:14:52 +00:00
< div class = "col-md-4 mb-3" >
2026-01-24 21:50:28 +00:00
< label for = "owner-phone" class = "form-label" > Owner Phone< / label >
< input type = "tel" class = "form-control" id = "owner-phone" name = "owner-phone" >
< / div >
2026-01-30 16:14:52 +00:00
< div class = "col-md-4 mb-3" >
2026-01-24 21:50:28 +00:00
< label for = "owner-email" class = "form-label" > Owner Email< / label >
< input type = "email" class = "form-control" id = "owner-email" name = "owner-email" >
< / div >
2026-01-30 16:14:52 +00:00
< / div >
< div class = "row mb-4" >
2026-01-30 16:10:17 +00:00
< div class = "col-md-6 mb-3 row" >
< div class = "form-check mt-4" >
2026-01-30 16:14:52 +00:00
< input type = "checkbox" class = "form-check-input" name = "property-ownership" >
2026-01-30 16:10:17 +00:00
< label class = "form-check-label" for = "property-ownership" > This is my property< / label >
< / div >
2026-01-30 16:14:52 +00:00
< div class = "form-check mt-4" >
< input type = "checkbox" class = "form-check-input" name = "" >
< label class = "form-check-label" for = "property-ownership" > I grant permission to enter the back yard of this property.< / label >
< / div >
2026-01-30 16:10:17 +00:00
< / div >
2026-01-24 21:40:37 +00:00
< / div >
< / div >
< / div >
<!-- Submit Section -->
< div class = "submit-container" >
< div class = "row align-items-center" >
< div class = "col-md-8" >
< p class = "mb-0" > < strong > Thank you for helping us keep our community safe from mosquito-borne illnesses.< / strong > < / p >
< p class = "mb-0 small text-muted" > After submission, you will receive a confirmation with a report ID for tracking purposes.< / p >
< / div >
< div class = "col-md-4 text-md-end mt-3 mt-md-0" >
2026-01-24 21:55:10 +00:00
< a class = "btn btn-primary btn-lg" href = "{{.URL.NuisanceSubmitComplete}}" >
2026-01-24 21:40:37 +00:00
Submit Report
2026-01-24 21:55:10 +00:00
< / a >
2026-01-24 21:40:37 +00:00
< / div >
< / div >
< / div >
< / form >
< / div >
< / main >
<!-- Larvae Info Modal -->
< div class = "modal fade" id = "larvaeInfoModal" tabindex = "-1" aria-labelledby = "larvaeInfoModalLabel" aria-hidden = "true" >
< div class = "modal-dialog modal-lg" >
< div class = "modal-content" >
< div class = "modal-header" >
< h5 class = "modal-title" id = "larvaeInfoModalLabel" > How to Identify Mosquito Larvae and Pupae< / h5 >
< button type = "button" class = "btn-close" data-bs-dismiss = "modal" aria-label = "Close" > < / button >
< / div >
< div class = "modal-body" >
< div class = "row mb-4" >
< div class = "col-md-6" >
< h6 > Mosquito Larvae (Wigglers)< / h6 >
< p > Mosquito larvae, often called "wigglers," are:< / p >
< ul >
< li > Small, worm-like aquatic organisms< / li >
< li > Usually 1/4 to 1/2 inch long< / li >
< li > Move with a wiggling motion in water< / li >
< li > Hang upside-down at the water surface to breathe< / li >
< li > Visible to the naked eye in standing water< / li >
< / ul >
< / div >
< div class = "col-md-6" >
< h6 > Mosquito Pupae (Tumblers)< / h6 >
< p > Mosquito pupae, often called "tumblers," are:< / p >
< ul >
< li > Comma-shaped organisms< / li >
< li > Typically darker than larvae< / li >
< li > Move with a tumbling motion when disturbed< / li >
< li > Rest at the water surface< / li >
< li > The stage just before adult mosquitoes emerge< / li >
< / ul >
< / div >
< / div >
< p > When looking for mosquito larvae and pupae, check standing water sources like:< / p >
< ul >
< li > Swimming pools< / li >
< li > Bird baths< / li >
< li > Buckets or containers< / li >
< li > Drainage ditches< / li >
< li > Plant saucers< / li >
< li > Rain gutters< / li >
< / ul >
< p > If you see small creatures moving in standing water, there's a good chance they're mosquito larvae or pupae.< / p >
< div class = "text-center" >
< a href = "#" class = "btn btn-outline-primary" > View Detailed Identification Guide< / a >
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-bs-dismiss = "modal" > Close< / button >
< / div >
< / div >
< / div >
< / div >
{{end}}