Add park and ride card components with advanced search functionality

- Implemented park card component with image, status badge, favorite button, and quick stats overlay.
- Developed ride card component featuring thrill level badge, status badge, favorite button, and detailed stats.
- Created advanced search page with filters for parks and rides, including location, type, status, and thrill level.
- Added dynamic quick search functionality with results display.
- Enhanced user experience with JavaScript for filter toggling, range slider updates, and view switching.
- Included custom CSS for improved styling of checkboxes and search results layout.
This commit is contained in:
pacnpal
2025-09-24 23:10:48 -04:00
parent 4373d18176
commit b1c369c1bb
39 changed files with 5202 additions and 824 deletions

View File

@@ -1,472 +1,435 @@
{% comment %}
Enhanced Header Component - Matches React Frontend Design
Includes: Browse menu, advanced search, theme toggle, user dropdown, mobile menu
{% endcomment %}
{% load static %}
<header class="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div class="flex items-center h-14 px-4 gap-3 md:gap-4">
<!-- Logo and Browse Menu -->
<div class="flex items-center gap-4 shrink-0">
<!-- Logo -->
<a href="{% url 'home' %}" class="flex items-center space-x-2 flex-shrink-0">
<div class="w-6 h-6 bg-purple-600 rounded flex items-center justify-center">
<span class="text-white text-xs font-bold">TW</span>
</div>
<span class="font-bold text-lg whitespace-nowrap">ThrillWiki</span>
</a>
<!-- Browse Menu (Desktop) -->
<div class="hidden md:block">
<div
x-data="{ open: false }"
@mouseenter="open = true"
@mouseleave="open = false"
class="relative"
>
<button
class="flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md hover:bg-accent transition-colors"
@click="open = !open"
>
<i class="fas fa-compass w-4 h-4"></i>
Browse
<i class="fas fa-chevron-down w-3 h-3 transition-transform duration-200" :class="{ 'rotate-180': open }"></i>
</button>
<!-- Browse Dropdown -->
<div
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
x-cloak
class="absolute left-0 mt-1 w-auto max-w-4xl p-6 bg-background border rounded-lg shadow-lg z-50"
>
<div class="flex gap-8">
<!-- Left Column -->
<div class="flex-1 space-y-4 min-w-0">
<a
href="{% url 'parks:park_list' %}"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-map-marker-alt w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Parks</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Explore theme parks worldwide</p>
</div>
</a>
<a
href="{% url 'rides:manufacturer_list' %}"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-wrench w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Manufacturers</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Ride and attraction manufacturers</p>
</div>
</a>
<a
href="{% url 'parks:operator_list' %}"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-users w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Operators</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Theme park operating companies</p>
</div>
</a>
</div>
<!-- Right Column -->
<div class="flex-1 space-y-4 min-w-0">
<a
href="{% url 'rides:global_ride_list' %}"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-rocket w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Rides</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Discover rides and attractions</p>
</div>
</a>
<a
href="{% url 'rides:designer_list' %}"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-drafting-compass w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Designers</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Ride designers and architects</p>
</div>
</a>
<a
href="#"
class="flex items-start gap-3 p-3 rounded-md hover:bg-accent transition-colors group"
@click="open = false"
>
<i class="fas fa-trophy w-4 h-4 mt-0.5 text-muted-foreground group-hover:text-foreground flex-shrink-0"></i>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-sm mb-1 leading-tight">Top Lists</h3>
<p class="text-xs text-muted-foreground leading-relaxed">Community rankings and favorites</p>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Center Search Bar -->
<div class="hidden md:flex flex-1 min-w-0 justify-center">
<!-- Enhanced Search -->
<div class="relative w-full min-w-0 max-w-xl lg:max-w-2xl" x-data="searchComponent">
<div class="relative">
<i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground"></i>
<input
type="search"
placeholder="Search parks, rides..."
class="w-full min-w-0 pl-10 pr-3 h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
x-model="query"
@input.debounce.300ms="search()"
hx-get="{% url 'search:search' %}"
hx-trigger="input changed delay:300ms"
hx-target="#search-results"
hx-include="this"
name="q"
/>
</div>
<!-- Enhanced Navigation Header -->
<header class="fixed top-0 left-0 right-0 z-50 transition-all duration-300"
x-data="{
isOpen: false,
isScrolled: false,
searchOpen: false,
userMenuOpen: false
}"
x-init="
window.addEventListener('scroll', () => {
isScrolled = window.scrollY > 20;
});
"
:class="isScrolled ? 'bg-white/95 dark:bg-neutral-900/95 backdrop-blur-xl shadow-xl border-b border-neutral-200/50 dark:border-neutral-700/50' : 'bg-transparent'">
<nav class="container mx-auto px-6 py-4" role="navigation" aria-label="Main navigation">
<div class="flex items-center justify-between">
<!-- Logo/Brand -->
<div class="flex items-center space-x-4">
<a href="{% url 'home' %}"
class="nav-brand text-2xl md:text-3xl font-bold transition-all duration-300 hover:scale-105"
aria-label="ThrillWiki Home">
<span class="bg-gradient-to-r from-thrill-primary via-purple-500 to-pink-500 bg-clip-text text-transparent">
ThrillWiki
</span>
</a>
<!-- Search Results Dropdown -->
<div
id="search-results"
x-show="results.length > 0"
x-transition
x-cloak
class="absolute top-full left-0 right-0 mt-1 bg-background border rounded-md shadow-lg z-50 max-h-96 overflow-y-auto"
>
<!-- Search results will be populated by HTMX -->
<!-- Beta Badge -->
<div class="hidden sm:block">
<span class="badge badge-info text-xs pulse-glow">
<i class="fas fa-rocket mr-1"></i>
Beta
</span>
</div>
</div>
</div>
<!-- Desktop Right Side -->
<div class="hidden md:flex items-center gap-4 shrink-0">
<!-- Search Button -->
<button
type="submit"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4"
>
Search
</button>
<!-- Theme Toggle -->
<button
x-data="themeToggle"
@click="toggleTheme()"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-12 w-12"
>
<i class="fas fa-sun h-5 w-5 md:h-7 md:w-7 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 text-lg"></i>
<i class="fas fa-moon absolute h-5 w-5 md:h-7 md:w-7 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-lg"></i>
<span class="sr-only">Toggle theme</span>
</button>
<!-- User Icon -->
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
<button
@click="open = !open"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-12 w-12"
>
{% if user.is_authenticated %}
{% if user.profile.avatar %}
<img
src="{{ user.profile.avatar.url }}"
alt="{{ user.get_full_name|default:user.username }}"
class="h-8 w-8 rounded-full object-cover"
/>
{% else %}
<div class="h-8 w-8 rounded-full bg-primary flex items-center justify-center text-primary-foreground text-sm font-medium">
{{ user.get_full_name.0|default:user.username.0|upper }}
</div>
{% endif %}
{% else %}
<i class="fas fa-user h-5 w-5 text-lg"></i>
{% endif %}
</button>
<!-- User Dropdown -->
<div
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
x-cloak
class="absolute right-0 top-full mt-2 w-48 bg-background border rounded-md shadow-lg z-50"
>
{% if user.is_authenticated %}
<div class="flex items-center justify-start gap-2 p-3">
<div class="flex flex-col space-y-1 leading-none">
<p class="font-medium">{{ user.get_full_name|default:user.username }}</p>
<p class="w-[180px] truncate text-sm text-muted-foreground">{{ user.email }}</p>
</div>
</div>
<div class="border-t"></div>
<a href="{% url 'profile' user.username %}" class="flex items-center px-3 py-2 text-sm hover:bg-accent" @click="open = false">
<i class="fas fa-user mr-2 h-4 w-4"></i>
Profile
</a>
<a href="{% url 'settings' %}" class="flex items-center px-3 py-2 text-sm hover:bg-accent" @click="open = false">
<i class="fas fa-cog mr-2 h-4 w-4"></i>
Settings
</a>
{% if has_moderation_access %}
<a href="{% url 'moderation:dashboard' %}" class="flex items-center px-3 py-2 text-sm hover:bg-accent" @click="open = false">
<i class="fas fa-shield-alt mr-2 h-4 w-4"></i>
Moderation
</a>
{% endif %}
<div class="border-t"></div>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
<button type="submit" class="flex items-center w-full px-3 py-2 text-sm text-red-600 hover:bg-accent">
<i class="fas fa-sign-out-alt mr-2 h-4 w-4"></i>
Log out
</button>
</form>
{% else %}
<div class="p-2">
<button
@click="window.authModal.show('login'); open = false"
class="flex items-center w-full px-3 py-2 text-sm hover:bg-accent rounded-md"
>
<i class="fas fa-sign-in-alt mr-2 h-4 w-4"></i>
Login
</button>
<button
@click="window.authModal.show('register'); open = false"
class="flex items-center w-full px-3 py-2 text-sm hover:bg-accent rounded-md"
>
<i class="fas fa-user-plus mr-2 h-4 w-4"></i>
Register
</button>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Mobile Menu -->
<div class="md:hidden flex items-center space-x-2 flex-shrink-0">
<!-- Theme Toggle (Mobile) -->
<div x-data="themeToggle">
<button
@click="toggleTheme()"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-10 w-10"
>
<i class="fas fa-sun h-6 w-6 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"></i>
<i class="fas fa-moon absolute h-6 w-6 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"></i>
</button>
</div>
<!-- Mobile User Menu -->
{% if user.is_authenticated %}
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
<button @click="open = !open" class="relative h-8 w-8 rounded-full">
{% if user.profile.avatar %}
<img
src="{{ user.profile.avatar.url }}"
alt="{{ user.get_full_name|default:user.username }}"
class="h-8 w-8 rounded-full object-cover"
/>
{% else %}
<div class="h-8 w-8 rounded-full bg-primary flex items-center justify-center text-primary-foreground text-sm font-medium">
{{ user.get_full_name.0|default:user.username.0|upper }}
</div>
{% endif %}
</button>
<!-- Mobile User Dropdown -->
<div
x-show="open"
x-transition
x-cloak
class="absolute right-0 mt-2 w-56 bg-background border rounded-md shadow-lg z-50"
>
<div class="flex items-center justify-start gap-2 p-2">
<div class="flex flex-col space-y-1 leading-none">
<p class="font-medium">{{ user.get_full_name|default:user.username }}</p>
<p class="w-[200px] truncate text-sm text-muted-foreground">{{ user.email }}</p>
</div>
</div>
<div class="border-t"></div>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
<button type="submit" class="flex items-center w-full px-2 py-2 text-sm text-red-600 hover:bg-accent">
<i class="fas fa-sign-out-alt mr-2 h-4 w-4"></i>
Log out
</button>
</form>
</div>
</div>
{% else %}
<div class="flex items-center gap-1">
<c-button
variant="outline"
size="default"
hx_get="{% url 'account_login' %}"
hx_target="body"
hx_swap="beforeend"
>
Login
</c-button>
<c-button
variant="default"
size="default"
hx_get="{% url 'account_signup' %}"
hx_target="body"
hx_swap="beforeend"
>
Join
</c-button>
</div>
{% endif %}
<!-- Mobile Menu Button -->
<div x-data="{ open: false }">
<button
@click="open = !open"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-10 w-10"
>
<i class="fas fa-bars h-5 w-5"></i>
</button>
<!-- Mobile Menu Overlay -->
<div
x-show="open"
x-transition:enter="transition-opacity ease-linear duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition-opacity ease-linear duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
x-cloak
class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm"
@click="open = false"
>
<!-- Mobile Menu Panel -->
<div
x-show="open"
x-transition:enter="transition ease-in-out duration-300 transform"
x-transition:enter-start="translate-x-full"
x-transition:enter-end="translate-x-0"
x-transition:leave="transition ease-in-out duration-300 transform"
x-transition:leave-start="translate-x-0"
x-transition:leave-end="translate-x-full"
class="fixed right-0 top-0 h-full w-full sm:w-96 bg-background border-l shadow-lg"
@click.stop
>
<div class="flex flex-col h-full">
<!-- Mobile Menu Header -->
<div class="flex items-center justify-between p-4 border-b">
<div class="flex items-center space-x-2">
<div class="w-6 h-6 bg-purple-600 rounded flex items-center justify-center">
<span class="text-white text-xs font-bold">TW</span>
</div>
<span class="font-bold text-lg">ThrillWiki</span>
<!-- Desktop Navigation -->
<div class="hidden lg:flex items-center space-x-8">
<!-- Main Navigation Links -->
<div class="flex items-center space-x-6">
<a href="{% url 'parks:list' %}"
class="nav-link group relative"
hx-get="{% url 'parks:list' %}"
hx-target="#main-content"
hx-swap="innerHTML transition:true">
<i class="fas fa-map-marked-alt mr-2 text-thrill-primary"></i>
Parks
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-thrill-primary to-purple-500 transition-all duration-300 group-hover:w-full"></span>
</a>
<a href="{% url 'rides:list' %}"
class="nav-link group relative"
hx-get="{% url 'rides:list' %}"
hx-target="#main-content"
hx-swap="innerHTML transition:true">
<i class="fas fa-rocket mr-2 text-thrill-secondary"></i>
Rides
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-thrill-secondary to-red-500 transition-all duration-300 group-hover:w-full"></span>
</a>
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
<button class="nav-link group relative flex items-center"
@click="open = !open">
<i class="fas fa-compass mr-2 text-thrill-success"></i>
Explore
<i class="fas fa-chevron-down ml-2 text-xs transition-transform duration-200" :class="open ? 'rotate-180' : ''"></i>
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-thrill-success to-teal-500 transition-all duration-300 group-hover:w-full"></span>
</button>
<!-- Dropdown Menu -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="absolute top-full left-0 mt-2 w-64 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl shadow-xl border border-neutral-200/50 dark:border-neutral-700/50 py-2"
@click.away="open = false">
<a href="{% url 'parks:trending' %}"
class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<div class="w-8 h-8 bg-gradient-to-r from-thrill-primary to-purple-500 rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-fire text-white text-xs"></i>
</div>
<button
@click="open = false"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-10 w-10"
>
<i class="fas fa-times h-5 w-5"></i>
<div>
<div class="font-semibold">Trending Parks</div>
<div class="text-xs text-neutral-500">Most popular destinations</div>
</div>
</a>
<a href="{% url 'rides:new' %}"
class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<div class="w-8 h-8 bg-gradient-to-r from-thrill-secondary to-red-500 rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-plus text-white text-xs"></i>
</div>
<div>
<div class="font-semibold">New Attractions</div>
<div class="text-xs text-neutral-500">Latest additions</div>
</div>
</a>
<a href="{% url 'search:advanced' %}"
class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<div class="w-8 h-8 bg-gradient-to-r from-thrill-success to-teal-500 rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-search text-white text-xs"></i>
</div>
<div>
<div class="font-semibold">Advanced Search</div>
<div class="text-xs text-neutral-500">Find exactly what you want</div>
</div>
</a>
</div>
</div>
</div>
<!-- Search Bar -->
<div class="relative" x-data="searchComponent()">
<div class="relative">
<input type="text"
x-model="query"
@input="handleInput()"
placeholder="Search parks, rides, locations..."
class="w-80 pl-12 pr-4 py-3 bg-white/80 dark:bg-neutral-800/80 backdrop-blur-sm border border-neutral-300/50 dark:border-neutral-600/50 rounded-xl text-sm transition-all duration-300 focus:w-96 focus:bg-white dark:focus:bg-neutral-800 focus:border-thrill-primary focus:ring-2 focus:ring-thrill-primary/20 focus:shadow-lg"
hx-get="{% url 'parks:search_parks' %}"
hx-trigger="keyup changed delay:300ms"
hx-target="#search-results"
hx-include="this"
hx-vals='{"quick_search": "true"}'
name="search">
<div class="absolute left-4 top-1/2 transform -translate-y-1/2">
<i class="fas fa-search text-neutral-400 transition-colors duration-300"
:class="loading ? 'fa-spinner fa-spin text-thrill-primary' : 'text-thrill-primary'"></i>
</div>
<button x-show="query.length > 0"
@click="clearSearch()"
class="absolute right-4 top-1/2 transform -translate-y-1/2 text-neutral-400 hover:text-neutral-600 transition-colors">
<i class="fas fa-times"></i>
</button>
</div>
<!-- Search Results Dropdown -->
<div id="search-results"
x-show="showResults"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="absolute top-full left-0 right-0 mt-2 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl shadow-xl border border-neutral-200/50 dark:border-neutral-700/50 max-h-96 overflow-y-auto z-50"
@click.away="showResults = false">
<!-- Dynamic search results will be loaded here via HTMX -->
</div>
</div>
<!-- Action Buttons -->
<div class="flex items-center space-x-4">
<!-- Theme Toggle -->
<button class="p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-800/50 transition-colors"
onclick="toggleTheme()"
aria-label="Toggle theme">
<i class="fas fa-moon dark:hidden text-neutral-600"></i>
<i class="fas fa-sun hidden dark:block text-yellow-400"></i>
</button>
<!-- Notifications -->
<div class="relative" x-data="{ open: false }">
<button class="p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-800/50 transition-colors relative"
@click="open = !open"
aria-label="Notifications">
<i class="fas fa-bell text-neutral-600 dark:text-neutral-400"></i>
<span class="absolute -top-1 -right-1 w-3 h-3 bg-thrill-danger rounded-full animate-pulse"></span>
</button>
<!-- Notifications Dropdown -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="absolute top-full right-0 mt-2 w-80 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl shadow-xl border border-neutral-200/50 dark:border-neutral-700/50 py-4"
@click.away="open = false">
<div class="px-4 pb-2 border-b border-neutral-200/50 dark:border-neutral-700/50">
<h3 class="font-semibold text-lg">Notifications</h3>
</div>
<div class="py-2 max-h-64 overflow-y-auto">
<div class="px-4 py-3 hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<div class="flex items-start space-x-3">
<div class="w-2 h-2 bg-thrill-primary rounded-full mt-2 flex-shrink-0"></div>
<div class="flex-1">
<p class="text-sm font-medium">New park added: Universal Epic Universe</p>
<p class="text-xs text-neutral-500 mt-1">2 hours ago</p>
</div>
</div>
</div>
<div class="px-4 py-3 hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<div class="flex items-start space-x-3">
<div class="w-2 h-2 bg-thrill-secondary rounded-full mt-2 flex-shrink-0"></div>
<div class="flex-1">
<p class="text-sm font-medium">Ride update: Steel Vengeance reopened</p>
<p class="text-xs text-neutral-500 mt-1">1 day ago</p>
</div>
</div>
</div>
</div>
<div class="px-4 pt-2 border-t border-neutral-200/50 dark:border-neutral-700/50">
<button class="text-sm text-thrill-primary hover:text-thrill-primary-dark transition-colors">
View all notifications
</button>
</div>
</div>
</div>
<!-- Mobile Menu Content -->
<div class="flex-1 overflow-y-auto p-4 space-y-6">
<p class="text-sm text-muted-foreground">
Navigate through the ultimate theme park database
</p>
<!-- Navigation Section -->
<div>
<h3 class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">
NAVIGATION
</h3>
<div class="space-y-1">
<a href="{% url 'home' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-home w-4 h-4"></i>
<span>Home</span>
</a>
<a href="{% url 'search:search' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-search w-4 h-4"></i>
<span>Search</span>
</a>
<a href="{% url 'parks:park_list' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-map-marker-alt w-4 h-4"></i>
<span>Parks</span>
</a>
<a href="{% url 'rides:global_ride_list' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-rocket w-4 h-4"></i>
<span>Rides</span>
</a>
<a href="{% url 'rides:manufacturer_list' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-wrench w-4 h-4"></i>
<span>Manufacturers</span>
</a>
<a href="{% url 'parks:operator_list' %}" class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent transition-colors" @click="open = false">
<i class="fas fa-building w-4 h-4"></i>
<span>Operators</span>
</a>
</div>
<!-- User Menu -->
{% if user.is_authenticated %}
<div class="relative" x-data="{ open: false }">
<button class="flex items-center space-x-2 p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-800/50 transition-colors"
@click="open = !open">
{% if user.avatar %}
<img src="{{ user.avatar.url }}" alt="{{ user.username }}" class="w-8 h-8 rounded-full object-cover">
{% else %}
<div class="w-8 h-8 bg-gradient-to-r from-thrill-primary to-purple-500 rounded-full flex items-center justify-center">
<span class="text-white text-sm font-semibold">{{ user.username|first|upper }}</span>
</div>
{% endif %}
<i class="fas fa-chevron-down text-xs transition-transform duration-200" :class="open ? 'rotate-180' : ''"></i>
</button>
<!-- User Dropdown -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="absolute top-full right-0 mt-2 w-64 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl shadow-xl border border-neutral-200/50 dark:border-neutral-700/50 py-2"
@click.away="open = false">
<div class="px-4 py-3 border-b border-neutral-200/50 dark:border-neutral-700/50">
<p class="font-semibold">{{ user.get_full_name|default:user.username }}</p>
<p class="text-sm text-neutral-500">{{ user.email }}</p>
</div>
<a href="{% url 'accounts:profile' %}" class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<i class="fas fa-user mr-3 text-thrill-primary"></i>
Profile
</a>
<a href="{% url 'accounts:settings' %}" class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<i class="fas fa-cog mr-3 text-neutral-500"></i>
Settings
</a>
<a href="{% url 'accounts:favorites' %}" class="flex items-center px-4 py-3 text-sm hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<i class="fas fa-heart mr-3 text-red-500"></i>
Favorites
</a>
<div class="border-t border-neutral-200/50 dark:border-neutral-700/50 mt-2 pt-2">
<form method="post" action="{% url 'accounts:logout' %}">
{% csrf_token %}
<button type="submit" class="flex items-center w-full px-4 py-3 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
<i class="fas fa-sign-out-alt mr-3"></i>
Sign Out
</button>
</form>
</div>
</div>
</div>
{% else %}
<div class="flex items-center space-x-3">
<button class="btn-ghost btn-sm"
onclick="openAuthModal('login')">
Sign In
</button>
<button class="btn-primary btn-sm"
onclick="openAuthModal('register')">
Join Now
</button>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Mobile Search Bar -->
<div class="md:hidden border-t bg-background">
<div class="px-4 py-3">
<div class="flex items-center gap-2 w-full">
<div class="relative flex-1">
<i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground"></i>
<input
type="search"
placeholder="Search parks, rides..."
class="w-full pl-10 pr-3 h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
hx-get="{% url 'search:search' %}"
hx-trigger="input changed delay:300ms"
hx-target="#mobile-search-results"
hx-include="this"
name="q"
/>
<!-- Mobile Menu Button -->
<button class="lg:hidden p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-800/50 transition-colors"
@click="isOpen = !isOpen"
aria-label="Toggle mobile menu">
<div class="w-6 h-6 relative">
<span class="absolute top-1 left-0 w-6 h-0.5 bg-current transition-all duration-300"
:class="isOpen ? 'rotate-45 top-2.5' : ''"></span>
<span class="absolute top-2.5 left-0 w-6 h-0.5 bg-current transition-all duration-300"
:class="isOpen ? 'opacity-0' : ''"></span>
<span class="absolute top-4 left-0 w-6 h-0.5 bg-current transition-all duration-300"
:class="isOpen ? '-rotate-45 top-2.5' : ''"></span>
</div>
<button type="submit" class="flex-shrink-0 inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 w-10">
<i class="fas fa-search h-4 w-4"></i>
</button>
</div>
<div id="mobile-search-results" class="mt-2"></div>
</button>
</div>
</div>
<!-- Mobile Navigation Menu -->
<div x-show="isOpen"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform -translate-y-4"
x-transition:enter-end="opacity-100 transform translate-y-0"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 transform translate-y-0"
x-transition:leave-end="opacity-0 transform -translate-y-4"
class="lg:hidden mt-6 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl border border-neutral-200/50 dark:border-neutral-700/50 p-6">
<!-- Mobile Search -->
<div class="mb-6" x-data="searchComponent()">
<div class="relative">
<input type="text"
x-model="query"
@input="handleInput()"
placeholder="Search parks, rides, locations..."
class="w-full pl-12 pr-4 py-3 bg-neutral-100/50 dark:bg-neutral-700/50 border border-neutral-300/50 dark:border-neutral-600/50 rounded-xl text-sm"
hx-get="{% url 'parks:search_parks' %}"
hx-trigger="keyup changed delay:300ms"
hx-target="#mobile-search-results"
hx-include="this"
hx-vals='{"quick_search": "true"}'
name="search">
<div class="absolute left-4 top-1/2 transform -translate-y-1/2">
<i class="fas fa-search text-neutral-400 transition-colors duration-300"
:class="loading ? 'fa-spinner fa-spin text-thrill-primary' : ''"></i>
</div>
<button x-show="query.length > 0"
@click="clearSearch()"
class="absolute right-4 top-1/2 transform -translate-y-1/2 text-neutral-400 hover:text-neutral-600 transition-colors">
<i class="fas fa-times"></i>
</button>
</div>
<!-- Mobile Search Results -->
<div id="mobile-search-results"
x-show="showResults"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="mt-2 bg-white/95 dark:bg-neutral-800/95 backdrop-blur-xl rounded-2xl shadow-xl border border-neutral-200/50 dark:border-neutral-700/50 max-h-64 overflow-y-auto"
@click.away="showResults = false">
<!-- Dynamic search results will be loaded here via HTMX -->
</div>
</div>
<!-- Mobile Navigation Links -->
<div class="space-y-4">
<a href="{% url 'parks:list' %}"
class="flex items-center p-3 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors"
@click="isOpen = false">
<i class="fas fa-map-marked-alt mr-3 text-thrill-primary"></i>
<span class="font-medium">Parks</span>
</a>
<a href="{% url 'rides:list' %}"
class="flex items-center p-3 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors"
@click="isOpen = false">
<i class="fas fa-rocket mr-3 text-thrill-secondary"></i>
<span class="font-medium">Rides</span>
</a>
<a href="{% url 'search:advanced' %}"
class="flex items-center p-3 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors"
@click="isOpen = false">
<i class="fas fa-search mr-3 text-thrill-success"></i>
<span class="font-medium">Advanced Search</span>
</a>
</div>
<!-- Mobile User Actions -->
<div class="mt-6 pt-6 border-t border-neutral-200/50 dark:border-neutral-700/50">
{% if user.is_authenticated %}
<div class="flex items-center space-x-3 mb-4">
{% if user.avatar %}
<img src="{{ user.avatar.url }}" alt="{{ user.username }}" class="w-10 h-10 rounded-full object-cover">
{% else %}
<div class="w-10 h-10 bg-gradient-to-r from-thrill-primary to-purple-500 rounded-full flex items-center justify-center">
<span class="text-white font-semibold">{{ user.username|first|upper }}</span>
</div>
{% endif %}
<div>
<p class="font-semibold">{{ user.get_full_name|default:user.username }}</p>
<p class="text-sm text-neutral-500">{{ user.email }}</p>
</div>
</div>
<div class="space-y-2">
<a href="{% url 'accounts:profile' %}" class="flex items-center p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<i class="fas fa-user mr-3 text-thrill-primary"></i>
Profile
</a>
<a href="{% url 'accounts:settings' %}" class="flex items-center p-2 rounded-lg hover:bg-neutral-100/50 dark:hover:bg-neutral-700/50 transition-colors">
<i class="fas fa-cog mr-3 text-neutral-500"></i>
Settings
</a>
<form method="post" action="{% url 'accounts:logout' %}">
{% csrf_token %}
<button type="submit" class="flex items-center w-full p-2 rounded-lg text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
<i class="fas fa-sign-out-alt mr-3"></i>
Sign Out
</button>
</form>
</div>
{% else %}
<div class="space-y-3">
<button class="btn-primary w-full" onclick="openAuthModal('register')">
Join ThrillWiki
</button>
<button class="btn-secondary w-full" onclick="openAuthModal('login')">
Sign In
</button>
</div>
{% endif %}
</div>
</div>
</nav>
</header>
<!-- Spacer to prevent content from hiding behind fixed header -->
<div class="h-20"></div>