Refactor designer, manufacturer, and ride model forms to utilize Alpine.js for state management. Improved form submission handling, HTMX event integration, and enhanced user experience through better event dispatching and modal management.

This commit is contained in:
pacnpal
2025-09-26 14:34:59 -04:00
parent 5b7b203619
commit 09e2c69493
3 changed files with 206 additions and 94 deletions

View File

@@ -1,35 +1,66 @@
{% load static %}
<form method="post"
class="space-y-6"
x-data="{ submitting: false }"
@submit.prevent="
if (!submitting) {
submitting = true;
const formData = new FormData($event.target);
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('designerForm', () => ({
submitting: false,
init() {
// Listen for HTMX events on this form
this.$el.addEventListener('htmx:afterRequest', (event) => {
if (event.detail.pathInfo.requestPath === '/rides/designers/create/') {
this.handleResponse(event);
}
});
},
async submitForm(event) {
if (this.submitting) return;
this.submitting = true;
const formData = new FormData(event.target);
const csrfToken = this.$el.querySelector('[name=csrfmiddlewaretoken]').value;
// Use HTMX for form submission
htmx.ajax('POST', '/rides/designers/create/', {
values: Object.fromEntries(formData),
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
'X-CSRFToken': csrfToken
},
target: this.$el,
swap: 'none'
});
},
// Handle HTMX response using event listeners
document.addEventListener('htmx:afterRequest', function handleResponse(event) {
if (event.detail.pathInfo.requestPath === '/rides/designers/create/') {
document.removeEventListener('htmx:afterRequest', handleResponse);
handleResponse(event) {
this.submitting = false;
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
if (typeof selectDesigner === 'function') {
selectDesigner(data.id, data.name);
}
$dispatch('close-designer-modal');
}
submitting = false;
}
});
}">
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
// Dispatch event with designer data for parent components
this.$dispatch('designer-created', {
id: data.id,
name: data.name
});
// Close modal if in modal context
this.$dispatch('close-designer-modal');
} else {
// Handle error case
this.$dispatch('designer-creation-error', {
error: event.detail.xhr.responseText
});
}
}
}));
});
</script>
<form method="post"
class="space-y-6"
x-data="designerForm()"
@submit.prevent="submitForm($event)">
{% csrf_token %}
<div id="designer-form-notification"></div>

View File

@@ -1,35 +1,66 @@
{% load static %}
<form method="post"
class="space-y-6"
x-data="{ submitting: false }"
@submit.prevent="
if (!submitting) {
submitting = true;
const formData = new FormData($event.target);
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('manufacturerForm', () => ({
submitting: false,
init() {
// Listen for HTMX events on this form
this.$el.addEventListener('htmx:afterRequest', (event) => {
if (event.detail.pathInfo.requestPath === '/rides/manufacturers/create/') {
this.handleResponse(event);
}
});
},
async submitForm(event) {
if (this.submitting) return;
this.submitting = true;
const formData = new FormData(event.target);
const csrfToken = this.$el.querySelector('[name=csrfmiddlewaretoken]').value;
// Use HTMX for form submission
htmx.ajax('POST', '/rides/manufacturers/create/', {
values: Object.fromEntries(formData),
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
'X-CSRFToken': csrfToken
},
target: this.$el,
swap: 'none'
});
},
// Handle HTMX response using event listeners
document.addEventListener('htmx:afterRequest', function handleResponse(event) {
if (event.detail.pathInfo.requestPath === '/rides/manufacturers/create/') {
document.removeEventListener('htmx:afterRequest', handleResponse);
handleResponse(event) {
this.submitting = false;
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
if (typeof selectManufacturer === 'function') {
selectManufacturer(data.id, data.name);
}
$dispatch('close-manufacturer-modal');
}
submitting = false;
}
});
}">
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
// Dispatch event with manufacturer data for parent components
this.$dispatch('manufacturer-created', {
id: data.id,
name: data.name
});
// Close modal if in modal context
this.$dispatch('close-manufacturer-modal');
} else {
// Handle error case
this.$dispatch('manufacturer-creation-error', {
error: event.detail.xhr.responseText
});
}
}
}));
});
</script>
<form method="post"
class="space-y-6"
x-data="manufacturerForm()"
@submit.prevent="submitForm($event)">
{% csrf_token %}
<div id="manufacturer-form-notification"></div>

View File

@@ -1,53 +1,103 @@
{% load static %}
<form method="post"
class="space-y-6"
x-data="{
submitting: false,
manufacturerSearchTerm: '',
setManufacturerModal(value, term = '') {
const parentForm = document.querySelector('[x-data]');
if (parentForm) {
const parentData = Alpine.$data(parentForm);
if (parentData && parentData.setManufacturerModal) {
parentData.setManufacturerModal(value, term);
}
}
}
}"
@submit.prevent="
if (!submitting) {
submitting = true;
const formData = new FormData($event.target);
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('rideModelForm', () => ({
submitting: false,
manufacturerSearchTerm: '',
init() {
// Listen for HTMX events on this form
this.$el.addEventListener('htmx:afterRequest', (event) => {
if (event.detail.pathInfo.requestPath === '/rides/models/create/') {
this.handleResponse(event);
}
});
// Initialize form with any pre-filled values
this.initializeForm();
},
initializeForm() {
const searchInput = this.$el.querySelector('#id_ride_model_search');
const nameInput = this.$el.querySelector('#id_name');
if (searchInput && searchInput.value && nameInput) {
nameInput.value = searchInput.value;
}
},
setManufacturerModal(value, term = '') {
// Dispatch event to parent component to handle manufacturer modal
this.$dispatch('set-manufacturer-modal', {
show: value,
searchTerm: term
});
},
async submitForm(event) {
if (this.submitting) return;
this.submitting = true;
const formData = new FormData(event.target);
const csrfToken = this.$el.querySelector('[name=csrfmiddlewaretoken]').value;
// Use HTMX for form submission
htmx.ajax('POST', '/rides/models/create/', {
values: Object.fromEntries(formData),
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
'X-CSRFToken': csrfToken
},
target: this.$el,
swap: 'none'
});
},
// Handle HTMX response using event listeners
document.addEventListener('htmx:afterRequest', function handleResponse(event) {
if (event.detail.pathInfo.requestPath === '/rides/models/create/') {
document.removeEventListener('htmx:afterRequest', handleResponse);
handleResponse(event) {
this.submitting = false;
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
if (typeof selectRideModel === 'function') {
selectRideModel(data.id, data.name);
}
const parentForm = document.querySelector('[x-data]');
if (parentForm) {
const parentData = Alpine.$data(parentForm);
if (parentData && parentData.setRideModelModal) {
parentData.setRideModelModal(false);
}
}
}
submitting = false;
}
});
}">
if (event.detail.xhr.status >= 200 && event.detail.xhr.status < 300) {
const data = JSON.parse(event.detail.xhr.response);
// Dispatch event with ride model data for parent components
this.$dispatch('ride-model-created', {
id: data.id,
name: data.name
});
// Close modal if in modal context
this.$dispatch('close-ride-model-modal');
} else {
// Handle error case
this.$dispatch('ride-model-creation-error', {
error: event.detail.xhr.responseText
});
}
},
selectManufacturer(manufacturerId, manufacturerName) {
// Update manufacturer fields using AlpineJS reactive approach
const manufacturerInput = this.$el.querySelector('#id_manufacturer');
const searchInput = this.$el.querySelector('#id_manufacturer_search');
const resultsDiv = this.$el.querySelector('#manufacturer-search-results');
if (manufacturerInput) manufacturerInput.value = manufacturerId;
if (searchInput) searchInput.value = manufacturerName;
if (resultsDiv) resultsDiv.innerHTML = '';
},
clearManufacturerResults() {
const resultsDiv = this.$el.querySelector('#manufacturer-search-results');
if (resultsDiv) resultsDiv.innerHTML = '';
}
}));
});
</script>
<form method="post"
class="space-y-6"
x-data="rideModelForm()"
@submit.prevent="submitForm($event)"
@click.outside="clearManufacturerResults()">
{% csrf_token %}
<div id="ride-model-notification"></div>