Files
thrillwiki_django_no_react/templates/search/advanced_search.html
pacnpal e1cb76f1c6 Refactor ParkLocation model to inherit from TrackedModel for enhanced history tracking. Update point handling to temporarily store coordinates as a string. Implement Haversine formula for distance calculation as a placeholder until PostGIS is enabled.
Refactor advanced search template to utilize Alpine.js for state management. Enhance search functionality with dynamic view modes and improved filter handling using HTMX.
2025-09-26 15:56:28 -04:00

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 %}