Actually upload something on quick report page
We aren't handling it yet, but we do get the upload.
This commit is contained in:
parent
376d471b17
commit
462febe1d7
6 changed files with 465 additions and 91 deletions
|
|
@ -16,15 +16,19 @@ var EmbeddedStaticFS embed.FS
|
|||
type ContextNuisance struct{}
|
||||
type ContextPool struct{}
|
||||
type ContextQuick struct{}
|
||||
type ContextQuickSubmitComplete struct {
|
||||
ReportID string
|
||||
}
|
||||
type ContextRoot struct{}
|
||||
type ContextStatus struct{}
|
||||
|
||||
var (
|
||||
Nuisance = buildTemplate("nuisance", "base")
|
||||
Pool = buildTemplate("pool", "base")
|
||||
Quick = buildTemplate("quick", "base")
|
||||
Root = buildTemplate("root", "base")
|
||||
Status = buildTemplate("status", "base")
|
||||
Nuisance = buildTemplate("nuisance", "base")
|
||||
Pool = buildTemplate("pool", "base")
|
||||
Quick = buildTemplate("quick", "base")
|
||||
QuickSubmitComplete = buildTemplate("quick-submit-complete", "base")
|
||||
Root = buildTemplate("root", "base")
|
||||
Status = buildTemplate("status", "base")
|
||||
)
|
||||
|
||||
var components = [...]string{"footer"}
|
||||
|
|
|
|||
125
htmlpage/public-reports/template/quick-submit-complete.html
Normal file
125
htmlpage/public-reports/template/quick-submit-complete.html
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
{{template "base.html" .}}
|
||||
|
||||
{{define "title"}}Dash{{end}}
|
||||
{{define "extraheader"}}
|
||||
<style>
|
||||
</style>
|
||||
<script>
|
||||
</script>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<!-- Success Card -->
|
||||
<div class="card shadow-sm border-success mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="my-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-check-circle-fill me-2" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||
</svg>
|
||||
Report Successfully Submitted
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div class="text-center mb-4">
|
||||
<p class="lead">Thank you for helping us control mosquito populations in your area!</p>
|
||||
<div class="alert alert-info py-3">
|
||||
<strong>Your Report ID:</strong>
|
||||
<span class="fs-4 fw-bold">{{.ReportID}}</span>
|
||||
</div>
|
||||
<p class="text-muted">Please save this ID for your reference.</p>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- Status Check Section -->
|
||||
<div class="mb-4">
|
||||
<h4 class="mb-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-search me-2" viewBox="0 0 16 16">
|
||||
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
|
||||
</svg>
|
||||
Check Your Report Status
|
||||
</h4>
|
||||
<p>You can check the status of your report at any time using your Report ID.</p>
|
||||
<a href="/check-report-status" class="btn btn-outline-primary">
|
||||
Check Status
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- Notifications Section -->
|
||||
<div>
|
||||
<h4 class="mb-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-bell me-2" viewBox="0 0 16 16">
|
||||
<path d="M8 16a2 2 0 0 0 2-2H6a2 2 0 0 0 2 2zM8 1.918l-.797.161A4.002 4.002 0 0 0 4 6c0 .628-.134 2.197-.459 3.742-.16.767-.376 1.566-.663 2.258h10.244c-.287-.692-.502-1.49-.663-2.258C12.134 8.197 12 6.628 12 6a4.002 4.002 0 0 0-3.203-3.92L8 1.917zM14.22 12c.223.447.481.801.78 1H1c.299-.199.557-.553.78-1C2.68 10.2 3 6.88 3 6c0-2.42 1.72-4.44 4.005-4.901a1 1 0 1 1 1.99 0A5.002 5.002 0 0 1 13 6c0 .88.32 4.2 1.22 6z"/>
|
||||
</svg>
|
||||
Get Updates
|
||||
</h4>
|
||||
<p>Provide your contact information to receive updates about your report.</p>
|
||||
|
||||
<form id="notificationForm" action="/register-notifications" method="post" class="needs-validation" novalidate>
|
||||
<input type="hidden" name="reportId" value="{{.ReportID}}">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="email" class="form-control" id="email" name="email" placeholder="your@email.com">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">Phone Number (for SMS updates)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-phone" viewBox="0 0 16 16">
|
||||
<path d="M11 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6zM5 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H5z"/>
|
||||
<path d="M8 14a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="tel" class="form-control" id="phone" name="phone" placeholder="(123) 456-7890">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="consent" name="consent" required>
|
||||
<label class="form-check-label" for="consent">
|
||||
I consent to receiving updates about this report
|
||||
</label>
|
||||
<div class="invalid-feedback">
|
||||
You must consent to receive notifications.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Register for Updates</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="text-center">
|
||||
<a href="/" class="btn btn-outline-secondary me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house me-1" viewBox="0 0 16 16">
|
||||
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5ZM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5 5 5Z"/>
|
||||
</svg>
|
||||
Return to Home
|
||||
</a>
|
||||
<a href="/report-mosquito" class="btn btn-outline-success">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle me-1" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
|
||||
</svg>
|
||||
Submit Another Report
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -51,6 +51,184 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Elements
|
||||
const form = document.getElementById('mosquitoReportForm');
|
||||
const photoInput = document.getElementById('photos');
|
||||
const photoPreviewContainer = document.getElementById('photoPreviewContainer');
|
||||
const locationStatus = document.getElementById('locationStatus');
|
||||
const latitudeInput = document.getElementById('latitude');
|
||||
const longitudeInput = document.getElementById('longitude');
|
||||
const createdInput = document.getElementById('created');
|
||||
const submitButton = document.getElementById('submitButton');
|
||||
const loadingOverlay = document.getElementById('loadingOverlay');
|
||||
|
||||
// Get current location
|
||||
requestLocation();
|
||||
|
||||
// Set current time
|
||||
createdInput.value = new Date().toISOString();
|
||||
|
||||
// Handle photo selection
|
||||
photoInput.addEventListener('change', handlePhotoSelection);
|
||||
|
||||
// Handle form submission
|
||||
form.addEventListener('submit', handleFormSubmission);
|
||||
|
||||
/**
|
||||
* Request user's geolocation
|
||||
*/
|
||||
function requestLocation() {
|
||||
if (navigator.geolocation) {
|
||||
locationStatus.textContent = "Requesting your location...";
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
// Success callback
|
||||
function(position) {
|
||||
locationStatus.textContent = "Location successfully added";
|
||||
locationStatus.classList.add('text-success');
|
||||
|
||||
// Store location in hidden fields
|
||||
latitudeInput.value = position.coords.latitude;
|
||||
longitudeInput.value = position.coords.longitude;
|
||||
},
|
||||
// Error callback
|
||||
function(error) {
|
||||
let errorMessage = "Unable to get your location";
|
||||
switch(error.code) {
|
||||
case error.PERMISSION_DENIED:
|
||||
errorMessage = "Location access denied. Please enable location services.";
|
||||
break;
|
||||
case error.POSITION_UNAVAILABLE:
|
||||
errorMessage = "Location information unavailable.";
|
||||
break;
|
||||
case error.TIMEOUT:
|
||||
errorMessage = "Location request timed out.";
|
||||
break;
|
||||
}
|
||||
locationStatus.textContent = errorMessage;
|
||||
locationStatus.classList.add('text-danger');
|
||||
},
|
||||
// Options
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0
|
||||
}
|
||||
);
|
||||
} else {
|
||||
locationStatus.textContent = "Geolocation is not supported by your browser";
|
||||
locationStatus.classList.add('text-danger');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle photo selection and preview
|
||||
*/
|
||||
function handlePhotoSelection() {
|
||||
// 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) => {
|
||||
if (!file.type.match('image.*')) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle form submission
|
||||
*/
|
||||
function handleFormSubmission(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// Show loading overlay
|
||||
loadingOverlay.classList.remove('d-none');
|
||||
|
||||
// Disable submit button to prevent double submission
|
||||
submitButton.disabled = true;
|
||||
|
||||
// Create FormData object
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Send AJAX request
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Navigate to the URL the server specified
|
||||
window.location.href = response.url;
|
||||
return;
|
||||
} else {
|
||||
console.error("Not ok response:", response);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('There was a problem submitting your report. Please try again.');
|
||||
|
||||
// Re-enable submit button
|
||||
submitButton.disabled = false;
|
||||
|
||||
// Hide loading overlay
|
||||
loadingOverlay.classList.add('d-none');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
<!-- Main Content -->
|
||||
|
|
@ -62,14 +240,18 @@
|
|||
<h2 class="card-title text-center mb-4">Quick Mosquito Report</h2>
|
||||
|
||||
<!-- Form -->
|
||||
<form action="/service-request-quick-confirmation" method="get">
|
||||
<form id="mosquitoReportForm" action="/quick-submit" method="POST" enctype="multipart/form-data">
|
||||
<!-- Location Automatic Collection Note -->
|
||||
<div class="location-info d-flex align-items-center mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-geo-alt me-2" viewBox="0 0 16 16">
|
||||
<path d="M12.166 8.94c-.524 1.062-1.234 2.12-1.96 3.07A31.493 31.493 0 0 1 8 14.58a31.481 31.481 0 0 1-2.206-2.57c-.726-.95-1.436-2.008-1.96-3.07C3.304 7.867 3 6.862 3 6a5 5 0 0 1 10 0c0 .862-.305 1.867-.834 2.94zM8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10z"/>
|
||||
<path d="M8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 1a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
|
||||
</svg>
|
||||
<span>Your location and current time will be automatically collected with your report.</span>
|
||||
<span id="locationStatus">Requesting your location...</span>
|
||||
<!-- Hidden fields for location data -->
|
||||
<input type="hidden" id="latitude" name="latitude">
|
||||
<input type="hidden" id="longitude" name="longitude">
|
||||
<input type="hidden" id="created" name="created">
|
||||
</div>
|
||||
|
||||
<!-- Photo Upload -->
|
||||
|
|
@ -81,15 +263,14 @@
|
|||
<path d="M8 11a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm0 1a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zM3 6.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<input type="file" id="photos" class="d-none" accept="image/*" multiple>
|
||||
<input type="file" id="photos" name="photos" class="d-none" accept="image/*" multiple>
|
||||
<button type="button" class="btn btn-outline-primary mb-2" onclick="document.getElementById('photos').click()">Add Photos</button>
|
||||
</div>
|
||||
<small class="d-block text-muted">Take pictures of the mosquito problem area</small>
|
||||
|
||||
<!-- Photo Preview Area (would be populated by JavaScript) -->
|
||||
<div class="photo-preview mt-3">
|
||||
<!-- Example preview images (these would be dynamically added) -->
|
||||
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80' fill='%23dddddd'%3E%3Crect width='80' height='80' fill='%23eeeeee'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' font-family='sans-serif' font-size='12' fill='%23999999'%3EPreview%3C/text%3E%3C/svg%3E" alt="Preview">
|
||||
<!-- Photo Preview Area -->
|
||||
<div id="photoPreviewContainer" class="photo-preview mt-3 d-flex flex-wrap">
|
||||
<!-- Image previews will be added here by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -97,11 +278,11 @@
|
|||
<!-- Comments -->
|
||||
<div class="mb-4">
|
||||
<label for="comments" class="form-label fw-bold">Comments</label>
|
||||
<textarea class="form-control" id="comments" rows="4" placeholder="Describe the mosquito issue (e.g., standing water, high mosquito activity, time of day they're most active)"></textarea>
|
||||
<textarea class="form-control" id="comments" name="comments" rows="4" placeholder="Describe the mosquito issue (e.g., standing water, high mosquito activity, time of day they're most active)"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button type="submit" class="btn btn-success w-100 submit-btn mt-4">
|
||||
<button type="submit" class="btn btn-success w-100 submit-btn mt-4" id="submitButton">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-send-fill me-2" viewBox="0 0 16 16">
|
||||
<path d="M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855H.766l-.452.18a.5.5 0 0 0-.082.887l.41.26.001.002 4.995 3.178 3.178 4.995.002.002.26.41a.5.5 0 0 0 .886-.083l6-15Zm-1.833 1.89L6.637 10.07l-.215-.338a.5.5 0 0 0-.154-.154l-.338-.215 7.494-7.494 1.178-.471-.47 1.178Z"/>
|
||||
</svg>
|
||||
|
|
@ -110,18 +291,15 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Back Link -->
|
||||
<div class="text-center mt-3">
|
||||
<a href="javascript:history.back()" class="text-decoration-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left me-1" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"/>
|
||||
</svg>
|
||||
Back to home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Loading Indicator Overlay (Initially hidden) -->
|
||||
<div id="loadingOverlay" class="position-fixed top-0 start-0 w-100 h-100 d-none" style="background-color: rgba(0,0,0,0.5); z-index: 1050;">
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-white text-center">
|
||||
<div class="spinner-border" role="status"></div>
|
||||
<p class="mt-2">Submitting your report...</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
8
main.go
8
main.go
|
|
@ -15,8 +15,8 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/background"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/public-report"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/queue"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/report"
|
||||
nidussync "github.com/Gleipnir-Technology/nidus-sync/sync"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
|
@ -52,9 +52,9 @@ func main() {
|
|||
|
||||
// Set up routing by hostname
|
||||
sr := nidussync.Router()
|
||||
hr.Map("", sr) // default
|
||||
hr.Map("*", sr) // default
|
||||
hr.Map(config.URLReport, report.Router()) // report.mosquitoes.online
|
||||
hr.Map("", sr) // default
|
||||
hr.Map("*", sr) // default
|
||||
hr.Map(config.URLReport, publicreport.Router()) // report.mosquitoes.online
|
||||
hr.Map(config.URLSync, sr)
|
||||
r.Mount("/", hr)
|
||||
|
||||
|
|
|
|||
125
public-report/endpoint.go
Normal file
125
public-report/endpoint.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package publicreport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/htmlpage"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/htmlpage/public-reports"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func Router() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getRoot)
|
||||
r.Get("/nuisance", getNuisance)
|
||||
r.Get("/pool", getPool)
|
||||
r.Get("/quick", getQuick)
|
||||
r.Post("/quick-submit", postQuick)
|
||||
r.Get("/quick-submit-complete", getQuickSubmitComplete)
|
||||
r.Get("/status", getStatus)
|
||||
localFS := http.Dir("./static")
|
||||
htmlpage.FileServer(r, "/static", localFS, publicreports.EmbeddedStaticFS, "static")
|
||||
return r
|
||||
}
|
||||
|
||||
func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Root,
|
||||
publicreports.ContextRoot{},
|
||||
)
|
||||
}
|
||||
|
||||
func getNuisance(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Nuisance,
|
||||
publicreports.ContextNuisance{},
|
||||
)
|
||||
}
|
||||
func getPool(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Pool,
|
||||
publicreports.ContextPool{},
|
||||
)
|
||||
}
|
||||
func getQuick(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Quick,
|
||||
publicreports.ContextQuick{},
|
||||
)
|
||||
}
|
||||
func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) {
|
||||
report := r.URL.Query().Get("report")
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.QuickSubmitComplete,
|
||||
publicreports.ContextQuickSubmitComplete{
|
||||
ReportID: report,
|
||||
},
|
||||
)
|
||||
}
|
||||
func getStatus(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Status,
|
||||
publicreports.ContextStatus{},
|
||||
)
|
||||
}
|
||||
func postQuick(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to parse form")
|
||||
respondError(w, "Failed to parse form", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
latitude := r.FormValue("latitude")
|
||||
longitude := r.FormValue("longitude")
|
||||
created := r.FormValue("created")
|
||||
//photos := r.FormValue("photos")
|
||||
|
||||
log.Info().Str("latitude", latitude).Str("longitude", longitude).Str("created", created).Msg("Got upload")
|
||||
for _, fheaders := range r.MultipartForm.File {
|
||||
for _, headers := range fheaders {
|
||||
file, err := headers.Open()
|
||||
|
||||
if err != nil {
|
||||
respondError(w, "Failed to open header", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
buff := make([]byte, 512)
|
||||
file.Read(buff)
|
||||
|
||||
file.Seek(0, 0)
|
||||
contentType := http.DetectContentType(buff)
|
||||
var sizeBuff bytes.Buffer
|
||||
fileSize, err := sizeBuff.ReadFrom(file)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to read file", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
file.Seek(0, 0)
|
||||
contentBuf := bytes.NewBuffer(nil)
|
||||
if _, err := io.Copy(contentBuf, file); err != nil {
|
||||
respondError(w, "Failed to save file", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Info().Int64("size", fileSize).Str("filename", headers.Filename).Str("content-type", contentType).Msg("Got an uploaded file")
|
||||
}
|
||||
}
|
||||
http.Redirect(w, r, "/quick-submit-complete?report=123", http.StatusFound)
|
||||
}
|
||||
|
||||
// Respond with an error that is visible to the user
|
||||
func respondError(w http.ResponseWriter, m string, e error, s int) {
|
||||
log.Warn().Int("status", s).Err(e).Str("user message", m).Msg("Responding with an error")
|
||||
http.Error(w, m, s)
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/htmlpage"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/htmlpage/public-reports"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func Router() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getRoot)
|
||||
r.Get("/nuisance", getNuisance)
|
||||
r.Get("/pool", getPool)
|
||||
r.Get("/quick", getQuick)
|
||||
r.Get("/status", getStatus)
|
||||
localFS := http.Dir("./static")
|
||||
htmlpage.FileServer(r, "/static", localFS, publicreports.EmbeddedStaticFS, "static")
|
||||
return r
|
||||
}
|
||||
|
||||
func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Root,
|
||||
publicreports.ContextRoot{},
|
||||
)
|
||||
}
|
||||
|
||||
func getNuisance(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Nuisance,
|
||||
publicreports.ContextNuisance{},
|
||||
)
|
||||
}
|
||||
func getPool(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Pool,
|
||||
publicreports.ContextPool{},
|
||||
)
|
||||
}
|
||||
func getQuick(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Quick,
|
||||
publicreports.ContextQuick{},
|
||||
)
|
||||
}
|
||||
func getStatus(w http.ResponseWriter, r *http.Request) {
|
||||
htmlpage.RenderOrError(
|
||||
w,
|
||||
publicreports.Status,
|
||||
publicreports.ContextStatus{},
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue