mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:31:09 -05:00
- Enhanced filter sidebar with AlpineJS for collapsible sections and localStorage persistence. - Removed custom JavaScript in favor of AlpineJS for managing filter states and interactions. - Updated ride form to utilize AlpineJS for handling manufacturer, designer, and ride model selections. - Simplified search script to leverage AlpineJS for managing search input and suggestions. - Improved error handling for HTMX requests with minimal JavaScript. - Refactored ride form data handling to encapsulate logic within an AlpineJS component.
331 lines
12 KiB
HTML
331 lines
12 KiB
HTML
{% load static %}
|
|
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('rideForm', () => ({
|
|
init() {
|
|
// Handle form submission cleanup
|
|
this.$el.addEventListener('submit', () => {
|
|
this.clearAllSearchResults();
|
|
});
|
|
},
|
|
|
|
selectManufacturer(id, name) {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const manufacturerInput = this.$el.querySelector('#id_manufacturer');
|
|
const manufacturerSearch = this.$el.querySelector('#id_manufacturer_search');
|
|
const manufacturerResults = this.$el.querySelector('#manufacturer-search-results');
|
|
|
|
if (manufacturerInput) manufacturerInput.value = id;
|
|
if (manufacturerSearch) manufacturerSearch.value = name;
|
|
if (manufacturerResults) manufacturerResults.innerHTML = '';
|
|
|
|
// Update ride model search to include manufacturer
|
|
const rideModelSearch = this.$el.querySelector('#id_ride_model_search');
|
|
if (rideModelSearch) {
|
|
rideModelSearch.setAttribute('hx-include', '[name="manufacturer"]');
|
|
}
|
|
},
|
|
|
|
selectDesigner(id, name) {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const designerInput = this.$el.querySelector('#id_designer');
|
|
const designerSearch = this.$el.querySelector('#id_designer_search');
|
|
const designerResults = this.$el.querySelector('#designer-search-results');
|
|
|
|
if (designerInput) designerInput.value = id;
|
|
if (designerSearch) designerSearch.value = name;
|
|
if (designerResults) designerResults.innerHTML = '';
|
|
},
|
|
|
|
selectRideModel(id, name) {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const rideModelInput = this.$el.querySelector('#id_ride_model');
|
|
const rideModelSearch = this.$el.querySelector('#id_ride_model_search');
|
|
const rideModelResults = this.$el.querySelector('#ride-model-search-results');
|
|
|
|
if (rideModelInput) rideModelInput.value = id;
|
|
if (rideModelSearch) rideModelSearch.value = name;
|
|
if (rideModelResults) rideModelResults.innerHTML = '';
|
|
},
|
|
|
|
clearAllSearchResults() {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const manufacturerResults = this.$el.querySelector('#manufacturer-search-results');
|
|
const designerResults = this.$el.querySelector('#designer-search-results');
|
|
const rideModelResults = this.$el.querySelector('#ride-model-search-results');
|
|
|
|
if (manufacturerResults) manufacturerResults.innerHTML = '';
|
|
if (designerResults) designerResults.innerHTML = '';
|
|
if (rideModelResults) rideModelResults.innerHTML = '';
|
|
},
|
|
|
|
clearManufacturerResults() {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const manufacturerResults = this.$el.querySelector('#manufacturer-search-results');
|
|
if (manufacturerResults) manufacturerResults.innerHTML = '';
|
|
},
|
|
|
|
clearDesignerResults() {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const designerResults = this.$el.querySelector('#designer-search-results');
|
|
if (designerResults) designerResults.innerHTML = '';
|
|
},
|
|
|
|
clearRideModelResults() {
|
|
// Use AlpineJS $el to scope queries within component
|
|
const rideModelResults = this.$el.querySelector('#ride-model-search-results');
|
|
if (rideModelResults) rideModelResults.innerHTML = '';
|
|
}
|
|
}));
|
|
});
|
|
</script>
|
|
|
|
<form method="post"
|
|
id="ride-form"
|
|
class="space-y-6"
|
|
enctype="multipart/form-data"
|
|
x-data="rideForm"
|
|
x-init="init()">
|
|
{% csrf_token %}
|
|
|
|
<!-- Park Area -->
|
|
{% if form.park_area %}
|
|
<div class="space-y-2">
|
|
<label for="{{ form.park_area.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Park Area
|
|
</label>
|
|
{{ form.park_area }}
|
|
{% if form.park_area.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.park_area.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Name -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.name.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Name *
|
|
</label>
|
|
{{ form.name }}
|
|
{% if form.name.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.name.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Manufacturer -->
|
|
<div class="space-y-2">
|
|
<div id="manufacturer-search-container"
|
|
class="relative"
|
|
@click.outside="clearManufacturerResults()">
|
|
<label for="{{ form.manufacturer_search.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Manufacturer
|
|
</label>
|
|
{{ form.manufacturer_search }}
|
|
{{ form.manufacturer }}
|
|
<div id="manufacturer-search-results" class="relative"></div>
|
|
{% if form.manufacturer.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.manufacturer.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Designer -->
|
|
<div class="space-y-2">
|
|
<div id="designer-search-container"
|
|
class="relative"
|
|
@click.outside="clearDesignerResults()">
|
|
<label for="{{ form.designer_search.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Designer
|
|
</label>
|
|
{{ form.designer_search }}
|
|
{{ form.designer }}
|
|
<div id="designer-search-results" class="relative"></div>
|
|
{% if form.designer.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.designer.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ride Model -->
|
|
<div class="space-y-2">
|
|
<div id="ride-model-search-container"
|
|
class="relative"
|
|
@click.outside="clearRideModelResults()">
|
|
<label for="{{ form.ride_model_search.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Ride Model
|
|
</label>
|
|
{{ form.ride_model_search }}
|
|
{{ form.ride_model }}
|
|
<div id="ride-model-search-results" class="relative"></div>
|
|
{% if form.ride_model.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.ride_model.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Model Name -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.model_name.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Model Name
|
|
</label>
|
|
{{ form.model_name }}
|
|
{% if form.model_name.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.model_name.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Category -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.category.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Category *
|
|
</label>
|
|
{{ form.category }}
|
|
{% if form.category.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.category.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Coaster Fields -->
|
|
<div id="coaster-fields"></div>
|
|
|
|
<!-- Status -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.status.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Status
|
|
</label>
|
|
{{ form.status }}
|
|
{% if form.status.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.status.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Opening Date -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.opening_date.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Opening Date
|
|
</label>
|
|
{{ form.opening_date }}
|
|
{% if form.opening_date.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.opening_date.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Closing Date -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.closing_date.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Closing Date
|
|
</label>
|
|
{{ form.closing_date }}
|
|
{% if form.closing_date.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.closing_date.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Status Since -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.status_since.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Status Since
|
|
</label>
|
|
{{ form.status_since }}
|
|
{% if form.status_since.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.status_since.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Min Height -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.min_height_in.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Minimum Height (inches)
|
|
</label>
|
|
{{ form.min_height_in }}
|
|
{% if form.min_height_in.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.min_height_in.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Max Height -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.max_height_in.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Maximum Height (inches)
|
|
</label>
|
|
{{ form.max_height_in }}
|
|
{% if form.max_height_in.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.max_height_in.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Capacity -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.capacity_per_hour.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Hourly Capacity
|
|
</label>
|
|
{{ form.capacity_per_hour }}
|
|
{% if form.capacity_per_hour.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.capacity_per_hour.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Ride Duration -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.ride_duration_seconds.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Ride Duration (seconds)
|
|
</label>
|
|
{{ form.ride_duration_seconds }}
|
|
{% if form.ride_duration_seconds.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.ride_duration_seconds.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.description.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Description
|
|
</label>
|
|
{{ form.description }}
|
|
{% if form.description.errors %}
|
|
<div class="text-sm text-red-600 dark:text-red-400">
|
|
{{ form.description.errors }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div class="flex justify-end">
|
|
<button type="submit"
|
|
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-blue-500 dark:hover:bg-blue-600">
|
|
{% if is_edit %}Save Changes{% else %}Add Ride{% endif %}
|
|
</button>
|
|
</div>
|
|
</form>
|