Make photo upload component common to quick and pool reports
This commit is contained in:
parent
fc10e3e95d
commit
2c85624583
5 changed files with 187 additions and 283 deletions
|
|
@ -39,7 +39,7 @@ var (
|
||||||
Status = buildTemplate("status", "base")
|
Status = buildTemplate("status", "base")
|
||||||
)
|
)
|
||||||
|
|
||||||
var components = [...]string{"footer"}
|
var components = [...]string{"footer", "photo-upload", "photo-upload-header"}
|
||||||
|
|
||||||
func buildTemplate(files ...string) *htmlpage.BuiltTemplate {
|
func buildTemplate(files ...string) *htmlpage.BuiltTemplate {
|
||||||
subdir := "public-report"
|
subdir := "public-report"
|
||||||
|
|
|
||||||
126
public-report/template/component/photo-upload-header.html
Normal file
126
public-report/template/component/photo-upload-header.html
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
{{define "photo-upload-header"}}
|
||||||
|
<style>
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* Handle photo selection and preview
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
19
public-report/template/component/photo-upload.html
Normal file
19
public-report/template/component/photo-upload.html
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{{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"/>
|
||||||
|
<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 class="file-upload-container" id="photoDropArea">
|
||||||
|
<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 -->
|
||||||
|
<div id="photoPreviewContainer" class="photo-preview mt-3 d-flex flex-wrap">
|
||||||
|
<!-- Image previews will be added here by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
@ -2,80 +2,7 @@
|
||||||
|
|
||||||
{{define "title"}}Green Pool{{end}}
|
{{define "title"}}Green Pool{{end}}
|
||||||
{{define "extraheader"}}
|
{{define "extraheader"}}
|
||||||
<script>
|
{{template "photo-upload-header"}}
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const photoUpload = document.getElementById('photoUpload');
|
|
||||||
const imagePreview = document.getElementById('imagePreview');
|
|
||||||
const dropArea = document.getElementById('dropArea');
|
|
||||||
|
|
||||||
// Handle file selection
|
|
||||||
photoUpload.addEventListener('change', handleFileSelect);
|
|
||||||
|
|
||||||
// Handle drag and drop
|
|
||||||
dropArea.addEventListener('dragover', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
dropArea.style.backgroundColor = '#e9ecef';
|
|
||||||
});
|
|
||||||
|
|
||||||
dropArea.addEventListener('dragleave', function() {
|
|
||||||
dropArea.style.backgroundColor = '#f8f9fa';
|
|
||||||
});
|
|
||||||
|
|
||||||
dropArea.addEventListener('drop', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
dropArea.style.backgroundColor = '#f8f9fa';
|
|
||||||
|
|
||||||
if (e.dataTransfer.files.length) {
|
|
||||||
handleFiles(e.dataTransfer.files);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleFileSelect(e) {
|
|
||||||
const files = e.target.files;
|
|
||||||
handleFiles(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFiles(files) {
|
|
||||||
const maxFiles = 5;
|
|
||||||
const currentFiles = imagePreview.querySelectorAll('.preview-item').length;
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length && currentFiles + i < maxFiles; i++) {
|
|
||||||
const file = files[i];
|
|
||||||
|
|
||||||
if (!file.type.match('image.*')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
|
|
||||||
reader.onload = (function(theFile) {
|
|
||||||
return function(e) {
|
|
||||||
const previewItem = document.createElement('div');
|
|
||||||
previewItem.className = 'preview-item';
|
|
||||||
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.src = e.target.result;
|
|
||||||
|
|
||||||
const removeBtn = document.createElement('div');
|
|
||||||
removeBtn.className = 'preview-remove';
|
|
||||||
removeBtn.innerHTML = 'x';
|
|
||||||
removeBtn.addEventListener('click', function() {
|
|
||||||
imagePreview.removeChild(previewItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
previewItem.appendChild(img);
|
|
||||||
previewItem.appendChild(removeBtn);
|
|
||||||
imagePreview.appendChild(previewItem);
|
|
||||||
};
|
|
||||||
})(file);
|
|
||||||
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
photoUpload.value = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style>
|
<style>
|
||||||
.district-logo {
|
.district-logo {
|
||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
|
|
@ -91,51 +18,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
.file-upload-container {
|
|
||||||
border: 2px dashed #dee2e6;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
}
|
|
||||||
.file-upload-container:hover {
|
|
||||||
background-color: #e9ecef;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.image-preview {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
.preview-item {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.preview-item img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
.preview-remove {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
background-color: rgba(0,0,0,0.5);
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.form-section {
|
.form-section {
|
||||||
margin-bottom: 2.5rem;
|
margin-bottom: 2.5rem;
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
|
|
@ -203,23 +85,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
</div>
|
</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>
|
<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>
|
||||||
|
|
||||||
<div class="file-upload-container" id="dropArea">
|
{{template "photo-upload"}}
|
||||||
<input type="file" id="photoUpload" multiple accept="image/*" class="d-none">
|
|
||||||
<div class="mb-2">
|
|
||||||
<i class="bi bi-cloud-arrow-up fs-1 text-primary"></i>
|
|
||||||
</div>
|
|
||||||
<p class="mb-1"><strong>Drag and drop photos here</strong></p>
|
|
||||||
<p class="mb-1">- or -</p>
|
|
||||||
<button type="button" class="btn btn-primary mt-2" onclick="document.getElementById('photoUpload').click()">
|
|
||||||
Select Photos
|
|
||||||
</button>
|
|
||||||
<p class="small text-muted mt-2 mb-0">You can upload multiple photos (maximum 5)</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Image Preview Area -->
|
|
||||||
<div class="image-preview" id="imagePreview">
|
|
||||||
<!-- Preview items will be added here dynamically -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Location Section -->
|
<!-- Location Section -->
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,13 @@
|
||||||
|
|
||||||
{{define "title"}}Quick Report{{end}}
|
{{define "title"}}Quick Report{{end}}
|
||||||
{{define "extraheader"}}
|
{{define "extraheader"}}
|
||||||
|
{{template "photo-upload-header"}}
|
||||||
<style>
|
<style>
|
||||||
.district-logo {
|
.district-logo {
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
width: auto;
|
width: 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
|
|
@ -55,8 +33,6 @@
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Elements
|
// Elements
|
||||||
const form = document.getElementById('mosquitoReportForm');
|
const form = document.getElementById('mosquitoReportForm');
|
||||||
const photoInput = document.getElementById('photos');
|
|
||||||
const photoPreviewContainer = document.getElementById('photoPreviewContainer');
|
|
||||||
const locationStatus = document.getElementById('locationStatus');
|
const locationStatus = document.getElementById('locationStatus');
|
||||||
const latitudeInput = document.getElementById('latitude');
|
const latitudeInput = document.getElementById('latitude');
|
||||||
const longitudeInput = document.getElementById('longitude');
|
const longitudeInput = document.getElementById('longitude');
|
||||||
|
|
@ -66,9 +42,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Get current location
|
// Get current location
|
||||||
requestLocation();
|
requestLocation();
|
||||||
|
|
||||||
// Handle photo selection
|
|
||||||
photoInput.addEventListener('change', handlePhotoSelection);
|
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
form.addEventListener('submit', handleFormSubmission);
|
form.addEventListener('submit', handleFormSubmission);
|
||||||
|
|
||||||
|
|
@ -76,111 +49,47 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
* Request user's geolocation
|
* Request user's geolocation
|
||||||
*/
|
*/
|
||||||
function requestLocation() {
|
function requestLocation() {
|
||||||
if (navigator.geolocation) {
|
if (navigator.geolocation) {
|
||||||
locationStatus.textContent = "Requesting your location...";
|
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
|
navigator.geolocation.getCurrentPosition(
|
||||||
latitudeInput.value = position.coords.latitude;
|
// Success callback
|
||||||
longitudeInput.value = position.coords.longitude;
|
function(position) {
|
||||||
},
|
locationStatus.textContent = "Location successfully added";
|
||||||
// Error callback
|
locationStatus.classList.add('text-success');
|
||||||
function(error) {
|
|
||||||
let errorMessage = "Unable to get your location";
|
// Store location in hidden fields
|
||||||
switch(error.code) {
|
latitudeInput.value = position.coords.latitude;
|
||||||
case error.PERMISSION_DENIED:
|
longitudeInput.value = position.coords.longitude;
|
||||||
errorMessage = "Location access denied. Please enable location services.";
|
},
|
||||||
break;
|
// Error callback
|
||||||
case error.POSITION_UNAVAILABLE:
|
function(error) {
|
||||||
errorMessage = "Location information unavailable.";
|
let errorMessage = "Unable to get your location";
|
||||||
break;
|
switch(error.code) {
|
||||||
case error.TIMEOUT:
|
case error.PERMISSION_DENIED:
|
||||||
errorMessage = "Location request timed out.";
|
errorMessage = "Location access denied. Please enable location services.";
|
||||||
break;
|
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
|
||||||
}
|
}
|
||||||
locationStatus.textContent = errorMessage;
|
);
|
||||||
|
} else {
|
||||||
|
locationStatus.textContent = "Geolocation is not supported by your browser";
|
||||||
locationStatus.classList.add('text-danger');
|
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -251,23 +160,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
<!-- Photo Upload -->
|
<!-- Photo Upload -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="photos" class="form-label fw-bold">Photos (Optional)</label>
|
{{template "photo-upload"}}
|
||||||
<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"/>
|
|
||||||
<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" 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 -->
|
|
||||||
<div id="photoPreviewContainer" class="photo-preview mt-3 d-flex flex-wrap">
|
|
||||||
<!-- Image previews will be added here by JavaScript -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Comments -->
|
<!-- Comments -->
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue