mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 09:31:09 -05:00
Refactor advanced search template to utilize Alpine.js for state management. Enhance search functionality with dynamic view modes and improved filter handling using HTMX.
364 lines
16 KiB
HTML
364 lines
16 KiB
HTML
{% extends 'base/base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Advanced Search - ThrillWiki{% endblock %}
|
|
|
|
{% block meta_description %}Find your perfect theme park adventure with our advanced search. Filter by location, thrill level, ride type, and more to discover exactly what you're looking for.{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Advanced Search Page - HTMX + AlpineJS ONLY -->
|
|
<div class="min-h-screen bg-gradient-to-br from-white via-blue-50/30 to-indigo-50/30 dark:from-gray-950 dark:via-indigo-950/30 dark:to-purple-950/30"
|
|
x-data="{
|
|
searchType: 'parks',
|
|
viewMode: 'grid',
|
|
|
|
toggleSearchType(type) {
|
|
this.searchType = type;
|
|
// Use HTMX to update filters
|
|
htmx.trigger('#filter-form', 'change');
|
|
},
|
|
|
|
setViewMode(mode) {
|
|
this.viewMode = mode;
|
|
}
|
|
}">
|
|
|
|
<!-- Search Header -->
|
|
<section class="py-16 bg-gradient-to-r from-thrill-primary/10 via-purple-500/10 to-pink-500/10 backdrop-blur-sm">
|
|
<div class="container mx-auto px-6">
|
|
<div class="text-center max-w-4xl mx-auto">
|
|
<h1 class="text-4xl md:text-5xl font-bold mb-6">
|
|
<span class="bg-gradient-to-r from-thrill-primary via-purple-500 to-pink-500 bg-clip-text text-transparent">
|
|
Advanced Search
|
|
</span>
|
|
</h1>
|
|
<p class="text-xl text-neutral-600 dark:text-neutral-400 mb-8">
|
|
Find your perfect theme park adventure with precision. Use our advanced filters to discover exactly what you're looking for.
|
|
</p>
|
|
|
|
<!-- Quick Search Bar -->
|
|
<div class="relative max-w-2xl mx-auto">
|
|
<input type="text"
|
|
placeholder="Quick search: parks, rides, locations..."
|
|
class="w-full pl-16 pr-6 py-4 bg-white/80 dark:bg-neutral-800/80 backdrop-blur-sm border border-neutral-300/50 dark:border-neutral-600/50 rounded-2xl text-lg shadow-lg focus:shadow-xl focus:bg-white dark:focus:bg-neutral-800 focus:border-thrill-primary focus:ring-2 focus:ring-thrill-primary/20 transition-all duration-300"
|
|
hx-get="/search/quick/"
|
|
hx-trigger="keyup changed delay:300ms"
|
|
hx-target="#quick-results"
|
|
hx-swap="innerHTML">
|
|
<div class="absolute left-6 top-1/2 transform -translate-y-1/2">
|
|
<i class="fas fa-search text-2xl text-thrill-primary"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Results -->
|
|
<div id="quick-results" class="mt-4"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Advanced Filters -->
|
|
<section class="py-12">
|
|
<div class="container mx-auto px-6">
|
|
<div class="grid lg:grid-cols-4 gap-8">
|
|
|
|
<!-- Filters Sidebar -->
|
|
<div class="lg:col-span-1">
|
|
<div class="card p-6 sticky top-24">
|
|
<h2 class="text-2xl font-bold mb-6 flex items-center">
|
|
<i class="fas fa-filter mr-3 text-thrill-primary"></i>
|
|
Filters
|
|
</h2>
|
|
|
|
<form id="filter-form"
|
|
hx-get="/search/results/"
|
|
hx-target="#search-results"
|
|
hx-trigger="change, submit"
|
|
hx-swap="innerHTML"
|
|
class="space-y-6">
|
|
|
|
<!-- Search Type Toggle -->
|
|
<div class="form-group">
|
|
<label class="form-label">Search For</label>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<label class="flex items-center p-3 border border-neutral-300 dark:border-neutral-600 rounded-lg cursor-pointer hover:bg-thrill-primary/5 transition-colors"
|
|
:class="{ 'bg-thrill-primary/10 border-thrill-primary': searchType === 'parks' }">
|
|
<input type="radio"
|
|
name="search_type"
|
|
value="parks"
|
|
x-model="searchType"
|
|
class="sr-only">
|
|
<div class="w-4 h-4 border-2 border-thrill-primary rounded-full mr-3 flex items-center justify-center">
|
|
<div class="w-2 h-2 bg-thrill-primary rounded-full transition-opacity"
|
|
:class="{ 'opacity-100': searchType === 'parks', 'opacity-0': searchType !== 'parks' }"></div>
|
|
</div>
|
|
<i class="fas fa-map-marked-alt mr-2 text-thrill-primary"></i>
|
|
Parks
|
|
</label>
|
|
<label class="flex items-center p-3 border border-neutral-300 dark:border-neutral-600 rounded-lg cursor-pointer hover:bg-thrill-secondary/5 transition-colors"
|
|
:class="{ 'bg-thrill-secondary/10 border-thrill-secondary': searchType === 'rides' }">
|
|
<input type="radio"
|
|
name="search_type"
|
|
value="rides"
|
|
x-model="searchType"
|
|
class="sr-only">
|
|
<div class="w-4 h-4 border-2 border-thrill-secondary rounded-full mr-3 flex items-center justify-center">
|
|
<div class="w-2 h-2 bg-thrill-secondary rounded-full transition-opacity"
|
|
:class="{ 'opacity-100': searchType === 'rides', 'opacity-0': searchType !== 'rides' }"></div>
|
|
</div>
|
|
<i class="fas fa-rocket mr-2 text-thrill-secondary"></i>
|
|
Rides
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location Filters -->
|
|
<div class="form-group">
|
|
<label class="form-label">Location</label>
|
|
<div class="space-y-3">
|
|
<select name="country" class="form-select">
|
|
<option value="">Any Country</option>
|
|
<option value="US">United States</option>
|
|
<option value="CA">Canada</option>
|
|
<option value="GB">United Kingdom</option>
|
|
<option value="DE">Germany</option>
|
|
<option value="FR">France</option>
|
|
<option value="JP">Japan</option>
|
|
<option value="CN">China</option>
|
|
</select>
|
|
|
|
<select name="region" class="form-select">
|
|
<option value="">Any State/Region</option>
|
|
</select>
|
|
|
|
<input type="text" name="city" placeholder="City" class="form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Park-Specific Filters -->
|
|
<div x-show="searchType === 'parks'" x-transition class="space-y-6">
|
|
<div class="form-group">
|
|
<label class="form-label">Park Type</label>
|
|
<select name="park_type" class="form-select">
|
|
<option value="">Any Type</option>
|
|
<option value="THEME_PARK">Theme Park</option>
|
|
<option value="AMUSEMENT_PARK">Amusement Park</option>
|
|
<option value="WATER_PARK">Water Park</option>
|
|
<option value="FAMILY_ENTERTAINMENT">Family Entertainment</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Park Status</label>
|
|
<div class="space-y-2">
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="status" value="OPERATING" checked class="form-checkbox">
|
|
<span class="badge-operating ml-2">Operating</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="status" value="CONSTRUCTION" class="form-checkbox">
|
|
<span class="badge-construction ml-2">Under Construction</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Minimum Rides</label>
|
|
<input type="range" name="min_rides" min="0" max="100" value="0" class="w-full form-range">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0</span>
|
|
<span x-text="$el.querySelector('input[name=min_rides]')?.value || '0'"></span>
|
|
<span>100+</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ride-Specific Filters -->
|
|
<div x-show="searchType === 'rides'" x-transition class="space-y-6">
|
|
<div class="form-group">
|
|
<label class="form-label">Thrill Level</label>
|
|
<div class="space-y-2">
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="thrill_level" value="MILD" class="form-checkbox">
|
|
<span class="badge bg-green-500/10 text-green-600 border-green-500/20 ml-2">
|
|
<i class="fas fa-leaf mr-1"></i>
|
|
Family Friendly
|
|
</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="thrill_level" value="MODERATE" class="form-checkbox">
|
|
<span class="badge bg-yellow-500/10 text-yellow-600 border-yellow-500/20 ml-2">
|
|
<i class="fas fa-star mr-1"></i>
|
|
Moderate
|
|
</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="thrill_level" value="HIGH" class="form-checkbox">
|
|
<span class="badge bg-orange-500/10 text-orange-600 border-orange-500/20 ml-2">
|
|
<i class="fas fa-bolt mr-1"></i>
|
|
High Thrill
|
|
</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="thrill_level" value="EXTREME" class="form-checkbox">
|
|
<span class="badge bg-red-500/10 text-red-600 border-red-500/20 ml-2">
|
|
<i class="fas fa-fire mr-1"></i>
|
|
Extreme
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Ride Category</label>
|
|
<select name="category" class="form-select">
|
|
<option value="">Any Category</option>
|
|
<option value="ROLLER_COASTER">Roller Coaster</option>
|
|
<option value="WATER_RIDE">Water Ride</option>
|
|
<option value="DARK_RIDE">Dark Ride</option>
|
|
<option value="FLAT_RIDE">Flat Ride</option>
|
|
<option value="KIDDIE_RIDE">Kids Ride</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Minimum Height (ft)</label>
|
|
<input type="range" name="min_height" min="0" max="500" value="0" class="w-full form-range">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0ft</span>
|
|
<span x-text="($el.querySelector('input[name=min_height]')?.value || '0') + 'ft'"></span>
|
|
<span>500ft+</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Minimum Speed (mph)</label>
|
|
<input type="range" name="min_speed" min="0" max="150" value="0" class="w-full form-range">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0mph</span>
|
|
<span x-text="($el.querySelector('input[name=min_speed]')?.value || '0') + 'mph'"></span>
|
|
<span>150mph+</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sort Options -->
|
|
<div class="form-group">
|
|
<label class="form-label">Sort By</label>
|
|
<select name="sort" class="form-select">
|
|
<option value="relevance">Relevance</option>
|
|
<option value="name">Name (A-Z)</option>
|
|
<option value="-name">Name (Z-A)</option>
|
|
<option value="rating">Rating (Low to High)</option>
|
|
<option value="-rating">Rating (High to Low)</option>
|
|
<option value="opened_date">Oldest First</option>
|
|
<option value="-opened_date">Newest First</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Clear Filters -->
|
|
<button type="reset"
|
|
class="btn-ghost w-full"
|
|
hx-get="/search/results/"
|
|
hx-target="#search-results"
|
|
hx-swap="innerHTML">
|
|
<i class="fas fa-times mr-2"></i>
|
|
Clear All Filters
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Results -->
|
|
<div class="lg:col-span-3">
|
|
<!-- Results Header -->
|
|
<div class="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h2 class="text-2xl font-bold">Search Results</h2>
|
|
<p class="text-neutral-600 dark:text-neutral-400">
|
|
Use filters to find your perfect adventure
|
|
</p>
|
|
</div>
|
|
|
|
<!-- View Toggle -->
|
|
<div class="flex items-center space-x-2 bg-white dark:bg-neutral-800 rounded-lg p-1 border border-neutral-200 dark:border-neutral-700">
|
|
<button class="p-2 rounded-md transition-colors"
|
|
:class="{ 'bg-thrill-primary text-white': viewMode === 'grid', 'text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-700': viewMode !== 'grid' }"
|
|
@click="setViewMode('grid')">
|
|
<i class="fas fa-th-large"></i>
|
|
</button>
|
|
<button class="p-2 rounded-md transition-colors"
|
|
:class="{ 'bg-thrill-primary text-white': viewMode === 'list', 'text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-700': viewMode !== 'list' }"
|
|
@click="setViewMode('list')">
|
|
<i class="fas fa-list"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Results Container -->
|
|
<div id="search-results"
|
|
class="min-h-96"
|
|
:class="{ 'grid-view': viewMode === 'grid', 'list-view': viewMode === 'list' }">
|
|
<!-- Initial State -->
|
|
<div class="text-center py-16">
|
|
<div class="w-24 h-24 bg-gradient-to-r from-thrill-primary to-purple-500 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
<i class="fas fa-search text-3xl text-white"></i>
|
|
</div>
|
|
<h3 class="text-2xl font-bold mb-4">Ready to Explore?</h3>
|
|
<p class="text-neutral-600 dark:text-neutral-400 max-w-md mx-auto">
|
|
Use the filters on the left to discover amazing theme parks and thrilling rides that match your preferences.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Load More Button -->
|
|
<div class="text-center mt-8 hidden" id="load-more-container">
|
|
<button class="btn-secondary btn-lg"
|
|
hx-get="/search/results/"
|
|
hx-target="#search-results"
|
|
hx-swap="beforeend">
|
|
<i class="fas fa-plus mr-2"></i>
|
|
Load More Results
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<!-- Custom CSS for enhanced styling -->
|
|
<style>
|
|
.grid-view .search-results-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
gap: 2rem;
|
|
}
|
|
|
|
.list-view .search-results-grid {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.list-view .card-park,
|
|
.list-view .card-ride {
|
|
display: flex;
|
|
flex-direction: row;
|
|
max-width: none;
|
|
}
|
|
|
|
.list-view .card-park-image,
|
|
.list-view .card-ride-image {
|
|
width: 200px;
|
|
height: 150px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.list-view .card-park-content,
|
|
.list-view .card-ride-content {
|
|
flex: 1;
|
|
padding: 1.5rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|