mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 12:31:22 -05:00
lol
This commit is contained in:
@@ -6,7 +6,7 @@ from django.utils import timezone
|
|||||||
import json
|
import json
|
||||||
from .models import EditSubmission, PhotoSubmission
|
from .models import EditSubmission, PhotoSubmission
|
||||||
|
|
||||||
class EditSubmissionMixin(LoginRequiredMixin):
|
class EditSubmissionMixin:
|
||||||
"""
|
"""
|
||||||
Mixin for handling edit submissions with proper moderation.
|
Mixin for handling edit submissions with proper moderation.
|
||||||
"""
|
"""
|
||||||
@@ -67,6 +67,12 @@ class EditSubmissionMixin(LoginRequiredMixin):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""Handle POST requests for editing"""
|
"""Handle POST requests for editing"""
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'You must be logged in to make edits.'
|
||||||
|
}, status=403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
changes = data.get('changes', {})
|
changes = data.get('changes', {})
|
||||||
@@ -101,12 +107,18 @@ class EditSubmissionMixin(LoginRequiredMixin):
|
|||||||
'message': str(e)
|
'message': str(e)
|
||||||
}, status=500)
|
}, status=500)
|
||||||
|
|
||||||
class PhotoSubmissionMixin(LoginRequiredMixin):
|
class PhotoSubmissionMixin:
|
||||||
"""
|
"""
|
||||||
Mixin for handling photo submissions with proper moderation.
|
Mixin for handling photo submissions with proper moderation.
|
||||||
"""
|
"""
|
||||||
def handle_photo_submission(self, request):
|
def handle_photo_submission(self, request):
|
||||||
"""Handle a photo submission based on user's role"""
|
"""Handle a photo submission based on user's role"""
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'You must be logged in to upload photos.'
|
||||||
|
}, status=403)
|
||||||
|
|
||||||
if not request.FILES.get('photo'):
|
if not request.FILES.get('photo'):
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,5 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from . import views
|
from . import views
|
||||||
from rides.views import RideDetailView
|
|
||||||
|
|
||||||
app_name = 'parks'
|
app_name = 'parks'
|
||||||
|
|
||||||
@@ -8,5 +7,4 @@ urlpatterns = [
|
|||||||
path('', views.ParkListView.as_view(), name='park_list'),
|
path('', views.ParkListView.as_view(), name='park_list'),
|
||||||
path('create/', views.ParkCreateView.as_view(), name='park_create'),
|
path('create/', views.ParkCreateView.as_view(), name='park_create'),
|
||||||
path('<slug:slug>/', views.ParkDetailView.as_view(), name='park_detail'),
|
path('<slug:slug>/', views.ParkDetailView.as_view(), name='park_detail'),
|
||||||
path('<slug:park_slug>/<slug:ride_slug>/', RideDetailView.as_view(), name='ride_detail'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
|||||||
reason=self.request.POST.get('reason', ''),
|
reason=self.request.POST.get('reason', ''),
|
||||||
source=self.request.POST.get('source', '')
|
source=self.request.POST.get('source', '')
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(reverse('parks:park_list'))
|
return HttpResponseRedirect(reverse('park_list'))
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('parks:park_detail', kwargs={'slug': self.object.slug})
|
return reverse('park_detail', kwargs={'slug': self.object.slug})
|
||||||
|
|
||||||
class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
|
class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
|
||||||
model = Park
|
model = Park
|
||||||
@@ -63,7 +63,7 @@ class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixi
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_redirect_url_pattern(self):
|
def get_redirect_url_pattern(self):
|
||||||
return 'parks:park_detail'
|
return 'park_detail'
|
||||||
|
|
||||||
class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
|
class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
|
||||||
model = ParkArea
|
model = ParkArea
|
||||||
@@ -90,7 +90,7 @@ class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmission
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_redirect_url_pattern(self):
|
def get_redirect_url_pattern(self):
|
||||||
return 'parks:area_detail'
|
return 'park_detail'
|
||||||
|
|
||||||
def get_redirect_url_kwargs(self):
|
def get_redirect_url_kwargs(self):
|
||||||
return {
|
return {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -6,5 +6,4 @@ app_name = 'rides'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.RideListView.as_view(), name='ride_list'),
|
path('', views.RideListView.as_view(), name='ride_list'),
|
||||||
path('create/', views.RideCreateView.as_view(), name='ride_create'),
|
path('create/', views.RideCreateView.as_view(), name='ride_create'),
|
||||||
path('<slug:park_slug>/<slug:ride_slug>/', views.RideDetailView.as_view(), name='ride_detail'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ class RideCreateView(LoginRequiredMixin, CreateView):
|
|||||||
reason=self.request.POST.get('reason', ''),
|
reason=self.request.POST.get('reason', ''),
|
||||||
source=self.request.POST.get('source', '')
|
source=self.request.POST.get('source', '')
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(reverse('rides:ride_list'))
|
return HttpResponseRedirect(reverse('ride_list'))
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('rides:ride_detail', kwargs={
|
return reverse('ride_detail', kwargs={
|
||||||
'park_slug': self.object.park.slug,
|
'park_slug': self.object.park.slug,
|
||||||
'ride_slug': self.object.slug
|
'ride_slug': self.object.slug
|
||||||
})
|
})
|
||||||
@@ -75,7 +75,7 @@ class RideDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixi
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_redirect_url_pattern(self):
|
def get_redirect_url_pattern(self):
|
||||||
return 'rides:ride_detail'
|
return 'ride_detail'
|
||||||
|
|
||||||
def get_redirect_url_kwargs(self):
|
def get_redirect_url_kwargs(self):
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2943,14 +2943,14 @@ select {
|
|||||||
--tw-gradient-stops: var(--tw-gradient-from), #eff6ff var(--tw-gradient-via-position), var(--tw-gradient-to);
|
--tw-gradient-stops: var(--tw-gradient-from), #eff6ff var(--tw-gradient-via-position), var(--tw-gradient-to);
|
||||||
}
|
}
|
||||||
|
|
||||||
.to-secondary {
|
|
||||||
--tw-gradient-to: #e11d48 var(--tw-gradient-to-position);
|
|
||||||
}
|
|
||||||
|
|
||||||
.to-indigo-50 {
|
.to-indigo-50 {
|
||||||
--tw-gradient-to: #eef2ff var(--tw-gradient-to-position);
|
--tw-gradient-to: #eef2ff var(--tw-gradient-to-position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.to-secondary {
|
||||||
|
--tw-gradient-to: #e11d48 var(--tw-gradient-to-position);
|
||||||
|
}
|
||||||
|
|
||||||
.bg-clip-text {
|
.bg-clip-text {
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
|
|||||||
@@ -8,17 +8,15 @@
|
|||||||
id="turnstile-widget"
|
id="turnstile-widget"
|
||||||
class="cf-turnstile"
|
class="cf-turnstile"
|
||||||
data-sitekey="{{ site_key }}"
|
data-sitekey="{{ site_key }}"
|
||||||
data-theme="dark"
|
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Apply theme to the Turnstile widget based on system preference
|
// Apply theme to the Turnstile widget based on the retrieved theme
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
const turnstileWidget = document.getElementById("turnstile-widget");
|
const turnstileWidget = document.getElementById("turnstile-widget");
|
||||||
if (turnstileWidget) {
|
if (turnstileWidget) {
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
turnstileWidget.setAttribute("data-theme", theme);
|
||||||
turnstileWidget.setAttribute("data-theme", prefersDark ? "dark" : "light");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="csrf-token" content="{{ csrf_token }}">
|
<meta name="csrf-token" content="{{ csrf_token }}" />
|
||||||
<title>{% block title %}ThrillWiki{% endblock %}</title>
|
<title>{% block title %}ThrillWiki{% endblock %}</title>
|
||||||
|
|
||||||
<!-- Google Fonts -->
|
<!-- Google Fonts -->
|
||||||
@@ -101,78 +101,74 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- User Menu -->
|
<!-- User Menu -->
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %} {% if has_moderation_access %}
|
||||||
{% if has_moderation_access %}
|
<a href="{% url 'moderation:edit_submissions' %}" class="nav-link">
|
||||||
<a href="{% url 'moderation:edit_submissions' %}" class="nav-link">
|
<i class="fas fa-shield-alt"></i>
|
||||||
<i class="fas fa-shield-alt"></i>
|
<span>Moderation</span>
|
||||||
<span>Moderation</span>
|
</a>
|
||||||
</a>
|
{% endif %}
|
||||||
{% endif %}
|
<div class="relative" x-data="{ open: false }">
|
||||||
<div class="relative" x-data="{ open: false }">
|
<button
|
||||||
<button
|
@click="open = !open"
|
||||||
@click="open = !open"
|
class="flex items-center space-x-2 transition-transform hover:scale-105"
|
||||||
class="flex items-center space-x-2 transition-transform hover:scale-105"
|
>
|
||||||
>
|
{% if user.profile.avatar %}
|
||||||
{% if user.profile.avatar %}
|
<img
|
||||||
<img
|
src="{{ user.profile.avatar.url }}"
|
||||||
src="{{ user.profile.avatar.url }}"
|
alt="{{ user.username }}"
|
||||||
alt="{{ user.username }}"
|
class="w-8 h-8 rounded-full ring-2 ring-primary/20"
|
||||||
class="w-8 h-8 rounded-full ring-2 ring-primary/20"
|
/>
|
||||||
/>
|
{% else %}
|
||||||
{% else %}
|
|
||||||
<div
|
|
||||||
class="flex items-center justify-center w-8 h-8 text-white rounded-full bg-gradient-to-br from-primary to-secondary"
|
|
||||||
>
|
|
||||||
{{ user.username.0|upper }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<span>{{ user.username }}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Dropdown Menu -->
|
|
||||||
<div
|
<div
|
||||||
x-show="open"
|
class="flex items-center justify-center w-8 h-8 text-white rounded-full bg-gradient-to-br from-primary to-secondary"
|
||||||
@click.away="open = false"
|
|
||||||
class="absolute right-0 w-48 py-1 mt-2 bg-white rounded-md shadow-lg dark:bg-gray-800"
|
|
||||||
>
|
>
|
||||||
<a
|
{{ user.username.0|upper }}
|
||||||
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>
|
||||||
|
{% endif %}
|
||||||
|
<span>{{ user.username }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Dropdown Menu -->
|
||||||
|
<div
|
||||||
|
x-show="open"
|
||||||
|
@click.away="open = false"
|
||||||
|
class="absolute right-0 w-48 py-1 mt-2 bg-white rounded-md shadow-lg 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>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!-- Login/Register (Desktop) -->
|
<!-- Login/Register (Desktop) -->
|
||||||
<div class="hidden space-x-3 lg:flex">
|
<div class="hidden space-x-3 lg:flex">
|
||||||
<a href="{% url 'account_login' %}" class="btn-secondary">
|
<a href="{% url 'account_login' %}" class="btn-secondary">
|
||||||
<i class="mr-2 fas fa-sign-in-alt"></i>
|
<i class="mr-2 fas fa-sign-in-alt"></i>
|
||||||
Login
|
Login
|
||||||
</a>
|
</a>
|
||||||
<a href="{% url 'account_signup' %}" class="btn-primary">
|
<a href="{% url 'account_signup' %}" class="btn-primary">
|
||||||
<i class="mr-2 fas fa-user-plus"></i>
|
<i class="mr-2 fas fa-user-plus"></i>
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Mobile Menu Button -->
|
<!-- Mobile Menu Button -->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 mb-12">
|
<div class="mb-12 bg-white border border-gray-200 rounded-lg shadow-lg dark:bg-gray-800 dark:border-gray-700">
|
||||||
<div class="py-12 px-4 text-center">
|
<div class="px-4 py-12 text-center">
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold mb-6 text-gray-900 dark:text-white">
|
<h1 class="mb-6 text-4xl font-bold text-gray-900 md:text-5xl lg:text-6xl dark:text-white">
|
||||||
Welcome to ThrillWiki
|
Welcome to ThrillWiki
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-xl md:text-2xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto">
|
<p class="max-w-3xl mx-auto mb-8 text-xl text-gray-600 md:text-2xl dark:text-gray-300">
|
||||||
Your ultimate guide to theme parks and attractions worldwide
|
Your ultimate guide to theme parks and attractions worldwide
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap justify-center gap-4">
|
<div class="flex flex-wrap justify-center gap-4">
|
||||||
<a href="{% url 'parks:park_list' %}"
|
<a href="{% url 'parks:park_list' %}"
|
||||||
class="btn-primary text-lg px-8 py-3">
|
class="px-8 py-3 text-lg btn-primary">
|
||||||
Explore Parks
|
Explore Parks
|
||||||
</a>
|
</a>
|
||||||
<a href="{% url 'rides:ride_list' %}"
|
<a href="{% url 'rides:ride_list' %}"
|
||||||
class="btn-secondary text-lg px-8 py-3">
|
class="px-8 py-3 text-lg btn-secondary">
|
||||||
View Rides
|
View Rides
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stats Section -->
|
<!-- Stats Section -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
|
<div class="grid grid-cols-1 gap-6 mb-12 md:grid-cols-3">
|
||||||
<!-- Total Parks -->
|
<!-- Total Parks -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 text-center transform transition-transform hover:-translate-y-1">
|
<div class="p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1">
|
||||||
<div class="text-4xl font-bold text-blue-600 dark:text-blue-400 mb-2">
|
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
|
||||||
{{ stats.total_parks }}
|
{{ stats.total_parks }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xl text-gray-600 dark:text-gray-300">
|
<div class="text-xl text-gray-600 dark:text-gray-300">
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total Attractions -->
|
<!-- Total Attractions -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 text-center transform transition-transform hover:-translate-y-1">
|
<div class="p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1">
|
||||||
<div class="text-4xl font-bold text-blue-600 dark:text-blue-400 mb-2">
|
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
|
||||||
{{ stats.total_rides }}
|
{{ stats.total_rides }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xl text-gray-600 dark:text-gray-300">
|
<div class="text-xl text-gray-600 dark:text-gray-300">
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total Roller Coasters -->
|
<!-- Total Roller Coasters -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 text-center transform transition-transform hover:-translate-y-1">
|
<div class="p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1">
|
||||||
<div class="text-4xl font-bold text-blue-600 dark:text-blue-400 mb-2">
|
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
|
||||||
{{ stats.total_roller_coasters }}
|
{{ stats.total_roller_coasters }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xl text-gray-600 dark:text-gray-300">
|
<div class="text-xl text-gray-600 dark:text-gray-300">
|
||||||
@@ -60,15 +60,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Featured Content -->
|
<!-- Featured Content -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||||
<!-- Popular Parks -->
|
<!-- Popular Parks -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
<div class="p-6 bg-white rounded-lg shadow-lg dark:bg-gray-800">
|
||||||
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">
|
<h2 class="mb-6 text-2xl font-bold text-gray-900 dark:text-white">
|
||||||
Popular Parks
|
Popular Parks
|
||||||
</h2>
|
</h2>
|
||||||
{% for park in popular_parks %}
|
{% for park in popular_parks %}
|
||||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
<a href="{% url 'parks:park_detail' park.slug %}"
|
||||||
class="block mb-4 p-4 rounded-lg transition-all hover:bg-gray-50 dark:hover:bg-gray-700 hover:translate-x-2">
|
class="block p-4 mb-4 transition-all rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 hover:translate-x-2">
|
||||||
<div class="text-lg font-semibold text-blue-600 dark:text-blue-400">
|
<div class="text-lg font-semibold text-blue-600 dark:text-blue-400">
|
||||||
{{ park.name }}
|
{{ park.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
{{ park.location }}
|
{{ park.location }}
|
||||||
</div>
|
</div>
|
||||||
{% if park.average_rating %}
|
{% if park.average_rating %}
|
||||||
<div class="mt-1 flex items-center text-yellow-500">
|
<div class="flex items-center mt-1 text-yellow-500">
|
||||||
<span class="mr-1">★</span>
|
<span class="mr-1">★</span>
|
||||||
<span>{{ park.average_rating|floatformat:1 }}/10</span>
|
<span>{{ park.average_rating|floatformat:1 }}/10</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,13 +88,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Popular Rides -->
|
<!-- Popular Rides -->
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
<div class="p-6 bg-white rounded-lg shadow-lg dark:bg-gray-800">
|
||||||
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">
|
<h2 class="mb-6 text-2xl font-bold text-gray-900 dark:text-white">
|
||||||
Popular Rides
|
Popular Rides
|
||||||
</h2>
|
</h2>
|
||||||
{% for ride in popular_rides %}
|
{% for ride in popular_rides %}
|
||||||
<a href="{% url 'rides:ride_detail' ride.park.slug ride.slug %}"
|
<a href="{% url 'rides:ride_detail' ride.park.slug ride.slug %}"
|
||||||
class="block mb-4 p-4 rounded-lg transition-all hover:bg-gray-50 dark:hover:bg-gray-700 hover:translate-x-2">
|
class="block p-4 mb-4 transition-all rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 hover:translate-x-2">
|
||||||
<div class="text-lg font-semibold text-blue-600 dark:text-blue-400">
|
<div class="text-lg font-semibold text-blue-600 dark:text-blue-400">
|
||||||
{{ ride.name }}
|
{{ ride.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
at {{ ride.park.name }}
|
at {{ ride.park.name }}
|
||||||
</div>
|
</div>
|
||||||
{% if ride.average_rating %}
|
{% if ride.average_rating %}
|
||||||
<div class="mt-1 flex items-center text-yellow-500">
|
<div class="flex items-center mt-1 text-yellow-500">
|
||||||
<span class="mr-1">★</span>
|
<span class="mr-1">★</span>
|
||||||
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
|
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
{% for area in areas %}
|
{% for area in areas %}
|
||||||
<div class="p-4 transition-transform transform rounded-lg bg-gray-50 dark:bg-gray-700/50 hover:-translate-y-1">
|
<div class="p-4 transition-transform transform rounded-lg bg-gray-50 dark:bg-gray-700/50 hover:-translate-y-1">
|
||||||
<h3 class="mb-2 text-lg font-semibold text-gray-900 dark:text-white">
|
<h3 class="mb-2 text-lg font-semibold text-gray-900 dark:text-white">
|
||||||
<a href="{% url 'parks:area_detail' park.slug area.slug %}">{{ area.name }}</a>
|
{{ area.name }}
|
||||||
</h3>
|
</h3>
|
||||||
{% if area.description %}
|
{% if area.description %}
|
||||||
<p class="mb-2 text-gray-600 dark:text-gray-300">
|
<p class="mb-2 text-gray-600 dark:text-gray-300">
|
||||||
@@ -281,7 +281,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p class="text-gray-500 dark:text-gray-400">No history available.</p>
|
<p class="text-gray-500">No history available.</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<div class="flex flex-col items-start justify-between gap-4 mb-6 md:flex-row md:items-center">
|
<div class="flex flex-col items-start justify-between gap-4 mb-6 md:flex-row md:items-center">
|
||||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Parks</h1>
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Parks</h1>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<a href="{% url 'parks:park_create' %}" class="btn-primary">
|
<a href="{% url 'park_create' %}" class="btn-primary">
|
||||||
<i class="mr-2 fas fa-plus"></i>Add Park
|
<i class="mr-2 fas fa-plus"></i>Add Park
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<h2 class="mb-2 text-xl font-bold">
|
<h2 class="mb-2 text-xl font-bold">
|
||||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
<a href="{% url 'park_detail' park.slug %}"
|
||||||
class="text-gray-900 hover:text-blue-600 dark:text-white dark:hover:text-blue-400">
|
class="text-gray-900 hover:text-blue-600 dark:text-white dark:hover:text-blue-400">
|
||||||
{{ park.name }}
|
{{ park.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -275,6 +275,22 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Photos -->
|
||||||
|
{% if ride.photos.exists %}
|
||||||
|
<div class="p-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||||
|
<h2 class="mb-4 text-xl font-semibold text-gray-900 dark:text-white">Photos</h2>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
{% for photo in ride.photos.all %}
|
||||||
|
<div class="aspect-w-16 aspect-h-9">
|
||||||
|
<img src="{{ photo.image.url }}"
|
||||||
|
alt="{{ photo.caption|default:ride.name }}"
|
||||||
|
class="object-cover rounded-lg">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<span class="text-blue-800 bg-blue-100 status-badge dark:bg-blue-700 dark:text-blue-50">
|
<span class="text-blue-800 bg-blue-100 status-badge dark:bg-blue-400/30 dark:text-blue-200 dark:ring-1 dark:ring-blue-400/30">
|
||||||
{{ ride.get_category_display }}
|
{{ ride.get_category_display }}
|
||||||
</span>
|
</span>
|
||||||
<span class="status-badge {% if ride.status == 'OPERATING' %}status-operating
|
<span class="status-badge {% if ride.status == 'OPERATING' %}status-operating
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
{% block title %}Search Results - ThrillWiki{% endblock %}
|
{% block title %}Search Results - ThrillWiki{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container px-4 py-8 mx-auto">
|
||||||
<!-- Search Header -->
|
<!-- Search Header -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h1 class="text-3xl font-bold mb-2">Search Results</h1>
|
<h1 class="mb-2 text-3xl font-bold">Search Results</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">
|
<p class="text-gray-600 dark:text-gray-400">
|
||||||
{% if request.GET.q %}
|
{% if request.GET.q %}
|
||||||
Results for "{{ request.GET.q }}"
|
Results for "{{ request.GET.q }}"
|
||||||
@@ -19,36 +19,36 @@
|
|||||||
|
|
||||||
{% if request.GET.q %}
|
{% if request.GET.q %}
|
||||||
<!-- Parks Results -->
|
<!-- Parks Results -->
|
||||||
<div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6 mb-6">
|
<div class="p-6 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||||
<h2 class="text-xl font-semibold mb-4">Theme Parks</h2>
|
<h2 class="mb-4 text-xl font-semibold">Theme Parks</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{% for park in parks %}
|
{% for park in parks %}
|
||||||
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg overflow-hidden">
|
<div class="overflow-hidden rounded-lg bg-gray-50 dark:bg-gray-700">
|
||||||
{% if park.photos.exists %}
|
{% if park.photos.exists %}
|
||||||
<img src="{{ park.photos.first.image.url }}"
|
<img src="{{ park.photos.first.image.url }}"
|
||||||
alt="{{ park.name }}"
|
alt="{{ park.name }}"
|
||||||
class="w-full h-48 object-cover">
|
class="object-cover w-full h-48">
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="w-full h-48 bg-gray-200 dark:bg-gray-600 flex items-center justify-center">
|
<div class="flex items-center justify-center w-full h-48 bg-gray-200 dark:bg-gray-600">
|
||||||
<span class="text-gray-400">No image available</span>
|
<span class="text-gray-400">No image available</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<h3 class="text-lg font-semibold mb-2">
|
<h3 class="mb-2 text-lg font-semibold">
|
||||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
<a href="{% url 'parks:park_detail' park.slug %}"
|
||||||
class="text-blue-600 dark:text-blue-400 hover:underline">
|
class="text-blue-600 hover:underline dark:text-blue-400">
|
||||||
{{ park.name }}
|
{{ park.name }}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-gray-600 dark:text-gray-400 mb-2">{{ park.location }}</p>
|
<p class="mb-2 text-gray-600 dark:text-gray-400">{{ park.location }}</p>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-sm text-gray-500 dark:text-gray-400">
|
<span class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
{{ park.rides.count }} attractions
|
{{ park.rides.count }} attractions
|
||||||
</span>
|
</span>
|
||||||
{% if park.average_rating %}
|
{% if park.average_rating %}
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-yellow-400 mr-1">★</span>
|
<span class="mr-1 text-yellow-400">★</span>
|
||||||
<span>{{ park.average_rating|floatformat:1 }}/10</span>
|
<span>{{ park.average_rating|floatformat:1 }}/10</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<div class="col-span-full text-center py-4">
|
<div class="py-4 text-center col-span-full">
|
||||||
<p class="text-gray-500 dark:text-gray-400">No parks found matching your search.</p>
|
<p class="text-gray-500 dark:text-gray-400">No parks found matching your search.</p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -64,39 +64,39 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Rides Results -->
|
<!-- Rides Results -->
|
||||||
<div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6 mb-6">
|
<div class="p-6 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||||
<h2 class="text-xl font-semibold mb-4">Rides & Attractions</h2>
|
<h2 class="mb-4 text-xl font-semibold">Rides & Attractions</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{% for ride in rides %}
|
{% for ride in rides %}
|
||||||
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg overflow-hidden">
|
<div class="overflow-hidden rounded-lg bg-gray-50 dark:bg-gray-700">
|
||||||
{% if ride.photos.exists %}
|
{% if ride.photos.exists %}
|
||||||
<img src="{{ ride.photos.first.image.url }}"
|
<img src="{{ ride.photos.first.image.url }}"
|
||||||
alt="{{ ride.name }}"
|
alt="{{ ride.name }}"
|
||||||
class="w-full h-48 object-cover">
|
class="object-cover w-full h-48">
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="w-full h-48 bg-gray-200 dark:bg-gray-600 flex items-center justify-center">
|
<div class="flex items-center justify-center w-full h-48 bg-gray-200 dark:bg-gray-600">
|
||||||
<span class="text-gray-400">No image available</span>
|
<span class="text-gray-400">No image available</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<h3 class="text-lg font-semibold mb-2">
|
<h3 class="mb-2 text-lg font-semibold">
|
||||||
<a href="{% url 'rides:ride_detail' ride.park.slug ride.slug %}"
|
<a href="{% url 'rides:ride_detail' ride.park.slug ride.slug %}"
|
||||||
class="text-blue-600 dark:text-blue-400 hover:underline">
|
class="text-blue-600 hover:underline dark:text-blue-400">
|
||||||
{{ ride.name }}
|
{{ ride.name }}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-gray-600 dark:text-gray-400 mb-2">
|
<p class="mb-2 text-gray-600 dark:text-gray-400">
|
||||||
at <a href="{% url 'parks:park_detail' ride.park.slug %}"
|
at <a href="{% url 'parks:park_detail' ride.park.slug %}"
|
||||||
class="hover:underline">{{ ride.park.name }}</a>
|
class="hover:underline">{{ ride.park.name }}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap gap-2 mb-2">
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
<span class="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">
|
<span class="px-2 py-1 text-xs text-blue-800 bg-blue-100 rounded-full">
|
||||||
{{ ride.get_category_display }}
|
{{ ride.get_category_display }}
|
||||||
</span>
|
</span>
|
||||||
{% if ride.average_rating %}
|
{% if ride.average_rating %}
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-yellow-400 mr-1">★</span>
|
<span class="mr-1 text-yellow-400">★</span>
|
||||||
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
|
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<div class="col-span-full text-center py-4">
|
<div class="py-4 text-center col-span-full">
|
||||||
<p class="text-gray-500 dark:text-gray-400">No rides found matching your search.</p>
|
<p class="text-gray-500 dark:text-gray-400">No rides found matching your search.</p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -112,26 +112,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Companies Results -->
|
<!-- Companies Results -->
|
||||||
<div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6">
|
<div class="p-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||||
<h2 class="text-xl font-semibold mb-4">Companies</h2>
|
<h2 class="mb-4 text-xl font-semibold">Companies</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{% for company in companies %}
|
{% for company in companies %}
|
||||||
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
|
<div class="p-4 rounded-lg bg-gray-50 dark:bg-gray-700">
|
||||||
<h3 class="text-lg font-semibold mb-2">
|
<h3 class="mb-2 text-lg font-semibold">
|
||||||
<a href="{% url 'companies:company_detail' company.slug %}"
|
<a href="{% url 'companies:company_detail' company.slug %}"
|
||||||
class="text-blue-600 dark:text-blue-400 hover:underline">
|
class="text-blue-600 hover:underline dark:text-blue-400">
|
||||||
{{ company.name }}
|
{{ company.name }}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
{% if company.headquarters %}
|
{% if company.headquarters %}
|
||||||
<p class="text-gray-600 dark:text-gray-400 mb-2">{{ company.headquarters }}</p>
|
<p class="mb-2 text-gray-600 dark:text-gray-400">{{ company.headquarters }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
{{ company.parks.count }} parks owned
|
{{ company.parks.count }} parks owned
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<div class="col-span-full text-center py-4">
|
<div class="py-4 text-center col-span-full">
|
||||||
<p class="text-gray-500 dark:text-gray-400">No companies found matching your search.</p>
|
<p class="text-gray-500 dark:text-gray-400">No companies found matching your search.</p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Binary file not shown.
@@ -11,8 +11,12 @@ urlpatterns = [
|
|||||||
|
|
||||||
# Main app URLs
|
# Main app URLs
|
||||||
path('', HomeView.as_view(), name='home'),
|
path('', HomeView.as_view(), name='home'),
|
||||||
path('parks/', include('parks.urls')),
|
|
||||||
path('rides/', include('rides.urls')),
|
# Parks URLs
|
||||||
|
path('parks/', include('parks.urls', namespace='parks')),
|
||||||
|
path('rides/', include('rides.urls', namespace='rides')),
|
||||||
|
|
||||||
|
# Other URLs
|
||||||
path('reviews/', include('reviews.urls')),
|
path('reviews/', include('reviews.urls')),
|
||||||
path('companies/', include('companies.urls')),
|
path('companies/', include('companies.urls')),
|
||||||
path('search/', SearchView.as_view(), name='search'),
|
path('search/', SearchView.as_view(), name='search'),
|
||||||
@@ -28,8 +32,7 @@ urlpatterns = [
|
|||||||
path('accounts/email-required/', accounts_views.email_required, name='email_required'),
|
path('accounts/email-required/', accounts_views.email_required, name='email_required'),
|
||||||
|
|
||||||
# User profile URLs
|
# User profile URLs
|
||||||
path('users/<str:username>/', accounts_views.ProfileView.as_view(), name='user_profile'),
|
path('user/<str:username>/', accounts_views.ProfileView.as_view(), name='user_profile'),
|
||||||
path('user/<str:username>/', accounts_views.ProfileView.as_view(), name='single_user_profile'),
|
|
||||||
path('profile/<str:username>/', accounts_views.ProfileView.as_view(), name='profile'),
|
path('profile/<str:username>/', accounts_views.ProfileView.as_view(), name='profile'),
|
||||||
path('settings/', accounts_views.SettingsView.as_view(), name='settings'),
|
path('settings/', accounts_views.SettingsView.as_view(), name='settings'),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user