2026-01-07 21:47:33 +00:00
{{template "base.html" .}}
{{define "title"}}Green Pool{{end}}
{{define "extraheader"}}
2026-01-08 22:21:59 +00:00
{{template "location-geocode-header" .}}
2026-01-09 19:43:19 +00:00
< 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' / >
2026-01-08 21:29:33 +00:00
{{template "photo-upload-header"}}
2026-01-07 21:47:33 +00:00
< style >
2026-01-08 23:20:41 +00:00
.district-logo {
max-height: 80px;
width: auto;
}
.map-container {
height: 500px;
background-color: #e9ecef;
border-radius: 5px;
border: 1px solid #dee2e6;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.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;
}
2026-01-09 19:43:19 +00:00
.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;
}
.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;
}
2026-01-08 23:20:41 +00:00
.section-heading {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
}
.section-heading i {
margin-right: 10px;
font-size: 1.5rem;
color: #0d6efd;
}
.optional-label {
font-size: 0.875rem;
color: #6c757d;
font-weight: normal;
margin-left: 8px;
}
.submit-container {
background-color: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-top: 2rem;
}
2026-01-07 21:47:33 +00:00
< / style >
2026-01-09 19:43:19 +00:00
< script >
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);
});
}
}
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);
}
});
onLoadMap();
const suggestionsContainer = document.getElementById('suggestions');
suggestionsContainer.addEventListener("locationselected", (e) => {
setMapMarker(e.detail.coordinates);
});
});
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);
setLocationInputs(match);
}
function onLoadMap() {
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
let mapZoom = document.getElementById('map-zoom');
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.addControl(new mapboxgl.NavigationControl());
map.on("load", function() {
console.log("Map post-load...");
updateMapWithLocation(map);
console.log("Map post-load done.");
});
map.on("zoomend", function(e) {
mapZoom.value = e.target.getZoom();
});
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,
]);
reverseGeocode({
lat: position.coords.latitude,
lng: position.coords.longitude,
});
},
// 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");
}
}
< / script >
2026-01-07 21:47:33 +00:00
{{end}}
{{define "content"}}
<!-- Main Content -->
< main class = "py-5" >
< div class = "container" >
<!-- Page Title -->
< div class = "row mb-4" >
< div class = "col-12" >
< h2 > Report a Green Pool or Mosquito Source< / h2 >
< p class = "lead" > Help us locate and treat potential mosquito breeding sources in your area< / p >
< / div >
< / div >
<!-- Info Alert -->
< div class = "row mb-4" >
< div class = "col-12" >
< div class = "alert alert-info" role = "alert" >
< h5 class = "alert-heading" > < i class = "bi bi-info-circle me-2" > < / i > All fields are optional< / h5 >
< p class = "mb-0" > We appreciate any information you can provide. The more details you share, the better we can address the issue. Photos and location information are especially helpful.< / p >
< / div >
< / div >
< / div >
<!-- Report Form -->
2026-01-09 19:43:19 +00:00
< form id = "greenPoolForm" action = "/pool-submit" method = "POST" enctype = "multipart/form-data" >
2026-01-07 21:47:33 +00:00
<!-- Photo Upload Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-camera" > < / i >
< h3 > Photos< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
< p class = "mb-3" > Photos help us identify the severity of the issue and may contain location data that can help us find the source.< / p >
2026-01-09 19:43:19 +00:00
< div class = "mb-4" >
{{template "photo-upload"}}
< / div >
2026-01-07 21:47:33 +00:00
< / div >
<!-- Location Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-geo-alt" > < / i >
< h3 > Location< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
< p class = "mb-3" > Please provide the location of the potential mosquito breeding source. We may be able to extract this information from your photos if they contain location data.< / p >
< div class = "row mb-3" >
< div class = "col-md-12" >
2026-01-08 22:21:59 +00:00
{{template "location-geocode"}}
2026-01-07 21:47:33 +00:00
< / div >
2026-01-08 23:38:38 +00:00
< div class = "col-md-12 d-none" id = "gps-display" >
< div class = "location-detail" >
< div class = "detail-label" > Longitude< / div >
< div id = "longitude" class = "detail-value" > -< / div >
< / div >
< div class = "location-detail" >
< div class = "detail-label" > Latitude< / div >
< div id = "latitude" class = "detail-value" > -< / div >
< / div >
< / div >
2026-01-07 21:47:33 +00:00
< / div >
< p class = "small text-muted mb-2" > You can also click on the map to mark the location precisely< / p >
< div class = "map-container" >
2026-01-08 23:20:41 +00:00
< div id = "map" > < / div >
2026-01-07 21:47:33 +00:00
< / div >
2026-01-09 19:43:19 +00:00
< input type = "hidden" id = "map-zoom" name = "map-zoom" / >
2026-01-07 21:47:33 +00:00
< / div >
<!-- Source Details Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-water" > < / i >
< h3 > Source Details< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
< div class = "row mb-4" >
< div class = "col-md-6" >
< label for = "duration" class = "form-label" > How long has this source been present?< / label >
2026-01-09 19:43:19 +00:00
< select class = "form-select" id = "duration" name = "source-duration" >
< option value = "none" > I don't know< / option >
2026-01-07 21:47:33 +00:00
< 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 >
< / div >
< 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" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "larvae" name = "has-larvae" >
2026-01-07 21:47:33 +00:00
< label class = "form-check-label" for = "larvae" >
Larvae (wigglers) in water
< / label >
< / div >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "pupae" name = "has-pupae" >
2026-01-07 21:47:33 +00:00
< label class = "form-check-label" for = "pupae" >
Pupae (tumblers) in water
< / label >
< / div >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "adult" name = "has-adult" >
< label class = "form-check-label" for = "adult" >
2026-01-07 21:47:33 +00:00
Adult mosquitoes near the source
< / label >
< / div >
< / div >
< / div >
< / div >
<!-- Access Information Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-unlock" > < / i >
< h3 > Access Information< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
< 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" >
2026-01-09 19:43:19 +00:00
< 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 >
2026-01-07 21:47:33 +00:00
< / 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" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "gate" name = "access-gate" >
2026-01-07 21:47:33 +00:00
< label class = "form-check-label" for = "gate" > Gate< / label >
< / div >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "fence" name = "access-fence" >
2026-01-07 21:47:33 +00:00
< label class = "form-check-label" for = "fence" > Fence< / label >
< / div >
< / div >
< div class = "col-md-4" >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "locked" name = "access-locked" >
< label class = "form-check-label" for = "locked" > Locked entrance< / label >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "dogs" name = "access-dog" >
2026-01-07 21:47:33 +00:00
< label class = "form-check-label" for = "dogs" > Dogs/pets< / label >
< / div >
< / div >
< div class = "col-md-4" >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "access-other" name = "access-other" >
< label class = "form-check-label" for = "access-other" > Other obstacle< / label >
2026-01-07 21:47:33 +00:00
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Contact Information Sections -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-person-lines-fill" > < / i >
< h3 > Contact Information< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
<!-- Property Owner Information -->
< h5 class = "mb-3" > Property Owner Information (if known)< / h5 >
< div class = "row mb-4" >
< div class = "col-md-6 mb-3" >
2026-01-09 19:43:19 +00:00
< label for = "owner-name" class = "form-label" > Owner Name< / label >
< input type = "text" class = "form-control" id = "owner-name" name = "owner-name" >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "col-md-6 mb-3" >
2026-01-09 19:43:19 +00:00
< label for = "owner-phone" class = "form-label" > Owner Phone< / label >
< input type = "tel" class = "form-control" id = "owner-phone" name = "owner-phone" >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "col-md-12" >
2026-01-09 19:43:19 +00:00
< label for = "owner-email" class = "form-label" > Owner Email< / label >
< input type = "email" class = "form-control" id = "owner-email" name = "owner-email" >
2026-01-07 21:47:33 +00:00
< / div >
< / div >
<!-- Your Contact Information -->
< h5 class = "mb-3" > Your Contact Information (for updates)< / h5 >
< div class = "row mb-3" >
< div class = "col-md-6 mb-3" >
2026-01-09 19:43:19 +00:00
< label for = "reporter-name" class = "form-label" > Your Name< / label >
< input type = "text" class = "form-control" id = "reporter-name" name = "reporter-name" >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "col-md-6 mb-3" >
2026-01-09 19:43:19 +00:00
< label for = "reporter-phone" class = "form-label" > Your Phone< / label >
< input type = "tel" class = "form-control" id = "reporter-phone" name = "reporter-phone" >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "col-md-12 mb-3" >
2026-01-09 19:43:19 +00:00
< label for = "reporter-email" class = "form-label" > Your Email< / label >
< input type = "email" class = "form-control" id = "reporter-email" name = "reporter-email" >
2026-01-07 21:47:33 +00:00
< / div >
< div class = "col-md-12" >
< div class = "form-check" >
2026-01-09 19:43:19 +00:00
< input class = "form-check-input" type = "checkbox" id = "subscribe" name = "subscribe" checked >
< label class = "form-check-label" for = "subscribe" >
2026-01-07 21:47:33 +00:00
I would like to receive updates on this report
< / label >
< / div >
< / div >
< / div >
< / div >
<!-- Additional Information Section -->
< div class = "form-section" >
< div class = "section-heading" >
< i class = "bi bi-card-text" > < / i >
< h3 > Additional Information< / h3 >
< span class = "optional-label" > optional< / span >
< / div >
< p class = "mb-3" > Please provide any other information that might help us address this mosquito source.< / p >
< div class = "row" >
< div class = "col-md-12" >
2026-01-09 19:43:19 +00:00
< 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 >
2026-01-07 21:47:33 +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" >
< button type = "submit" class = "btn btn-primary btn-lg" >
Submit Report
< / button >
< / div >
< / div >
< / div >
< / form >
<!-- Back Button -->
< div class = "row mt-4" >
< div class = "col-12" >
< a href = "/" class = "btn btn-outline-secondary" >
< i class = "bi bi-arrow-left" > < / i > Back to Home
< / a >
< / div >
< / div >
< / 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}}