Compare commits

...

11 Commits

Author SHA1 Message Date
dependabot[bot]
d14c556820 Merge f5cdace56b into b1eac7a356 2025-02-10 21:12:33 +00: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
e140f66c39 Update review.yml 2025-02-10 13:35:29 -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
6 changed files with 42 additions and 52 deletions

View File

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

View File

@@ -1,7 +1,7 @@
# Django and REST framework
Django==5.1.6
djangorestframework==3.15.2
django-cors-headers==4.6.0
django-cors-headers==4.7.0
# Authentication
django-allauth==65.4.0

View File

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

View File

@@ -99,7 +99,7 @@
<i class="fas fa-map-marker-alt"></i>
<span>Parks</span>
</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>
<span>Rides</span>
</a>

View File

@@ -18,7 +18,7 @@
class="px-8 py-3 text-lg btn-primary">
Explore Parks
</a>
<a href="{% url 'rides:ride_list' %}"
<a href="{% url 'rides:global_ride_list' %}"
class="px-8 py-3 text-lg btn-secondary">
View Rides
</a>
@@ -40,7 +40,7 @@
</a>
<!-- 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">
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
{{ stats.ride_count }}
@@ -51,7 +51,7 @@
</a>
<!-- 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">
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
{{ stats.coaster_count }}
@@ -108,7 +108,7 @@
</h2>
<div class="space-y-4">
{% 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"
{% 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;"
@@ -147,7 +147,7 @@
{% for item in highest_rated %}
{% if item.park %}
<!-- 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"
{% 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;"

View File

@@ -32,12 +32,7 @@
Add Ride
</a>
{% 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">
<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>
<!-- No add ride button in global view - rides must be added from park pages -->
{% endif %}
{% endif %}
</div>
@@ -45,7 +40,7 @@
<!-- Filters -->
<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"
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-target="#rides-grid"
hx-push-url="true">
@@ -90,6 +85,9 @@
{% 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="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 %}
<img src="{{ ride.photos.first.image.url }}"
alt="{{ ride.name }}"
@@ -99,6 +97,7 @@
alt="{{ ride.name }}"
class="object-cover w-full">
{% endif %}
</a>
</div>
<div class="p-4">
<h2 class="mb-2 text-xl font-bold">