Compare commits

...

33 Commits

Author SHA1 Message Date
pacnpal
bb9d7607fb Merge branch 'main' into dependabot/pip/whitenoise-6.9.0 2025-02-10 17:19:54 -05:00
pacnpal
433f860fd2 Merge pull request #28 from pacnpal/dependabot/pip/django-htmx-1.22.0
[DEPENDABOT] Update: Bump django-htmx from 1.21.0 to 1.22.0
2025-02-10 17:19:15 -05:00
pacnpal
9c448a52ce Merge pull request #29 from pacnpal/dependabot/pip/django-allauth-65.4.1
[DEPENDABOT] Update: Bump django-allauth from 65.4.0 to 65.4.1
2025-02-10 17:19:04 -05:00
pacnpal
e2089ac76d Merge pull request #26 from pacnpal/dependabot/github_actions/actions/setup-python-5
[DEPENDABOT] Update Actions: Bump actions/setup-python from 4 to 5
2025-02-10 17:18:53 -05:00
pacnpal
19a5640615 Merge pull request #30 from pacnpal/dependabot/pip/django-tailwind-cli-4.0.1
[DEPENDABOT] Update: Bump django-tailwind-cli from 2.21.1 to 4.0.1
2025-02-10 17:18:27 -05:00
pacnpal
2b81681e46 Merge pull request #31 from pacnpal/dependabot/pip/pytest-django-4.10.0
[DEPENDABOT] Update: Bump pytest-django from 4.9.0 to 4.10.0
2025-02-10 17:16:37 -05:00
pacnpal
6bc412a325 Merge pull request #32 from pacnpal/dependabot/pip/django-pghistory-3.5.2
[DEPENDABOT] Update: Bump django-pghistory from 2.9.0 to 3.5.2
2025-02-10 17:16:13 -05:00
pacnpal
c2a7a98840 Merge pull request #37 from pacnpal/pacnpal-patch-3
Update README.md
2025-02-10 17:11:34 -05:00
pacnpal
e35470f193 Update README.md 2025-02-10 17:10:13 -05:00
pacnpal
88f7643c4e Merge pull request #35 from pacnpal/pacnpal-patch-2
Create README.md
2025-02-10 16:31:52 -05:00
pacnpal
c011f2edec Create README.md 2025-02-10 16:20:31 -05:00
pacnpal
b1eac7a356 Merge pull request #33 from pacnpal/dependabot/pip/django-cors-headers-4.7.0
[DEPENDABOT] Update: Bump django-cors-headers from 4.6.0 to 4.7.0
2025-02-10 16:12:25 -05:00
pacnpal
76ffdd7a20 Update review.yml 2025-02-10 15:03:59 -05:00
pacnpal
2fb975caba Update review.yml 2025-02-10 15:02:47 -05:00
pacnpal
c78299bc69 Update review.yml 2025-02-10 14:59:00 -05:00
pacnpal
dcb49b5611 Add ParkContextRequired mixin to enforce park context in ride views; update URLs and templates for global ride listing 2025-02-10 14:48:29 -05:00
pacnpal
b253be1aed Update review.yml 2025-02-10 13:36:58 -05:00
pacnpal
057a9ed70a Update review.yml 2025-02-10 13:36:34 -05:00
pacnpal
f5cdace56b Update review.yml 2025-02-10 13:36:19 -05:00
pacnpal
ed17b46ab4 Update review.yml 2025-02-10 13:36:06 -05:00
pacnpal
e140f66c39 Update review.yml 2025-02-10 13:35:29 -05:00
pacnpal
55c4456010 Update review.yml 2025-02-10 13:35:03 -05:00
pacnpal
a8b2661c2c Update review.yml 2025-02-10 13:32:21 -05:00
pacnpal
0713aea03f Update review.yml 2025-02-10 13:31:16 -05:00
pacnpal
0430d6941d Update review.yml 2025-02-10 13:28:58 -05:00
pacnpal
db29e822bb Add links to ride names in ride list template for improved navigation 2025-02-10 12:45:45 -05:00
dependabot[bot]
f4b806da68 [DEPENDABOT] Update: Bump django-cors-headers from 4.6.0 to 4.7.0
Bumps [django-cors-headers](https://github.com/adamchainz/django-cors-headers) from 4.6.0 to 4.7.0.
- [Changelog](https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/django-cors-headers/compare/4.6.0...4.7.0)

---
updated-dependencies:
- dependency-name: django-cors-headers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:51 +00:00
dependabot[bot]
e2f99f42eb [DEPENDABOT] Update: Bump django-pghistory from 2.9.0 to 3.5.2
Bumps [django-pghistory](https://github.com/AmbitionEng/django-pghistory) from 2.9.0 to 3.5.2.
- [Release notes](https://github.com/AmbitionEng/django-pghistory/releases)
- [Changelog](https://github.com/AmbitionEng/django-pghistory/blob/main/CHANGELOG.md)
- [Commits](https://github.com/AmbitionEng/django-pghistory/compare/2.9.0...3.5.2)

---
updated-dependencies:
- dependency-name: django-pghistory
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:38 +00:00
dependabot[bot]
fcd1f725eb [DEPENDABOT] Update: Bump pytest-django from 4.9.0 to 4.10.0
Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.9.0 to 4.10.0.
- [Release notes](https://github.com/pytest-dev/pytest-django/releases)
- [Changelog](https://github.com/pytest-dev/pytest-django/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.9.0...v4.10.0)

---
updated-dependencies:
- dependency-name: pytest-django
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:33 +00:00
dependabot[bot]
c284070d74 [DEPENDABOT] Update: Bump django-tailwind-cli from 2.21.1 to 4.0.1
Bumps [django-tailwind-cli](https://github.com/django-commons/django-tailwind-cli) from 2.21.1 to 4.0.1.
- [Release notes](https://github.com/django-commons/django-tailwind-cli/releases)
- [Changelog](https://github.com/django-commons/django-tailwind-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/django-commons/django-tailwind-cli/compare/2.21.1...4.0.1)

---
updated-dependencies:
- dependency-name: django-tailwind-cli
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:31 +00:00
dependabot[bot]
77e7cbc8f8 [DEPENDABOT] Update: Bump django-allauth from 65.4.0 to 65.4.1
Bumps [django-allauth](https://github.com/sponsors/pennersr) from 65.4.0 to 65.4.1.
- [Commits](https://github.com/sponsors/pennersr/commits)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:27 +00:00
dependabot[bot]
d1fded7191 [DEPENDABOT] Update: Bump django-htmx from 1.21.0 to 1.22.0
Bumps [django-htmx](https://github.com/adamchainz/django-htmx) from 1.21.0 to 1.22.0.
- [Changelog](https://github.com/adamchainz/django-htmx/blob/main/docs/changelog.rst)
- [Commits](https://github.com/adamchainz/django-htmx/compare/1.21.0...1.22.0)

---
updated-dependencies:
- dependency-name: django-htmx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:22 +00:00
dependabot[bot]
a17fa9a58a [DEPENDABOT] Update Actions: Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:41:58 +00:00
8 changed files with 49 additions and 58 deletions

View File

@@ -27,7 +27,7 @@ jobs:
run: brew install gdal run: brew install gdal
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}

View File

@@ -27,7 +27,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Run Claude Review - name: Run Claude Review
uses: pacnpal/claude-code-review@v1.0.7 uses: pacnpal/claude-code-review@main
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
anthropic-key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic-key: ${{ secrets.ANTHROPIC_API_KEY }}

1
README.md Normal file
View File

@@ -0,0 +1 @@
ThrillWiki.com

View File

@@ -1,10 +1,10 @@
# Django and REST framework # Django and REST framework
Django==5.1.6 Django==5.1.6
djangorestframework==3.15.2 djangorestframework==3.15.2
django-cors-headers==4.6.0 django-cors-headers==4.7.0
# Authentication # Authentication
django-allauth==65.4.0 django-allauth==65.4.1
django-oauth-toolkit==3.0.1 django-oauth-toolkit==3.0.1
dj-rest-auth==7.0.1 # Added for REST authentication endpoints dj-rest-auth==7.0.1 # Added for REST authentication endpoints
pyjwt==2.10.1 pyjwt==2.10.1
@@ -12,7 +12,7 @@ pyjwt==2.10.1
# Database # Database
psycopg2-binary==2.9.10 psycopg2-binary==2.9.10
dj-database-url==2.3.0 dj-database-url==2.3.0
django-pghistory==2.9.0 # For model history tracking django-pghistory==3.5.2 # For model history tracking
# Email # Email
requests==2.32.3 # For ForwardEmail.net API requests==2.32.3 # For ForwardEmail.net API
@@ -26,7 +26,7 @@ Pillow==11.1.0 # For image handling
django-cleanup==9.0.0 # Automatically delete files django-cleanup==9.0.0 # Automatically delete files
piexif==1.1.3 # For image EXIF metadata handling piexif==1.1.3 # For image EXIF metadata handling
django-filter==24.3 django-filter==24.3
django-htmx==1.21.0 django-htmx==1.22.0
whitenoise==6.9.0 # Static file serving whitenoise==6.9.0 # Static file serving
pycountry==24.6.1 pycountry==24.6.1
@@ -34,7 +34,7 @@ pycountry==24.6.1
black==25.1.0 black==25.1.0
flake8==7.1.1 flake8==7.1.1
pytest==8.3.4 pytest==8.3.4
pytest-django==4.9.0 pytest-django==4.10.0
# WebSocket Support # WebSocket Support
channels==4.2.0 channels==4.2.0
@@ -42,4 +42,4 @@ channels-redis==4.2.1
daphne==4.1.2 daphne==4.1.2
# React and Material UI will be handled via npm in the frontend directory # React and Material UI will be handled via npm in the frontend directory
django-tailwind-cli==2.21.1 django-tailwind-cli==4.0.1

View File

@@ -7,7 +7,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib import messages from django.contrib import messages
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse, Http404
from django.db.models import Count from django.db.models import Count
from .models import ( from .models import (
Ride, RollerCoasterStats, RideModel, RideEvent, Ride, RollerCoasterStats, RideModel, RideEvent,
@@ -21,6 +21,13 @@ from moderation.models import EditSubmission
from companies.models import Manufacturer from companies.models import Manufacturer
from designers.models import Designer from designers.models import Designer
class ParkContextRequired:
"""Mixin to require park context for views"""
def dispatch(self, request, *args, **kwargs):
if 'park_slug' not in self.kwargs:
raise Http404("Park context is required")
return super().dispatch(request, *args, **kwargs)
def show_coaster_fields(request: HttpRequest) -> HttpResponse: def show_coaster_fields(request: HttpRequest) -> HttpResponse:
"""Show roller coaster specific fields based on category selection""" """Show roller coaster specific fields based on category selection"""
category = request.GET.get('category') category = request.GET.get('category')
@@ -61,7 +68,7 @@ class RideDetailView(HistoryMixin, DetailView):
return context return context
class RideCreateView(LoginRequiredMixin, CreateView): class RideCreateView(LoginRequiredMixin, ParkContextRequired, CreateView):
"""View for creating a new ride""" """View for creating a new ride"""
model = Ride model = Ride
form_class = RideForm form_class = RideForm
@@ -69,17 +76,14 @@ class RideCreateView(LoginRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
"""Get URL to redirect to after successful creation""" """Get URL to redirect to after successful creation"""
if hasattr(self, 'park'):
return reverse('parks:rides:ride_detail', kwargs={ return reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug, 'park_slug': self.park.slug,
'ride_slug': self.object.slug 'ride_slug': self.object.slug
}) })
return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug})
def get_form_kwargs(self): def get_form_kwargs(self):
"""Pass park to the form""" """Pass park to the form"""
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
if 'park_slug' in self.kwargs:
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
kwargs['park'] = self.park kwargs['park'] = self.park
return kwargs return kwargs
@@ -87,7 +91,6 @@ class RideCreateView(LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Add park and park_slug to context""" """Add park and park_slug to context"""
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
if hasattr(self, 'park'):
context['park'] = self.park context['park'] = self.park
context['park_slug'] = self.park.slug context['park_slug'] = self.park.slug
context['is_edit'] = False context['is_edit'] = False
@@ -131,7 +134,7 @@ class RideCreateView(LoginRequiredMixin, CreateView):
return super().form_valid(form) return super().form_valid(form)
class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixin, UpdateView):
"""View for updating an existing ride""" """View for updating an existing ride"""
model = Ride model = Ride
form_class = RideForm form_class = RideForm
@@ -140,37 +143,25 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView):
def get_success_url(self): def get_success_url(self):
"""Get URL to redirect to after successful update""" """Get URL to redirect to after successful update"""
if hasattr(self, 'park'):
return reverse('parks:rides:ride_detail', kwargs={ return reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug, 'park_slug': self.park.slug,
'ride_slug': self.object.slug 'ride_slug': self.object.slug
}) })
return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug})
def get_queryset(self): def get_queryset(self):
"""Get ride for the specific park if park_slug is provided""" """Get ride for the specific park"""
queryset = Ride.objects.all() return Ride.objects.filter(park__slug=self.kwargs['park_slug'])
if 'park_slug' in self.kwargs:
queryset = queryset.filter(park__slug=self.kwargs['park_slug'])
return queryset
def get_form_kwargs(self): def get_form_kwargs(self):
"""Pass park to the form""" """Pass park to the form"""
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
# For park-specific URLs, use the park from the URL
if 'park_slug' in self.kwargs:
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
kwargs['park'] = self.park kwargs['park'] = self.park
# For global URLs, use the ride's park
else:
self.park = self.get_object().park
kwargs['park'] = self.park
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Add park and park_slug to context""" """Add park and park_slug to context"""
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
if hasattr(self, 'park'):
context['park'] = self.park context['park'] = self.park
context['park_slug'] = self.park.slug context['park_slug'] = self.park.slug
context['is_edit'] = True context['is_edit'] = True

View File

@@ -99,7 +99,7 @@
<i class="fas fa-map-marker-alt"></i> <i class="fas fa-map-marker-alt"></i>
<span>Parks</span> <span>Parks</span>
</a> </a>
<a href="{% url 'rides:ride_list' %}" class="nav-link"> <a href="{% url 'rides:global_ride_list' %}" class="nav-link">
<i class="fas fa-rocket"></i> <i class="fas fa-rocket"></i>
<span>Rides</span> <span>Rides</span>
</a> </a>

View File

@@ -18,7 +18,7 @@
class="px-8 py-3 text-lg btn-primary"> 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:global_ride_list' %}"
class="px-8 py-3 text-lg btn-secondary"> class="px-8 py-3 text-lg btn-secondary">
View Rides View Rides
</a> </a>
@@ -40,7 +40,7 @@
</a> </a>
<!-- Total Attractions --> <!-- Total Attractions -->
<a href="{% url 'rides:ride_list' %}" <a href="{% url 'rides:global_ride_list' %}"
class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl"> class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl">
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400"> <div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
{{ stats.ride_count }} {{ stats.ride_count }}
@@ -51,7 +51,7 @@
</a> </a>
<!-- Total Roller Coasters --> <!-- Total Roller Coasters -->
<a href="{% url 'rides:roller_coasters' %}" <a href="{% url 'rides:global_roller_coasters' %}"
class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl"> class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl">
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400"> <div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
{{ stats.coaster_count }} {{ stats.coaster_count }}
@@ -108,7 +108,7 @@
</h2> </h2>
<div class="space-y-4"> <div class="space-y-4">
{% for ride in popular_rides %} {% for ride in popular_rides %}
<a href="{% url 'rides:ride_detail' ride.slug %}" <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" class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if ride.photos.first %} {% 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;" 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;"
@@ -147,7 +147,7 @@
{% for item in highest_rated %} {% for item in highest_rated %}
{% if item.park %} {% if item.park %}
<!-- This is a ride --> <!-- This is a ride -->
<a href="{% url 'rides:ride_detail' item.slug %}" <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" class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if item.photos.first %} {% 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;" 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;"

View File

@@ -32,12 +32,7 @@
Add Ride Add Ride
</a> </a>
{% else %} {% else %}
<a href="{% url 'rides:ride_create' %}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600"> <!-- No add ride button in global view - rides must be added from park pages -->
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
</svg>
Add Ride
</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
@@ -45,7 +40,7 @@
<!-- Filters --> <!-- Filters -->
<div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800"> <div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
<form class="grid grid-cols-1 gap-4 md:grid-cols-3" <form class="grid grid-cols-1 gap-4 md:grid-cols-3"
hx-get="{% if park %}{% url 'parks:rides:ride_list' park.slug %}{% else %}{% url 'rides:ride_list' %}{% endif %}" hx-get="{% if park %}{% url 'parks:rides:ride_list' park.slug %}{% else %}{% url 'rides:global_ride_list' %}{% endif %}"
hx-trigger="change from:select, input from:input[type='text']" hx-trigger="change from:select, input from:input[type='text']"
hx-target="#rides-grid" hx-target="#rides-grid"
hx-push-url="true"> hx-push-url="true">
@@ -90,6 +85,9 @@
{% for ride in rides %} {% for ride in rides %}
<div class="overflow-hidden transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1"> <div class="overflow-hidden transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1">
<div class="aspect-w-16 aspect-h-9"> <div class="aspect-w-16 aspect-h-9">
<a href="{% url 'parks:rides:ride_detail' ride.park.slug ride.slug %}"
class="text-gray-900 hover:text-blue-600 dark:text-white dark:hover:text-blue-400">
{{ ride.name }}
{% 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 }}"
@@ -99,6 +97,7 @@
alt="{{ ride.name }}" alt="{{ ride.name }}"
class="object-cover w-full"> class="object-cover w-full">
{% endif %} {% endif %}
</a>
</div> </div>
<div class="p-4"> <div class="p-4">
<h2 class="mb-2 text-xl font-bold"> <h2 class="mb-2 text-xl font-bold">