mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 01:11:08 -05:00
Refactor park and ride detail templates to utilize Alpine.js for state management in photo galleries and upload modals. Enhanced photo handling and initialization logic for improved user experience.
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
{% if is_edit %}Edit{% else %}Create{% endif %} Park
|
||||
</h1>
|
||||
|
||||
<form method="post" enctype="multipart/form-data" class="space-y-6" x-data="parkForm">
|
||||
<form method="post" enctype="multipart/form-data" class="space-y-6" x-data="parkFormData">
|
||||
{% csrf_token %}
|
||||
|
||||
{# Basic Information #}
|
||||
@@ -81,7 +81,10 @@
|
||||
<div class="absolute top-0 right-0 p-2">
|
||||
<button type="button"
|
||||
class="p-2 text-white bg-red-600 rounded-full hover:bg-red-700"
|
||||
@click="removePhoto('{{ photo.id }}')">
|
||||
hx-delete="{% url 'media:photo_delete' photo.id %}"
|
||||
hx-confirm="Are you sure you want to remove this photo?"
|
||||
hx-target="closest .relative"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -101,7 +104,7 @@
|
||||
accept="image/*"
|
||||
class="hidden"
|
||||
x-ref="fileInput"
|
||||
@change="handleFileSelect">
|
||||
@change="handleFileSelect($event)">
|
||||
<button type="button"
|
||||
class="w-full px-4 py-2 text-gray-700 border-2 border-dashed rounded-lg dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
@click="$refs.fileInput.click()">
|
||||
@@ -212,8 +215,9 @@
|
||||
<div class="flex justify-end">
|
||||
<button type="submit"
|
||||
class="px-6 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800"
|
||||
:disabled="uploading"
|
||||
x-text="uploading ? 'Uploading...' : '{% if is_edit %}Save Changes{% else %}Create Park{% endif %}'">
|
||||
:disabled="uploading">
|
||||
<span x-show="!uploading">{% if is_edit %}Save Changes{% else %}Create Park{% endif %}</span>
|
||||
<span x-show="uploading">Uploading...</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -224,8 +228,8 @@
|
||||
{% block extra_js %}
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
function parkForm() {
|
||||
return {
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('parkFormData', () => ({
|
||||
previews: [],
|
||||
uploading: false,
|
||||
|
||||
@@ -257,133 +261,8 @@ function parkForm() {
|
||||
|
||||
removePreview(index) {
|
||||
this.previews.splice(index, 1);
|
||||
},
|
||||
|
||||
uploadPhotos() {
|
||||
if (!this.previews.length) return true;
|
||||
|
||||
this.uploading = true;
|
||||
let allUploaded = true;
|
||||
let uploadPromises = [];
|
||||
|
||||
for (let preview of this.previews) {
|
||||
if (preview.uploaded || preview.error) continue;
|
||||
|
||||
preview.uploading = true;
|
||||
|
||||
// Create temporary form for HTMX request
|
||||
const tempForm = document.createElement('form');
|
||||
tempForm.setAttribute('hx-post', '/photos/upload/');
|
||||
tempForm.setAttribute('hx-trigger', 'submit');
|
||||
tempForm.setAttribute('hx-swap', 'none');
|
||||
tempForm.enctype = 'multipart/form-data';
|
||||
|
||||
// Add CSRF token
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.type = 'hidden';
|
||||
csrfInput.name = 'csrfmiddlewaretoken';
|
||||
csrfInput.value = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||||
tempForm.appendChild(csrfInput);
|
||||
|
||||
// Add form data
|
||||
const imageInput = document.createElement('input');
|
||||
imageInput.type = 'file';
|
||||
imageInput.name = 'image';
|
||||
imageInput.files = this.createFileList([preview.file]);
|
||||
tempForm.appendChild(imageInput);
|
||||
|
||||
const appLabelInput = document.createElement('input');
|
||||
appLabelInput.type = 'hidden';
|
||||
appLabelInput.name = 'app_label';
|
||||
appLabelInput.value = 'parks';
|
||||
tempForm.appendChild(appLabelInput);
|
||||
|
||||
const modelInput = document.createElement('input');
|
||||
modelInput.type = 'hidden';
|
||||
modelInput.name = 'model';
|
||||
modelInput.value = 'park';
|
||||
tempForm.appendChild(modelInput);
|
||||
|
||||
const objectIdInput = document.createElement('input');
|
||||
objectIdInput.type = 'hidden';
|
||||
objectIdInput.name = 'object_id';
|
||||
objectIdInput.value = '{{ park.id }}';
|
||||
tempForm.appendChild(objectIdInput);
|
||||
|
||||
// Track upload completion with event listeners
|
||||
tempForm.addEventListener('htmx:afterRequest', (event) => {
|
||||
try {
|
||||
if (event.detail.xhr.status === 200) {
|
||||
const result = JSON.parse(event.detail.xhr.responseText);
|
||||
preview.uploading = false;
|
||||
preview.uploaded = true;
|
||||
} else {
|
||||
throw new Error('Upload failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error);
|
||||
preview.uploading = false;
|
||||
preview.error = true;
|
||||
allUploaded = false;
|
||||
}
|
||||
|
||||
// Track completion
|
||||
completedUploads++;
|
||||
if (completedUploads === totalUploads) {
|
||||
this.uploading = false;
|
||||
}
|
||||
|
||||
document.body.removeChild(tempForm);
|
||||
});
|
||||
document.body.appendChild(tempForm);
|
||||
htmx.trigger(tempForm, 'submit');
|
||||
}
|
||||
|
||||
// Initialize completion tracking
|
||||
let completedUploads = 0;
|
||||
const totalUploads = this.previews.filter(p => !p.uploaded && !p.error).length;
|
||||
|
||||
if (totalUploads === 0) {
|
||||
this.uploading = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true; // Return immediately, completion handled by event listeners
|
||||
},
|
||||
|
||||
createFileList(files) {
|
||||
const dt = new DataTransfer();
|
||||
files.forEach(file => dt.items.add(file));
|
||||
return dt.files;
|
||||
},
|
||||
|
||||
removePhoto(photoId) {
|
||||
if (confirm('Are you sure you want to remove this photo?')) {
|
||||
// Create temporary form for HTMX request
|
||||
const tempForm = document.createElement('form');
|
||||
tempForm.setAttribute('hx-delete', `/photos/${photoId}/delete/`);
|
||||
tempForm.setAttribute('hx-trigger', 'submit');
|
||||
tempForm.setAttribute('hx-swap', 'none');
|
||||
|
||||
// Add CSRF token
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.type = 'hidden';
|
||||
csrfInput.name = 'csrfmiddlewaretoken';
|
||||
csrfInput.value = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||||
tempForm.appendChild(csrfInput);
|
||||
|
||||
tempForm.addEventListener('htmx:afterRequest', (event) => {
|
||||
if (event.detail.xhr.status === 200) {
|
||||
window.location.reload();
|
||||
}
|
||||
document.body.removeChild(tempForm);
|
||||
});
|
||||
|
||||
document.body.appendChild(tempForm);
|
||||
htmx.trigger(tempForm, 'submit');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user