mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 07:51:09 -05:00
Ensure park and ride slugs are valid before displaying links
Prevents 500 errors by filtering out parks and rides with null or empty slugs from trending lists and excludes them from database queries where slugs are required. Additionally, it adds conditional rendering in templates to handle parks without slugs gracefully. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 0bdea3fb-49ea-4863-b501-fa6f5af0cbf0 Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
4
.replit
4
.replit
@@ -54,10 +54,6 @@ outputType = "webview"
|
||||
localPort = 5000
|
||||
externalPort = 80
|
||||
|
||||
[[ports]]
|
||||
localPort = 37689
|
||||
externalPort = 3002
|
||||
|
||||
[[ports]]
|
||||
localPort = 41923
|
||||
externalPort = 3000
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
Traceback (most recent call last):
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/contrib/staticfiles/handlers.py", line 80, in __call__
|
||||
return self.application(environ, start_response)
|
||||
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/wsgi.py", line 124, in __call__
|
||||
response = self.get_response(request)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/base.py", line 140, in get_response
|
||||
response = self._middleware_chain(request)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 57, in inner
|
||||
response = response_for_exception(request, exc)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 141, in response_for_exception
|
||||
response = handle_uncaught_exception(
|
||||
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 182, in handle_uncaught_exception
|
||||
return debug.technical_500_response(request, *exc_info)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_extensions/management/technical_response.py", line 41, in null_technical_500_response
|
||||
raise exc_value.with_traceback(tb)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
|
||||
response = get_response(request)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/base.py", line 220, in _get_response
|
||||
response = response.render()
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/response.py", line 114, in render
|
||||
self.content = self.rendered_content
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/response.py", line 92, in rendered_content
|
||||
return template.render(context, self._request)
|
||||
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/backends/django.py", line 107, in render
|
||||
return self.template.render(context)
|
||||
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 171, in render
|
||||
return self._render(context)
|
||||
~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
|
||||
return self.nodelist.render(context)
|
||||
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/loader_tags.py", line 159, in render
|
||||
return compiled_parent._render(context)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
|
||||
return self.nodelist.render(context)
|
||||
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/loader_tags.py", line 65, in render
|
||||
result = block.nodelist.render(context)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 243, in render
|
||||
nodelist.append(node.render_annotated(context))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_cotton/templatetags/_component.py", line 86, in render
|
||||
output = template.render(context)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 173, in render
|
||||
return self._render(context)
|
||||
~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
|
||||
return self.nodelist.render(context)
|
||||
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_cotton/templatetags/_vars.py", line 52, in render
|
||||
output = self.nodelist.render(context)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 327, in render
|
||||
return nodelist.render(context)
|
||||
~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 327, in render
|
||||
return nodelist.render(context)
|
||||
~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
|
||||
return SafeString("".join([node.render_annotated(context) for node in self]))
|
||||
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
|
||||
return self.render(context)
|
||||
~~~~~~~~~~~^^^^^^^^^
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 480, in render
|
||||
url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/urls/base.py", line 98, in reverse
|
||||
resolved_url = resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
|
||||
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/urls/resolvers.py", line 831, in _reverse_with_prefix
|
||||
raise NoReverseMatch(msg)
|
||||
django.urls.exceptions.NoReverseMatch: Reverse for 'park_detail' with arguments '('',)' not found. 1 pattern(s) tried: ['parks/(?P<slug>[-a-zA-Z0-9_]+)/\\Z']
|
||||
@@ -410,9 +410,6 @@
|
||||
.top-0 {
|
||||
top: calc(var(--spacing) * 0);
|
||||
}
|
||||
.top-1 {
|
||||
top: calc(var(--spacing) * 1);
|
||||
}
|
||||
.top-1\/2 {
|
||||
top: calc(1/2 * 100%);
|
||||
}
|
||||
@@ -452,9 +449,6 @@
|
||||
.left-0 {
|
||||
left: calc(var(--spacing) * 0);
|
||||
}
|
||||
.left-1 {
|
||||
left: calc(var(--spacing) * 1);
|
||||
}
|
||||
.left-1\/2 {
|
||||
left: calc(1/2 * 100%);
|
||||
}
|
||||
@@ -533,9 +527,6 @@
|
||||
max-width: 96rem;
|
||||
}
|
||||
}
|
||||
.-mx-1 {
|
||||
margin-inline: calc(var(--spacing) * -1);
|
||||
}
|
||||
.-mx-1\.5 {
|
||||
margin-inline: calc(var(--spacing) * -1.5);
|
||||
}
|
||||
@@ -554,9 +545,6 @@
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
.-my-1 {
|
||||
margin-block: calc(var(--spacing) * -1);
|
||||
}
|
||||
.-my-1\.5 {
|
||||
margin-block: calc(var(--spacing) * -1.5);
|
||||
}
|
||||
@@ -566,9 +554,6 @@
|
||||
.my-auto {
|
||||
margin-block: auto;
|
||||
}
|
||||
.mt-0 {
|
||||
margin-top: calc(var(--spacing) * 0);
|
||||
}
|
||||
.mt-0\.5 {
|
||||
margin-top: calc(var(--spacing) * 0.5);
|
||||
}
|
||||
@@ -641,9 +626,6 @@
|
||||
.mb-12 {
|
||||
margin-bottom: calc(var(--spacing) * 12);
|
||||
}
|
||||
.-ml-0 {
|
||||
margin-left: calc(var(--spacing) * -0);
|
||||
}
|
||||
.-ml-0\.5 {
|
||||
margin-left: calc(var(--spacing) * -0.5);
|
||||
}
|
||||
@@ -815,9 +797,6 @@
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
.w-1 {
|
||||
width: calc(var(--spacing) * 1);
|
||||
}
|
||||
.w-1\/2 {
|
||||
width: calc(1/2 * 100%);
|
||||
}
|
||||
@@ -953,13 +932,6 @@
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.-translate-x-1 {
|
||||
--tw-translate-x: calc(var(--spacing) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-x-1\/2 {
|
||||
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
@@ -976,10 +948,6 @@
|
||||
--tw-translate-x: 100%;
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-y-1 {
|
||||
--tw-translate-y: calc(var(--spacing) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-y-1\/2 {
|
||||
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
@@ -1162,13 +1130,6 @@
|
||||
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-8 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.-space-x-px {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-x-reverse: 0;
|
||||
@@ -1433,9 +1394,6 @@
|
||||
.bg-\[\#5865F2\] {
|
||||
background-color: #5865F2;
|
||||
}
|
||||
.bg-accent {
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
.bg-accent\/10 {
|
||||
background-color: color-mix(in srgb, #8b5cf6 10%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1484,9 +1442,6 @@
|
||||
.bg-blue-600 {
|
||||
background-color: var(--color-blue-600);
|
||||
}
|
||||
.bg-blue-900 {
|
||||
background-color: var(--color-blue-900);
|
||||
}
|
||||
.bg-blue-900\/40 {
|
||||
background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 40%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1541,9 +1496,6 @@
|
||||
.bg-green-600 {
|
||||
background-color: var(--color-green-600);
|
||||
}
|
||||
.bg-green-900 {
|
||||
background-color: var(--color-green-900);
|
||||
}
|
||||
.bg-green-900\/40 {
|
||||
background-color: color-mix(in srgb, oklch(39.3% 0.095 152.535) 40%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1598,9 +1550,6 @@
|
||||
.bg-red-600 {
|
||||
background-color: var(--color-red-600);
|
||||
}
|
||||
.bg-red-900 {
|
||||
background-color: var(--color-red-900);
|
||||
}
|
||||
.bg-red-900\/40 {
|
||||
background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 40%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1658,9 +1607,6 @@
|
||||
.bg-yellow-600 {
|
||||
background-color: var(--color-yellow-600);
|
||||
}
|
||||
.bg-yellow-900 {
|
||||
background-color: var(--color-yellow-900);
|
||||
}
|
||||
.bg-yellow-900\/40 {
|
||||
background-color: color-mix(in srgb, oklch(42.1% 0.095 57.708) 40%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1679,10 +1625,6 @@
|
||||
--tw-gradient-position: to top in oklab;
|
||||
background-image: linear-gradient(var(--tw-gradient-stops));
|
||||
}
|
||||
.from-black {
|
||||
--tw-gradient-from: var(--color-black);
|
||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||
}
|
||||
.from-black\/60 {
|
||||
--tw-gradient-from: color-mix(in srgb, #000 60%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1743,10 +1685,6 @@
|
||||
--tw-gradient-from: var(--color-red-500);
|
||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||
}
|
||||
.from-secondary {
|
||||
--tw-gradient-from: var(--color-secondary);
|
||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||
}
|
||||
.from-secondary\/20 {
|
||||
--tw-gradient-from: color-mix(in srgb, #e11d48 20%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1782,10 +1720,6 @@
|
||||
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
||||
--tw-gradient-stops: var(--tw-gradient-via-stops);
|
||||
}
|
||||
.to-accent {
|
||||
--tw-gradient-to: var(--color-accent);
|
||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||
}
|
||||
.to-accent\/20 {
|
||||
--tw-gradient-to: color-mix(in srgb, #8b5cf6 20%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1915,9 +1849,6 @@
|
||||
.px-8 {
|
||||
padding-inline: calc(var(--spacing) * 8);
|
||||
}
|
||||
.py-0 {
|
||||
padding-block: calc(var(--spacing) * 0);
|
||||
}
|
||||
.py-0\.5 {
|
||||
padding-block: calc(var(--spacing) * 0.5);
|
||||
}
|
||||
@@ -2122,9 +2053,6 @@
|
||||
.text-emerald-800 {
|
||||
color: var(--color-emerald-800);
|
||||
}
|
||||
.text-gray-200 {
|
||||
color: var(--color-gray-200);
|
||||
}
|
||||
.text-gray-300 {
|
||||
color: var(--color-gray-300);
|
||||
}
|
||||
@@ -2254,9 +2182,6 @@
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
.underline-offset-4 {
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
@@ -56,10 +56,16 @@ Features:
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<h2 class="text-xl lg:text-2xl font-bold">
|
||||
{% if park.slug %}
|
||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
||||
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
|
||||
{{ park.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent">
|
||||
{{ park.name }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
{# Status Badge #}
|
||||
@@ -123,10 +129,16 @@ Features:
|
||||
<div class="p-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<h2 class="text-xl font-bold line-clamp-2 flex-1 mr-3">
|
||||
{% if park.slug %}
|
||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
||||
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
|
||||
{{ park.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent">
|
||||
{{ park.name }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
{# Status Badge #}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends 'base/base.html' %}
|
||||
{% load static %}
|
||||
{% load cotton %}
|
||||
|
||||
{% block title %}ThrillWiki - Theme Parks & Attractions Guide{% endblock %}
|
||||
|
||||
@@ -71,30 +72,7 @@
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{% for park in popular_parks %}
|
||||
<a href="{% url 'parks:park_detail' park.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if park.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ park.photos.first.image.url }}') center/cover no-repeat;"
|
||||
{% else %}
|
||||
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"
|
||||
{% endif %}>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ park.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-200">
|
||||
{{ park.ride_count }} rides, {{ park.coaster_count }} coasters
|
||||
</div>
|
||||
{% if park.average_rating %}
|
||||
<div class="absolute top-0 right-0 p-2 text-yellow-400">
|
||||
<span class="mr-1">★</span>
|
||||
<span>{{ park.average_rating|floatformat:1 }}/10</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-sm text-gray-400">Rating not available</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
<c-park_card park=park view_mode="grid" class="h-48" />
|
||||
{% empty %}
|
||||
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">
|
||||
<div class="mb-4 text-4xl">🎢</div>
|
||||
@@ -112,30 +90,7 @@
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{% for ride in popular_rides %}
|
||||
<a href="{% url 'parks:rides:ride_detail' ride.park.slug ride.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if ride.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ ride.photos.first.image.url }}') center/cover no-repeat;"
|
||||
{% else %}
|
||||
style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);"
|
||||
{% endif %}>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ ride.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-200">
|
||||
at {{ ride.park.name }}
|
||||
</div>
|
||||
{% if ride.average_rating %}
|
||||
<div class="flex items-center mt-1 text-yellow-400">
|
||||
<span class="mr-1">★</span>
|
||||
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-sm text-gray-400">Rating not available</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
<c-ride_card ride=ride class="h-48" url_variant="park" />
|
||||
{% empty %}
|
||||
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">
|
||||
<div class="mb-4 text-4xl">🎠</div>
|
||||
@@ -155,48 +110,10 @@
|
||||
{% for item in highest_rated %}
|
||||
{% if item.park %}
|
||||
<!-- This is a ride -->
|
||||
<a href="{% url 'parks:rides:ride_detail' item.park.slug item.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if item.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ item.photos.first.image.url }}') center/cover no-repeat;"
|
||||
{% else %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7));"
|
||||
{% endif %}>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-200">
|
||||
at {{ item.park.name }}
|
||||
</div>
|
||||
<div class="flex items-center mt-1 text-yellow-400">
|
||||
<span class="mr-1">★</span>
|
||||
<span>{{ item.average_rating|floatformat:1 }}/10</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<c-ride_card ride=item class="h-48" url_variant="park" />
|
||||
{% else %}
|
||||
<!-- This is a park -->
|
||||
<a href="{% url 'parks:park_detail' item.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if item.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ item.photos.first.image.url }}') center/cover no-repeat;"
|
||||
{% else %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7));"
|
||||
{% endif %}>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-200">
|
||||
{{ item.ride_count }} rides, {{ item.coaster_count }} coasters
|
||||
</div>
|
||||
<div class="absolute top-0 right-0 p-2 text-yellow-400">
|
||||
<span class="mr-1">★</span>
|
||||
<span>{{ item.average_rating|floatformat:1 }}/10</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<c-park_card park=item view_mode="grid" class="h-48" />
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">
|
||||
|
||||
@@ -41,6 +41,8 @@ class HomeView(TemplateView):
|
||||
trending_parks = list(
|
||||
PageView.get_trending_items(Park, hours=24, limit=10)
|
||||
)
|
||||
# Filter out any parks with invalid slugs
|
||||
trending_parks = [p for p in trending_parks if getattr(p, 'slug', None)]
|
||||
if trending_parks:
|
||||
cache.set(
|
||||
"trending_parks", trending_parks, 3600
|
||||
@@ -49,18 +51,20 @@ class HomeView(TemplateView):
|
||||
# Fallback to highest rated parks if no trending data
|
||||
trending_parks = Park.objects.exclude(
|
||||
average_rating__isnull=True
|
||||
).order_by("-average_rating")[:10]
|
||||
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
|
||||
except Exception:
|
||||
# Fallback to highest rated parks if trending calculation fails
|
||||
trending_parks = Park.objects.exclude(
|
||||
average_rating__isnull=True
|
||||
).order_by("-average_rating")[:10]
|
||||
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
|
||||
|
||||
if trending_rides is None:
|
||||
try:
|
||||
trending_rides = list(
|
||||
PageView.get_trending_items(Ride, hours=24, limit=10)
|
||||
)
|
||||
# Filter out any rides with invalid slugs
|
||||
trending_rides = [r for r in trending_rides if getattr(r, 'slug', None)]
|
||||
if trending_rides:
|
||||
cache.set(
|
||||
"trending_rides", trending_rides, 3600
|
||||
@@ -69,24 +73,24 @@ class HomeView(TemplateView):
|
||||
# Fallback to highest rated rides if no trending data
|
||||
trending_rides = Ride.objects.exclude(
|
||||
average_rating__isnull=True
|
||||
).order_by("-average_rating")[:10]
|
||||
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
|
||||
except Exception:
|
||||
# Fallback to highest rated rides if trending calculation fails
|
||||
trending_rides = Ride.objects.exclude(
|
||||
average_rating__isnull=True
|
||||
).order_by("-average_rating")[:10]
|
||||
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
|
||||
|
||||
# Get highest rated items (mix of parks and rides)
|
||||
highest_rated_parks = list(
|
||||
Park.objects.exclude(average_rating__isnull=True).order_by(
|
||||
"-average_rating"
|
||||
)[:20]
|
||||
Park.objects.exclude(average_rating__isnull=True)
|
||||
.exclude(slug__isnull=True).exclude(slug__exact='')
|
||||
.order_by("-average_rating")[:20]
|
||||
) # Get more items to randomly select from
|
||||
|
||||
highest_rated_rides = list(
|
||||
Ride.objects.exclude(average_rating__isnull=True).order_by(
|
||||
"-average_rating"
|
||||
)[:20]
|
||||
Ride.objects.exclude(average_rating__isnull=True)
|
||||
.exclude(slug__isnull=True).exclude(slug__exact='')
|
||||
.order_by("-average_rating")[:20]
|
||||
) # Get more items to randomly select from
|
||||
|
||||
# Combine and shuffle highest rated items
|
||||
|
||||
Reference in New Issue
Block a user