mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:51:08 -05:00
212 lines
7.9 KiB
HTML
212 lines
7.9 KiB
HTML
{% load static %}
|
|
|
|
<!-- HTMX + AlpineJS ONLY - NO CUSTOM JAVASCRIPT -->
|
|
<style>
|
|
/* Ensure map container and its elements stay below other UI elements */
|
|
.leaflet-pane,
|
|
.leaflet-tile,
|
|
.leaflet-marker-icon,
|
|
.leaflet-marker-shadow,
|
|
.leaflet-tile-container,
|
|
.leaflet-pane > svg,
|
|
.leaflet-pane > canvas,
|
|
.leaflet-zoom-box,
|
|
.leaflet-image-layer,
|
|
.leaflet-layer {
|
|
z-index: 1 !important;
|
|
}
|
|
.leaflet-control {
|
|
z-index: 2 !important;
|
|
}
|
|
</style>
|
|
|
|
<div class="location-widget" id="locationWidget"
|
|
x-data="{
|
|
searchResults: [],
|
|
showResults: false,
|
|
searchTimeout: null,
|
|
|
|
init() {
|
|
// Initialize map via HTMX
|
|
this.initializeMap();
|
|
},
|
|
|
|
initializeMap() {
|
|
// Use HTMX to load map component
|
|
htmx.ajax('GET', '/maps/location-widget/', {
|
|
target: '#locationMap',
|
|
swap: 'innerHTML'
|
|
});
|
|
},
|
|
|
|
handleSearchInput(query) {
|
|
clearTimeout(this.searchTimeout);
|
|
|
|
if (!query.trim()) {
|
|
this.showResults = false;
|
|
return;
|
|
}
|
|
|
|
this.searchTimeout = setTimeout(() => {
|
|
this.searchLocation(query.trim());
|
|
}, 300);
|
|
},
|
|
|
|
searchLocation(query) {
|
|
// Use HTMX for location search
|
|
htmx.ajax('GET', '/parks/search/location/', {
|
|
values: { q: query },
|
|
target: '#search-results-container',
|
|
swap: 'innerHTML'
|
|
});
|
|
},
|
|
|
|
selectLocation(lat, lng, displayName, address) {
|
|
// Update coordinates
|
|
this.$refs.latitude.value = lat;
|
|
this.$refs.longitude.value = lng;
|
|
|
|
// Update address fields
|
|
if (address) {
|
|
this.$refs.streetAddress.value = address.street || '';
|
|
this.$refs.city.value = address.city || '';
|
|
this.$refs.state.value = address.state || '';
|
|
this.$refs.country.value = address.country || '';
|
|
this.$refs.postalCode.value = address.postal_code || '';
|
|
}
|
|
|
|
// Update search input
|
|
this.$refs.searchInput.value = displayName;
|
|
this.showResults = false;
|
|
|
|
// Update map via HTMX
|
|
htmx.ajax('POST', '/maps/update-marker/', {
|
|
values: { lat: lat, lng: lng },
|
|
target: '#locationMap',
|
|
swap: 'none'
|
|
});
|
|
},
|
|
|
|
handleMapClick(lat, lng) {
|
|
// Use HTMX for reverse geocoding
|
|
htmx.ajax('GET', '/parks/search/reverse-geocode/', {
|
|
values: { lat: lat, lon: lng },
|
|
target: '#location-form-fields',
|
|
swap: 'none'
|
|
});
|
|
}
|
|
}"
|
|
@click.outside="showResults = false">
|
|
|
|
{# Search Form #}
|
|
<div class="relative mb-4">
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Search Location
|
|
</label>
|
|
<input type="text"
|
|
x-ref="searchInput"
|
|
@input="handleSearchInput($event.target.value)"
|
|
hx-get="/parks/search/location/"
|
|
hx-trigger="input changed delay:300ms"
|
|
hx-target="#search-results-container"
|
|
hx-swap="innerHTML"
|
|
class="relative w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
placeholder="Search for a location..."
|
|
autocomplete="off"
|
|
style="z-index: 10;">
|
|
|
|
<div id="search-results-container"
|
|
x-show="showResults"
|
|
x-transition
|
|
style="position: absolute; top: 100%; left: 0; right: 0; z-index: 1000;"
|
|
class="w-full mt-1 overflow-auto bg-white border rounded-md shadow-lg max-h-60 dark:bg-gray-700 dark:border-gray-600">
|
|
<!-- Search results will be populated here via HTMX -->
|
|
</div>
|
|
</div>
|
|
|
|
{# Map Container #}
|
|
<div class="relative mb-4" style="z-index: 1;">
|
|
<div id="locationMap" class="h-[300px] w-full rounded-lg border border-gray-300 dark:border-gray-600">
|
|
<!-- Map will be loaded via HTMX -->
|
|
<div class="flex items-center justify-center h-full bg-gray-100 dark:bg-gray-800 rounded-lg">
|
|
<div class="text-center">
|
|
<div class="w-8 h-8 mx-auto mb-4 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">Loading map...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Location Form Fields #}
|
|
<div id="location-form-fields" class="relative grid grid-cols-1 gap-4 md:grid-cols-2" style="z-index: 10;">
|
|
<div>
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Street Address
|
|
</label>
|
|
<input type="text"
|
|
name="street_address"
|
|
x-ref="streetAddress"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
value="{{ form.street_address.value|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
City
|
|
</label>
|
|
<input type="text"
|
|
name="city"
|
|
x-ref="city"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
value="{{ form.city.value|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
State/Region
|
|
</label>
|
|
<input type="text"
|
|
name="state"
|
|
x-ref="state"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
value="{{ form.state.value|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Country
|
|
</label>
|
|
<input type="text"
|
|
name="country"
|
|
x-ref="country"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
value="{{ form.country.value|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Postal Code
|
|
</label>
|
|
<input type="text"
|
|
name="postal_code"
|
|
x-ref="postalCode"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
value="{{ form.postal_code.value|default:'' }}">
|
|
</div>
|
|
</div>
|
|
|
|
{# Hidden Coordinate Fields #}
|
|
<div class="hidden">
|
|
<input type="hidden" name="latitude" x-ref="latitude" value="{{ form.latitude.value|default:'' }}">
|
|
<input type="hidden" name="longitude" x-ref="longitude" value="{{ form.longitude.value|default:'' }}">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HTMX Error Handling (Minimal JavaScript as allowed by Context7 docs) -->
|
|
<div x-data="{
|
|
init() {
|
|
// Only essential HTMX error handling as shown in Context7 docs
|
|
this.$el.addEventListener('htmx:responseError', (evt) => {
|
|
if (evt.detail.xhr.status === 404 || evt.detail.xhr.status === 500) {
|
|
console.error('HTMX Error:', evt.detail.xhr.status);
|
|
}
|
|
});
|
|
}
|
|
}"></div>
|