mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 10:31:09 -05:00
577 lines
29 KiB
HTML
577 lines
29 KiB
HTML
{% extends 'base/base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Location Search Results - ThrillWiki{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<style>
|
|
.location-card {
|
|
@apply bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm hover:shadow-md transition-all cursor-pointer border border-gray-200 dark:border-gray-700;
|
|
}
|
|
|
|
.location-card:hover {
|
|
@apply border-blue-300 dark:border-blue-600 shadow-lg;
|
|
}
|
|
|
|
.location-type-badge {
|
|
@apply inline-flex items-center px-2 py-1 rounded-full text-xs font-medium;
|
|
}
|
|
|
|
.location-type-park {
|
|
@apply bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100;
|
|
}
|
|
|
|
.location-type-ride {
|
|
@apply bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100;
|
|
}
|
|
|
|
.location-type-company {
|
|
@apply bg-purple-100 text-purple-800 dark:bg-purple-800 dark:text-purple-100;
|
|
}
|
|
|
|
.status-badge {
|
|
@apply inline-flex items-center px-2 py-1 rounded-full text-xs font-medium;
|
|
}
|
|
|
|
.status-operating {
|
|
@apply bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100;
|
|
}
|
|
|
|
.status-closed {
|
|
@apply bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100;
|
|
}
|
|
|
|
.status-construction {
|
|
@apply bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100;
|
|
}
|
|
|
|
.status-demolished {
|
|
@apply bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100;
|
|
}
|
|
|
|
.filter-chip {
|
|
@apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium cursor-pointer transition-all;
|
|
}
|
|
|
|
.filter-chip.active {
|
|
@apply bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100;
|
|
}
|
|
|
|
.filter-chip.inactive {
|
|
@apply bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-600;
|
|
}
|
|
|
|
.pagination-container {
|
|
@apply flex items-center justify-between mt-8;
|
|
}
|
|
|
|
.pagination-info {
|
|
@apply text-sm text-gray-700 dark:text-gray-300;
|
|
}
|
|
|
|
.pagination-controls {
|
|
@apply flex items-center space-x-2;
|
|
}
|
|
|
|
.pagination-btn {
|
|
@apply px-3 py-2 text-sm font-medium rounded-lg border transition-colors;
|
|
}
|
|
|
|
.pagination-btn.active {
|
|
@apply bg-blue-600 text-white border-blue-600;
|
|
}
|
|
|
|
.pagination-btn.inactive {
|
|
@apply bg-white text-gray-700 border-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700;
|
|
}
|
|
|
|
.pagination-btn:disabled {
|
|
@apply opacity-50 cursor-not-allowed;
|
|
}
|
|
|
|
.search-summary {
|
|
@apply bg-blue-50 dark:bg-blue-900 dark:bg-opacity-30 rounded-lg p-4 mb-6;
|
|
}
|
|
|
|
.no-results {
|
|
@apply text-center py-12;
|
|
}
|
|
|
|
.loading-skeleton {
|
|
@apply animate-pulse;
|
|
}
|
|
|
|
.loading-skeleton .skeleton-text {
|
|
@apply bg-gray-200 dark:bg-gray-700 rounded h-4;
|
|
}
|
|
|
|
.loading-skeleton .skeleton-text.w-3-4 {
|
|
width: 75%;
|
|
}
|
|
|
|
.loading-skeleton .skeleton-text.w-1-2 {
|
|
width: 50%;
|
|
}
|
|
|
|
.loading-skeleton .skeleton-text.w-1-4 {
|
|
width: 25%;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container px-4 mx-auto">
|
|
<!-- Header -->
|
|
<div class="flex flex-col items-start justify-between gap-4 mb-6 md:flex-row md:items-center">
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Search Results</h1>
|
|
<p class="mt-2 text-gray-600 dark:text-gray-400">
|
|
{% if query %}
|
|
Search results for "{{ query }}"
|
|
{% else %}
|
|
Browse all locations in ThrillWiki
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="flex gap-3">
|
|
<a href="{% url 'maps:universal_map' %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
|
|
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
|
<i class="mr-2 fas fa-map"></i>View on Map
|
|
</a>
|
|
<a href="{% url 'maps:nearby_locations' %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
|
|
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 transition-colors">
|
|
<i class="mr-2 fas fa-search-location"></i>Find Nearby
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search and Filters -->
|
|
<div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
|
<form id="search-form" method="get" class="space-y-4">
|
|
<!-- Search Input -->
|
|
<div>
|
|
<label for="search" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Search</label>
|
|
<div class="relative">
|
|
<input type="text" name="q" id="search"
|
|
class="w-full pl-10 pr-4 py-2 border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
placeholder="Search by name, location, or keyword..."
|
|
value="{{ request.GET.q|default:'' }}"
|
|
hx-get="{% url 'maps:location_list' %}"
|
|
hx-trigger="input changed delay:500ms"
|
|
hx-target="#results-container"
|
|
hx-include="#search-form"
|
|
hx-indicator="#search-loading">
|
|
<i class="absolute left-3 top-1/2 transform -translate-y-1/2 fas fa-search text-gray-400"></i>
|
|
<div id="search-loading" class="htmx-indicator absolute right-3 top-1/2 transform -translate-y-1/2">
|
|
<div class="w-4 h-4 border-2 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Chips -->
|
|
<div class="space-y-3">
|
|
<!-- Location Types -->
|
|
<div>
|
|
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Location Types</label>
|
|
<div class="flex flex-wrap gap-2">
|
|
<label class="filter-chip {% if 'park' in location_types %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="types" value="park" class="hidden"
|
|
{% if 'park' in location_types %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-tree"></i>Parks
|
|
</label>
|
|
<label class="filter-chip {% if 'ride' in location_types %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="types" value="ride" class="hidden"
|
|
{% if 'ride' in location_types %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-rocket"></i>Rides
|
|
</label>
|
|
<label class="filter-chip {% if 'company' in location_types %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="types" value="company" class="hidden"
|
|
{% if 'company' in location_types %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-building"></i>Companies
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Filters (for parks) -->
|
|
{% if 'park' in location_types %}
|
|
<div>
|
|
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Park Status</label>
|
|
<div class="flex flex-wrap gap-2">
|
|
<label class="filter-chip {% if 'OPERATING' in park_statuses %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="park_status" value="OPERATING" class="hidden"
|
|
{% if 'OPERATING' in park_statuses %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-check-circle"></i>Operating
|
|
</label>
|
|
<label class="filter-chip {% if 'CLOSED_TEMP' in park_statuses %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="park_status" value="CLOSED_TEMP" class="hidden"
|
|
{% if 'CLOSED_TEMP' in park_statuses %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-clock"></i>Temporarily Closed
|
|
</label>
|
|
<label class="filter-chip {% if 'CLOSED_PERM' in park_statuses %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="park_status" value="CLOSED_PERM" class="hidden"
|
|
{% if 'CLOSED_PERM' in park_statuses %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-times-circle"></i>Permanently Closed
|
|
</label>
|
|
<label class="filter-chip {% if 'UNDER_CONSTRUCTION' in park_statuses %}active{% else %}inactive{% endif %}">
|
|
<input type="checkbox" name="park_status" value="UNDER_CONSTRUCTION" class="hidden"
|
|
{% if 'UNDER_CONSTRUCTION' in park_statuses %}checked{% endif %}>
|
|
<i class="mr-2 fas fa-hard-hat"></i>Under Construction
|
|
</label>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Location Filters -->
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
|
<div>
|
|
<label for="country" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Country</label>
|
|
<input type="text" name="country" id="country"
|
|
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
placeholder="Filter by country..."
|
|
value="{{ request.GET.country|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label for="state" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">State/Region</label>
|
|
<input type="text" name="state" id="state"
|
|
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
placeholder="Filter by state..."
|
|
value="{{ request.GET.state|default:'' }}">
|
|
</div>
|
|
<div>
|
|
<label for="sort" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Sort By</label>
|
|
<select name="sort" id="sort"
|
|
class="w-full border-gray-300 rounded-lg form-select dark:border-gray-600 dark:bg-gray-700 dark:text-white">
|
|
<option value="name" {% if request.GET.sort == 'name' %}selected{% endif %}>Name (A-Z)</option>
|
|
<option value="-name" {% if request.GET.sort == '-name' %}selected{% endif %}>Name (Z-A)</option>
|
|
<option value="location" {% if request.GET.sort == 'location' %}selected{% endif %}>Location</option>
|
|
<option value="-created_at" {% if request.GET.sort == '-created_at' %}selected{% endif %}>Recently Added</option>
|
|
{% if 'park' in location_types %}
|
|
<option value="-ride_count" {% if request.GET.sort == '-ride_count' %}selected{% endif %}>Most Rides</option>
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex gap-3">
|
|
<button type="submit"
|
|
class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
|
<i class="mr-2 fas fa-search"></i>Apply Filters
|
|
</button>
|
|
<a href="{% url 'maps:location_list' %}"
|
|
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 transition-colors">
|
|
<i class="mr-2 fas fa-times"></i>Clear All
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Search Summary -->
|
|
{% if locations %}
|
|
<div class="search-summary">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-blue-900 dark:text-blue-100">
|
|
{{ paginator.count }} location{{ paginator.count|pluralize }} found
|
|
</h3>
|
|
<p class="text-sm text-blue-700 dark:text-blue-200 mt-1">
|
|
{% if query %}
|
|
Showing results for "{{ query }}"
|
|
{% endif %}
|
|
{% if location_types %}
|
|
• Types: {{ location_types|join:", "|title }}
|
|
{% endif %}
|
|
{% if request.GET.country %}
|
|
• Country: {{ request.GET.country }}
|
|
{% endif %}
|
|
{% if request.GET.state %}
|
|
• State: {{ request.GET.state }}
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="text-sm text-blue-700 dark:text-blue-200">
|
|
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Results Container -->
|
|
<div id="results-container">
|
|
{% if locations %}
|
|
<!-- Location Cards -->
|
|
<div class="grid grid-cols-1 gap-4 mb-8 md:grid-cols-2 lg:grid-cols-3">
|
|
{% for location in locations %}
|
|
<div class="location-card"
|
|
onclick="window.location.href='{{ location.get_absolute_url }}'">
|
|
|
|
<div class="flex items-start justify-between mb-3">
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white truncate">
|
|
{{ location.name }}
|
|
</h3>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 truncate">
|
|
{{ location.formatted_location|default:"Location not specified" }}
|
|
</p>
|
|
</div>
|
|
<div class="flex-shrink-0 ml-3">
|
|
<span class="location-type-badge location-type-{{ location.type }}">
|
|
{% if location.type == 'park' %}
|
|
<i class="mr-1 fas fa-tree"></i>Park
|
|
{% elif location.type == 'ride' %}
|
|
<i class="mr-1 fas fa-rocket"></i>Ride
|
|
{% elif location.type == 'company' %}
|
|
<i class="mr-1 fas fa-building"></i>Company
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Type-specific information -->
|
|
{% if location.type == 'park' %}
|
|
<div class="space-y-2">
|
|
{% if location.status %}
|
|
<div class="flex items-center">
|
|
<span class="status-badge {% if location.status == 'OPERATING' %}status-operating{% elif location.status == 'CLOSED_TEMP' or location.status == 'CLOSED_PERM' %}status-closed{% elif location.status == 'UNDER_CONSTRUCTION' %}status-construction{% else %}status-demolished{% endif %}">
|
|
{% if location.status == 'OPERATING' %}
|
|
<i class="mr-1 fas fa-check-circle"></i>Operating
|
|
{% elif location.status == 'CLOSED_TEMP' %}
|
|
<i class="mr-1 fas fa-clock"></i>Temporarily Closed
|
|
{% elif location.status == 'CLOSED_PERM' %}
|
|
<i class="mr-1 fas fa-times-circle"></i>Permanently Closed
|
|
{% elif location.status == 'UNDER_CONSTRUCTION' %}
|
|
<i class="mr-1 fas fa-hard-hat"></i>Under Construction
|
|
{% elif location.status == 'DEMOLISHED' %}
|
|
<i class="mr-1 fas fa-ban"></i>Demolished
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
{% if location.operator %}
|
|
<i class="mr-2 fas fa-building"></i>
|
|
<span>{{ location.operator }}</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if location.ride_count %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-rocket"></i>
|
|
<span>{{ location.ride_count }} ride{{ location.ride_count|pluralize }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if location.average_rating %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-star text-yellow-500"></i>
|
|
<span>{{ location.average_rating|floatformat:1 }}/10</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% elif location.type == 'ride' %}
|
|
<div class="space-y-2">
|
|
{% if location.park_name %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-tree"></i>
|
|
<span>{{ location.park_name }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if location.manufacturer %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-industry"></i>
|
|
<span>{{ location.manufacturer }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if location.opening_date %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-calendar"></i>
|
|
<span>Opened {{ location.opening_date.year }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% elif location.type == 'company' %}
|
|
<div class="space-y-2">
|
|
{% if location.company_type %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-tag"></i>
|
|
<span>{{ location.get_company_type_display }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if location.founded_year %}
|
|
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
<i class="mr-2 fas fa-calendar"></i>
|
|
<span>Founded {{ location.founded_year }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Action buttons -->
|
|
<div class="flex gap-2 mt-4">
|
|
<a href="{{ location.get_absolute_url }}"
|
|
class="flex-1 px-3 py-2 text-sm text-center text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
|
View Details
|
|
</a>
|
|
{% if location.latitude and location.longitude %}
|
|
<a href="{% url 'maps:nearby_locations' %}?lat={{ location.latitude }}&lng={{ location.longitude }}&radius=25"
|
|
class="px-3 py-2 text-sm text-blue-600 border border-blue-600 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-900 transition-colors">
|
|
<i class="fas fa-search-location"></i>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if page_obj.has_other_pages %}
|
|
<div class="pagination-container">
|
|
<div class="pagination-info">
|
|
Showing {{ page_obj.start_index }}-{{ page_obj.end_index }} of {{ paginator.count }} results
|
|
</div>
|
|
|
|
<div class="pagination-controls">
|
|
{% if page_obj.has_previous %}
|
|
<a href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page=1"
|
|
class="pagination-btn inactive">
|
|
<i class="fas fa-angle-double-left"></i>
|
|
</a>
|
|
<a href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
|
class="pagination-btn inactive">
|
|
<i class="fas fa-angle-left"></i>
|
|
</a>
|
|
{% endif %}
|
|
|
|
{% for num in page_obj.paginator.page_range %}
|
|
{% if page_obj.number == num %}
|
|
<span class="pagination-btn active">{{ num }}</span>
|
|
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
|
<a href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ num }}"
|
|
class="pagination-btn inactive">{{ num }}</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if page_obj.has_next %}
|
|
<a href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ page_obj.next_page_number }}"
|
|
class="pagination-btn inactive">
|
|
<i class="fas fa-angle-right"></i>
|
|
</a>
|
|
<a href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ paginator.num_pages }}"
|
|
class="pagination-btn inactive">
|
|
<i class="fas fa-angle-double-right"></i>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<!-- No Results -->
|
|
<div class="no-results">
|
|
<i class="fas fa-search text-6xl text-gray-400 mb-6"></i>
|
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">No locations found</h3>
|
|
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
|
{% if query %}
|
|
No results found for "{{ query }}". Try adjusting your search or filters.
|
|
{% else %}
|
|
No locations match your current filters. Try adjusting your search criteria.
|
|
{% endif %}
|
|
</p>
|
|
<div class="flex justify-center gap-3">
|
|
<a href="{% url 'maps:location_list' %}"
|
|
class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
|
<i class="mr-2 fas fa-refresh"></i>Clear Filters
|
|
</a>
|
|
<a href="{% url 'maps:universal_map' %}"
|
|
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 transition-colors">
|
|
<i class="mr-2 fas fa-map"></i>Browse Map
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Loading Template for HTMX -->
|
|
<template id="loading-template">
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{% for i in "123456789" %}
|
|
<div class="location-card loading-skeleton">
|
|
<div class="flex items-start justify-between mb-3">
|
|
<div class="flex-1">
|
|
<div class="skeleton-text w-3-4 h-6 mb-2"></div>
|
|
<div class="skeleton-text w-1-2 h-4"></div>
|
|
</div>
|
|
<div class="skeleton-text w-1-4 h-6"></div>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<div class="skeleton-text w-1-2 h-4"></div>
|
|
<div class="skeleton-text w-3-4 h-4"></div>
|
|
</div>
|
|
<div class="flex gap-2 mt-4">
|
|
<div class="skeleton-text flex-1 h-8"></div>
|
|
<div class="skeleton-text w-10 h-8"></div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</template>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Handle filter chip toggles
|
|
document.querySelectorAll('.filter-chip').forEach(chip => {
|
|
const checkbox = chip.querySelector('input[type="checkbox"]');
|
|
|
|
chip.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
if (checkbox) {
|
|
checkbox.checked = !checkbox.checked;
|
|
chip.classList.toggle('active', checkbox.checked);
|
|
chip.classList.toggle('inactive', !checkbox.checked);
|
|
|
|
// Auto-submit form on filter change
|
|
document.getElementById('search-form').dispatchEvent(new Event('submit'));
|
|
}
|
|
});
|
|
});
|
|
|
|
// Handle form changes
|
|
document.getElementById('search-form').addEventListener('change', function(e) {
|
|
if (e.target.name !== 'q') { // Don't auto-submit on search input changes
|
|
this.submit();
|
|
}
|
|
});
|
|
|
|
// Show loading state during HTMX requests
|
|
document.addEventListener('htmx:beforeRequest', function(event) {
|
|
if (event.target.id === 'results-container') {
|
|
const template = document.getElementById('loading-template');
|
|
event.target.innerHTML = template.innerHTML;
|
|
}
|
|
});
|
|
|
|
// Handle HTMX errors
|
|
document.addEventListener('htmx:responseError', function(event) {
|
|
console.error('Search request failed:', event.detail);
|
|
event.target.innerHTML = `
|
|
<div class="text-center py-12">
|
|
<i class="fas fa-exclamation-triangle text-6xl text-red-400 mb-6"></i>
|
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">Search Error</h3>
|
|
<p class="text-gray-600 dark:text-gray-400 mb-6">There was an error performing your search. Please try again.</p>
|
|
<button onclick="location.reload()"
|
|
class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
|
<i class="mr-2 fas fa-refresh"></i>Retry
|
|
</button>
|
|
</div>
|
|
`;
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |