mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 03:51:09 -05:00
508 lines
21 KiB
HTML
508 lines
21 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 -->
|
|
<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="advancedSearch()">
|
|
|
|
<!-- 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"
|
|
id="quick-search"
|
|
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">
|
|
<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="advanced-search-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">
|
|
<input type="radio" name="search_type" value="parks" checked 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 opacity-0 transition-opacity"></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">
|
|
<input type="radio" name="search_type" value="rides" 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 opacity-0 transition-opacity"></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 id="park-filters" 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="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge-operating">Operating</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="status" value="CONSTRUCTION" class="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge-construction">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">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0</span>
|
|
<span id="min-rides-value">0</span>
|
|
<span>100+</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ride-Specific Filters -->
|
|
<div id="ride-filters" class="space-y-6 hidden">
|
|
<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="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge bg-green-500/10 text-green-600 border-green-500/20">
|
|
<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="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge bg-yellow-500/10 text-yellow-600 border-yellow-500/20">
|
|
<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="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge bg-orange-500/10 text-orange-600 border-orange-500/20">
|
|
<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="sr-only">
|
|
<div class="checkbox-custom mr-3"></div>
|
|
<span class="badge bg-red-500/10 text-red-600 border-red-500/20">
|
|
<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">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0ft</span>
|
|
<span id="min-height-value">0ft</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">
|
|
<div class="flex justify-between text-sm text-neutral-500 mt-1">
|
|
<span>0mph</span>
|
|
<span id="min-speed-value">0mph</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="button"
|
|
id="clear-filters"
|
|
class="btn-ghost w-full"
|
|
@click="clearFilters()">
|
|
<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" id="results-count">
|
|
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 bg-thrill-primary text-white" id="grid-view" @click="setViewMode('grid')">
|
|
<i class="fas fa-th-large"></i>
|
|
</button>
|
|
<button class="p-2 rounded-md text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-700" id="list-view" @click="setViewMode('list')">
|
|
<i class="fas fa-list"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Results Container -->
|
|
<div id="search-results" class="min-h-96">
|
|
<!-- 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 id="load-more-container" class="text-center mt-8 hidden">
|
|
<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>
|
|
|
|
<!-- AlpineJS Advanced Search Component (HTMX + AlpineJS Only) -->
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('advancedSearch', () => ({
|
|
searchType: 'parks',
|
|
viewMode: 'grid',
|
|
|
|
init() {
|
|
// Initialize range sliders
|
|
this.updateRangeValues();
|
|
this.setupRadioButtons();
|
|
this.setupCheckboxes();
|
|
},
|
|
|
|
toggleSearchType(type) {
|
|
this.searchType = type;
|
|
const parkFilters = this.$el.querySelector('#park-filters');
|
|
const rideFilters = this.$el.querySelector('#ride-filters');
|
|
|
|
if (type === 'parks') {
|
|
parkFilters?.classList.remove('hidden');
|
|
rideFilters?.classList.add('hidden');
|
|
} else {
|
|
parkFilters?.classList.add('hidden');
|
|
rideFilters?.classList.remove('hidden');
|
|
}
|
|
},
|
|
|
|
clearFilters() {
|
|
const form = this.$el.querySelector('#advanced-search-form');
|
|
if (form) {
|
|
form.reset();
|
|
this.searchType = 'parks';
|
|
this.toggleSearchType('parks');
|
|
this.updateRangeValues();
|
|
this.setupRadioButtons();
|
|
this.setupCheckboxes();
|
|
}
|
|
},
|
|
|
|
setViewMode(mode) {
|
|
this.viewMode = mode;
|
|
const gridBtn = this.$el.querySelector('#grid-view');
|
|
const listBtn = this.$el.querySelector('#list-view');
|
|
const resultsContainer = this.$el.querySelector('#search-results');
|
|
|
|
if (mode === 'grid') {
|
|
gridBtn?.classList.add('bg-thrill-primary', 'text-white');
|
|
gridBtn?.classList.remove('text-neutral-600', 'dark:text-neutral-400', 'hover:bg-neutral-100', 'dark:hover:bg-neutral-700');
|
|
listBtn?.classList.remove('bg-thrill-primary', 'text-white');
|
|
listBtn?.classList.add('text-neutral-600', 'dark:text-neutral-400', 'hover:bg-neutral-100', 'dark:hover:bg-neutral-700');
|
|
resultsContainer?.classList.remove('list-view');
|
|
resultsContainer?.classList.add('grid-view');
|
|
} else {
|
|
listBtn?.classList.add('bg-thrill-primary', 'text-white');
|
|
listBtn?.classList.remove('text-neutral-600', 'dark:text-neutral-400', 'hover:bg-neutral-100', 'dark:hover:bg-neutral-700');
|
|
gridBtn?.classList.remove('bg-thrill-primary', 'text-white');
|
|
gridBtn?.classList.add('text-neutral-600', 'dark:text-neutral-400', 'hover:bg-neutral-100', 'dark:hover:bg-neutral-700');
|
|
resultsContainer?.classList.remove('grid-view');
|
|
resultsContainer?.classList.add('list-view');
|
|
}
|
|
},
|
|
|
|
updateRangeValues() {
|
|
const minRidesSlider = this.$el.querySelector('input[name="min_rides"]');
|
|
const minRidesValue = this.$el.querySelector('#min-rides-value');
|
|
const minHeightSlider = this.$el.querySelector('input[name="min_height"]');
|
|
const minHeightValue = this.$el.querySelector('#min-height-value');
|
|
const minSpeedSlider = this.$el.querySelector('input[name="min_speed"]');
|
|
const minSpeedValue = this.$el.querySelector('#min-speed-value');
|
|
|
|
if (minRidesSlider && minRidesValue) {
|
|
minRidesValue.textContent = minRidesSlider.value;
|
|
minRidesSlider.addEventListener('input', (e) => {
|
|
minRidesValue.textContent = e.target.value;
|
|
});
|
|
}
|
|
|
|
if (minHeightSlider && minHeightValue) {
|
|
minHeightValue.textContent = minHeightSlider.value + 'ft';
|
|
minHeightSlider.addEventListener('input', (e) => {
|
|
minHeightValue.textContent = e.target.value + 'ft';
|
|
});
|
|
}
|
|
|
|
if (minSpeedSlider && minSpeedValue) {
|
|
minSpeedValue.textContent = minSpeedSlider.value + 'mph';
|
|
minSpeedSlider.addEventListener('input', (e) => {
|
|
minSpeedValue.textContent = e.target.value + 'mph';
|
|
});
|
|
}
|
|
},
|
|
|
|
setupRadioButtons() {
|
|
const radioButtons = this.$el.querySelectorAll('input[type="radio"]');
|
|
radioButtons.forEach(radio => {
|
|
const indicator = radio.parentElement.querySelector('div');
|
|
const dot = indicator?.querySelector('div');
|
|
|
|
if (radio.checked && dot) {
|
|
dot.style.opacity = '1';
|
|
}
|
|
|
|
radio.addEventListener('change', () => {
|
|
// Reset all radio buttons in the same group
|
|
const groupName = radio.name;
|
|
const groupRadios = this.$el.querySelectorAll(`input[name="${groupName}"]`);
|
|
groupRadios.forEach(groupRadio => {
|
|
const groupIndicator = groupRadio.parentElement.querySelector('div');
|
|
const groupDot = groupIndicator?.querySelector('div');
|
|
if (groupDot) {
|
|
groupDot.style.opacity = groupRadio.checked ? '1' : '0';
|
|
}
|
|
});
|
|
|
|
if (radio.name === 'search_type') {
|
|
this.toggleSearchType(radio.value);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
setupCheckboxes() {
|
|
const checkboxes = this.$el.querySelectorAll('input[type="checkbox"]');
|
|
checkboxes.forEach(checkbox => {
|
|
const customCheckbox = checkbox.parentElement.querySelector('.checkbox-custom');
|
|
|
|
if (checkbox.checked && customCheckbox) {
|
|
customCheckbox.classList.add('checked');
|
|
}
|
|
|
|
checkbox.addEventListener('change', () => {
|
|
if (customCheckbox) {
|
|
if (checkbox.checked) {
|
|
customCheckbox.classList.add('checked');
|
|
} else {
|
|
customCheckbox.classList.remove('checked');
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}));
|
|
});
|
|
</script>
|
|
|
|
<!-- Custom CSS for checkboxes and enhanced styling -->
|
|
<style>
|
|
.checkbox-custom {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
border: 2px solid #cbd5e1;
|
|
border-radius: 0.25rem;
|
|
position: relative;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.checkbox-custom.checked {
|
|
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
border-color: #6366f1;
|
|
}
|
|
|
|
.checkbox-custom.checked::after {
|
|
content: '✓';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
color: white;
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.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 %}
|