feat: Implement UI components for Django templates

- Added Button component with various styles and sizes.
- Introduced Card component for displaying content with titles and descriptions.
- Created Input component for form fields with support for various attributes.
- Developed Toast Notification Container for displaying alerts and messages.
- Designed pages for listing designers and operators with pagination and responsive layout.
- Documented frontend migration from React to HTMX + Alpine.js, detailing component usage and integration.
This commit is contained in:
pacnpal
2025-09-19 19:04:37 -04:00
parent 209b433577
commit 42a3dc7637
27 changed files with 3855 additions and 284 deletions

View File

@@ -33,11 +33,15 @@
<!-- Alpine.js -->
<script defer src="{% static 'js/alpine.min.js' %}"></script>
<!-- Alpine.js Components -->
<script src="{% static 'js/alpine-components.js' %}"></script>
<!-- Location Autocomplete -->
<script src="{% static 'js/location-autocomplete.js' %}"></script>
<!-- Tailwind CSS -->
<link href="{% static 'css/tailwind.css' %}" rel="stylesheet" />
<link href="{% static 'css/components.css' %}" rel="stylesheet" />
<link href="{% static 'css/alerts.css' %}" rel="stylesheet" />
<!-- Font Awesome -->
@@ -77,201 +81,8 @@
<body
class="flex flex-col min-h-screen text-gray-900 bg-gradient-to-br from-white via-blue-50 to-indigo-50 dark:from-gray-950 dark:via-indigo-950 dark:to-purple-950 dark:text-white"
>
<!-- Header -->
<header
class="sticky top-0 z-40 border-b shadow-lg bg-white/90 dark:bg-gray-800/90 backdrop-blur-lg border-gray-200/50 dark:border-gray-700/50"
>
<nav class="container mx-auto nav-container">
<div class="flex items-center justify-between">
<!-- Logo -->
<div class="flex items-center">
<a
href="{% url 'home' %}"
class="font-bold text-transparent transition-transform site-logo bg-gradient-to-r from-primary to-secondary bg-clip-text hover:scale-105"
>
ThrillWiki
</a>
</div>
<!-- Navigation Links (Always Visible) -->
<div class="flex items-center space-x-2 sm:space-x-4">
<a href="{% url 'parks:park_list' %}" class="nav-link">
<i class="fas fa-map-marker-alt"></i>
<span>Parks</span>
</a>
<a href="{% url 'rides:global_ride_list' %}" class="nav-link">
<i class="fas fa-rocket"></i>
<span>Rides</span>
</a>
</div>
<!-- Search Bar -->
<div class="flex-1 hidden max-w-md mx-8 lg:flex">
<form action="{% url 'search:search' %}" method="get" class="w-full">
<div class="relative">
<input
type="text"
name="q"
placeholder="Search parks and rides..."
class="form-input"
/>
</div>
</form>
</div>
<!-- Right Side Menu -->
<div class="flex items-center space-x-2 sm:space-x-6">
<!-- Theme Toggle -->
<label for="theme-toggle" class="cursor-pointer">
<input type="checkbox" id="theme-toggle" class="hidden" />
<div
class="inline-flex items-center justify-center p-2 text-gray-500 transition-colors hover:text-primary dark:text-gray-400 dark:hover:text-primary theme-toggle-btn"
role="button"
aria-label="Toggle dark mode"
>
<i class="text-xl fas"></i>
</div>
</label>
<!-- User Menu -->
{% if user.is_authenticated %} {% if has_moderation_access %}
<a href="{% url 'moderation:dashboard' %}" class="nav-link">
<i class="fas fa-shield-alt"></i>
<span>Moderation</span>
</a>
{% endif %}
<div
class="relative"
x-data="{ open: false }"
@click.outside="open = false"
>
<!-- Profile Picture Button -->
{% if user.profile.avatar %}
<img
@click="open = !open"
src="{{ user.profile.avatar.url }}"
alt="{{ user.username }}"
class="w-8 h-8 transition-transform rounded-full cursor-pointer ring-2 ring-primary/20 hover:scale-105"
/>
{% else %}
<div
@click="open = !open"
class="flex items-center justify-center w-8 h-8 text-white transition-transform rounded-full cursor-pointer bg-gradient-to-br from-primary to-secondary hover:scale-105"
>
{{ user.username.0|upper }}
</div>
{% endif %}
<!-- Dropdown Menu -->
<div
x-cloak
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"
class="bg-white dropdown-menu dark:bg-gray-800"
>
<a href="{% url 'profile' user.username %}" class="menu-item">
<i class="w-5 fas fa-user"></i>
<span>Profile</span>
</a>
<a href="{% url 'settings' %}" class="menu-item">
<i class="w-5 fas fa-cog"></i>
<span>Settings</span>
</a>
{% if has_admin_access %}
<a href="{% url 'admin:index' %}" class="menu-item">
<i class="w-5 fas fa-shield-alt"></i>
<span>Admin</span>
</a>
{% endif %}
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
<button type="submit" class="w-full menu-item">
<i class="w-5 fas fa-sign-out-alt"></i>
<span>Logout</span>
</button>
</form>
</div>
</div>
{% else %}
<!-- Generic Profile Icon for Unauthenticated Users -->
<div
class="relative"
x-data="{ open: false }"
@click.outside="open = false"
>
<div
@click="open = !open"
class="flex items-center justify-center w-8 h-8 text-gray-500 transition-transform rounded-full cursor-pointer hover:text-primary dark:text-gray-400 dark:hover:text-primary hover:scale-105"
>
<i class="text-xl fas fa-user"></i>
</div>
<!-- Auth Menu -->
<div
x-cloak
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"
class="bg-white dropdown-menu dark:bg-gray-800"
>
<div
hx-get="{% url 'account_login' %}"
hx-target="body"
hx-swap="beforeend"
class="cursor-pointer menu-item"
>
<i class="w-5 fas fa-sign-in-alt"></i>
<span>Login</span>
</div>
<div
hx-get="{% url 'account_signup' %}"
hx-target="body"
hx-swap="beforeend"
class="cursor-pointer menu-item"
>
<i class="w-5 fas fa-user-plus"></i>
<span>Register</span>
</div>
</div>
</div>
{% endif %}
<!-- Mobile Menu Button -->
<button
id="mobileMenuBtn"
class="p-2 text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-400"
aria-label="Toggle mobile menu"
>
<i class="text-2xl fas fa-bars"></i>
</button>
</div>
</div>
<!-- Mobile Menu -->
<div id="mobileMenu">
<div class="space-y-4">
<!-- Search (Mobile) -->
<form action="{% url 'search:search' %}" method="get" class="mb-4">
<input
type="text"
name="q"
placeholder="Search parks and rides..."
class="form-input"
/>
</form>
</div>
</div>
</nav>
</header>
<!-- Enhanced Header -->
{% include 'components/layout/enhanced_header.html' %}
<!-- Flash Messages -->
{% if messages %}
@@ -316,9 +127,15 @@
</div>
</footer>
<!-- Global Auth Modal -->
{% include 'components/auth/auth-modal.html' %}
<!-- Global Toast Container -->
{% include 'components/ui/toast-container.html' %}
<!-- Custom JavaScript -->
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/alerts.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
</html>