mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 07:51:09 -05:00
Standardize park and ride display cards using django-cotton
Replace custom park and ride card templates with django-cotton components, ensuring a consistent UI and enforcing its usage. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 0bdea3fb-49ea-4863-b501-fa6f5af0cbf0 Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
2
.replit
2
.replit
@@ -55,7 +55,7 @@ localPort = 5000
|
|||||||
externalPort = 80
|
externalPort = 80
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 32933
|
localPort = 35685
|
||||||
externalPort = 3002
|
externalPort = 3002
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load cotton %}
|
||||||
|
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<div class="p-4" data-testid="park-list-error">
|
<div class="p-4" data-testid="park-list-error">
|
||||||
@@ -11,140 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% for park in object_list|default:parks %}
|
{% for park in object_list|default:parks %}
|
||||||
{% if view_mode == 'list' %}
|
<c-park_card park=park view_mode=view_mode />
|
||||||
{# Enhanced List View Item #}
|
|
||||||
<article class="group bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm border border-gray-200/50 dark:border-gray-700/50 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-[1.02] overflow-hidden">
|
|
||||||
<div class="p-6">
|
|
||||||
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6">
|
|
||||||
{# Main Content Section #}
|
|
||||||
<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">
|
|
||||||
<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>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{# Status Badge #}
|
|
||||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold border
|
|
||||||
{% if park.status == 'operating' %}bg-green-50 text-green-700 border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800
|
|
||||||
{% elif park.status == 'closed' %}bg-red-50 text-red-700 border-red-200 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800
|
|
||||||
{% elif park.status == 'seasonal' %}bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800
|
|
||||||
{% else %}bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600{% endif %}">
|
|
||||||
{{ park.get_status_display }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if park.operator %}
|
|
||||||
<div class="text-base font-medium text-gray-600 dark:text-gray-400 mb-3">
|
|
||||||
{{ park.operator.name }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if park.description %}
|
|
||||||
<p class="text-gray-600 dark:text-gray-400 line-clamp-2 mb-4">
|
|
||||||
{{ park.description|truncatewords:30 }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Stats Section #}
|
|
||||||
{% if park.ride_count or park.coaster_count %}
|
|
||||||
<div class="flex items-center space-x-6 text-sm">
|
|
||||||
{% if park.ride_count %}
|
|
||||||
<div class="flex items-center space-x-2 px-4 py-2 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 rounded-lg border border-blue-200/50 dark:border-blue-800/50">
|
|
||||||
<svg class="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
|
|
||||||
</svg>
|
|
||||||
<span class="font-semibold text-blue-700 dark:text-blue-300">{{ park.ride_count }}</span>
|
|
||||||
<span class="text-blue-600 dark:text-blue-400">rides</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if park.coaster_count %}
|
|
||||||
<div class="flex items-center space-x-2 px-4 py-2 bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-900/20 dark:to-pink-900/20 rounded-lg border border-purple-200/50 dark:border-purple-800/50">
|
|
||||||
<svg class="w-5 h-5 text-purple-600 dark:text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
|
||||||
</svg>
|
|
||||||
<span class="font-semibold text-purple-700 dark:text-purple-300">{{ park.coaster_count }}</span>
|
|
||||||
<span class="text-purple-600 dark:text-purple-400">coasters</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% else %}
|
|
||||||
{# Enhanced Grid View Item #}
|
|
||||||
<article class="group bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm border border-gray-200/50 dark:border-gray-700/50 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105 hover:-rotate-1 overflow-hidden">
|
|
||||||
{# Card Header with Gradient #}
|
|
||||||
<div class="h-2 bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"></div>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
<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>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{# Status Badge #}
|
|
||||||
<span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold border shrink-0
|
|
||||||
{% if park.status == 'operating' %}bg-green-50 text-green-700 border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800
|
|
||||||
{% elif park.status == 'closed' %}bg-red-50 text-red-700 border-red-200 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800
|
|
||||||
{% elif park.status == 'seasonal' %}bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800
|
|
||||||
{% else %}bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600{% endif %}">
|
|
||||||
{{ park.get_status_display }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if park.operator %}
|
|
||||||
<div class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3 truncate">
|
|
||||||
{{ park.operator.name }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if park.description %}
|
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-3 mb-4">
|
|
||||||
{{ park.description|truncatewords:15 }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Stats Footer #}
|
|
||||||
{% if park.ride_count or park.coaster_count %}
|
|
||||||
<div class="flex items-center justify-between pt-4 border-t border-gray-200/50 dark:border-gray-600/50">
|
|
||||||
<div class="flex items-center space-x-4 text-sm">
|
|
||||||
{% if park.ride_count %}
|
|
||||||
<div class="flex items-center space-x-1 text-blue-600 dark:text-blue-400">
|
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
|
|
||||||
</svg>
|
|
||||||
<span class="font-semibold">{{ park.ride_count }}</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if park.coaster_count %}
|
|
||||||
<div class="flex items-center space-x-1 text-purple-600 dark:text-purple-400">
|
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
|
||||||
</svg>
|
|
||||||
<span class="font-semibold">{{ park.coaster_count }}</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# View Details Arrow #}
|
|
||||||
<div class="text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors duration-200">
|
|
||||||
<svg class="w-5 h-5 transform group-hover:translate-x-1 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<div class="{% if view_mode == 'list' %}w-full{% else %}col-span-full{% endif %} p-12 text-center" data-testid="no-parks-found">
|
<div class="{% if view_mode == 'list' %}w-full{% else %}col-span-full{% endif %} p-12 text-center" data-testid="no-parks-found">
|
||||||
<div class="mx-auto w-24 h-24 text-gray-300 dark:text-gray-600 mb-6">
|
<div class="mx-auto w-24 h-24 text-gray-300 dark:text-gray-600 mb-6">
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ Features:
|
|||||||
<!-- Name and category -->
|
<!-- Name and category -->
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-1">
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-1">
|
||||||
<a href="{% url 'rides:ride_detail' ride.id %}"
|
<a href="{% url 'rides:ride_detail' ride.slug %}"
|
||||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||||
{{ ride.name }}
|
{{ ride.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
{% load cotton %}
|
||||||
|
|
||||||
<!-- Active filters display (mobile and desktop) -->
|
<!-- Active filters display (mobile and desktop) -->
|
||||||
{% if has_filters %}
|
{% if has_filters %}
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
@@ -83,157 +85,7 @@
|
|||||||
{% if rides %}
|
{% if rides %}
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
{% for ride in rides %}
|
{% for ride in rides %}
|
||||||
<div class="ride-card bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden hover:shadow-lg transition-all duration-300">
|
<c-ride_card ride=ride />
|
||||||
<!-- Ride image -->
|
|
||||||
<div class="relative h-48 bg-gradient-to-br from-blue-500 to-purple-600">
|
|
||||||
{% if ride.image %}
|
|
||||||
<img src="{{ ride.image.url }}"
|
|
||||||
alt="{{ ride.name }}"
|
|
||||||
class="w-full h-full object-cover">
|
|
||||||
{% else %}
|
|
||||||
<div class="flex items-center justify-center h-full">
|
|
||||||
<i class="fas fa-rocket text-4xl text-white opacity-50"></i>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Status badge -->
|
|
||||||
<div class="absolute top-3 right-3">
|
|
||||||
{% if ride.operating_status == 'operating' %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
|
||||||
<i class="fas fa-play-circle mr-1"></i>
|
|
||||||
Operating
|
|
||||||
</span>
|
|
||||||
{% elif ride.operating_status == 'closed_temporarily' %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
|
|
||||||
<i class="fas fa-pause-circle mr-1"></i>
|
|
||||||
Temporarily Closed
|
|
||||||
</span>
|
|
||||||
{% elif ride.operating_status == 'closed_permanently' %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200">
|
|
||||||
<i class="fas fa-stop-circle mr-1"></i>
|
|
||||||
Permanently Closed
|
|
||||||
</span>
|
|
||||||
{% elif ride.operating_status == 'under_construction' %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
|
||||||
<i class="fas fa-hard-hat mr-1"></i>
|
|
||||||
Under Construction
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Ride details -->
|
|
||||||
<div class="p-5">
|
|
||||||
<!-- Name and category -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-1">
|
|
||||||
<a href="{% url 'rides:ride_detail' ride.id %}"
|
|
||||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
|
||||||
{{ ride.name }}
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300 mr-2">
|
|
||||||
{{ ride.category|default:"Ride" }}
|
|
||||||
</span>
|
|
||||||
{% if ride.park %}
|
|
||||||
<span class="flex items-center">
|
|
||||||
<i class="fas fa-map-marker-alt mr-1"></i>
|
|
||||||
{{ ride.park.name }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Key stats -->
|
|
||||||
<div class="grid grid-cols-2 gap-3 mb-4">
|
|
||||||
{% if ride.height %}
|
|
||||||
<div class="text-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
||||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ ride.height }}ft</div>
|
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-400">Height</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ride.rollercoaster_stats.max_speed %}
|
|
||||||
<div class="text-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
||||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ ride.rollercoaster_stats.max_speed }}mph</div>
|
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-400">Top Speed</div>
|
|
||||||
</div>
|
|
||||||
{% elif ride.max_speed %}
|
|
||||||
<div class="text-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
||||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ ride.max_speed }}mph</div>
|
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-400">Max Speed</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ride.capacity_per_hour %}
|
|
||||||
<div class="text-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
||||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ ride.capacity_per_hour }}</div>
|
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-400">Capacity/Hr</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ride.duration %}
|
|
||||||
<div class="text-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
||||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ ride.duration }}s</div>
|
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-400">Duration</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Special features -->
|
|
||||||
{% if ride.has_inversions or ride.has_launches or ride.rollercoaster_stats %}
|
|
||||||
<div class="flex flex-wrap gap-1 mb-3">
|
|
||||||
{% if ride.has_inversions %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
|
|
||||||
<i class="fas fa-sync-alt mr-1"></i>
|
|
||||||
{% if ride.rollercoaster_stats.number_of_inversions %}
|
|
||||||
{{ ride.rollercoaster_stats.number_of_inversions }} Inversion{{ ride.rollercoaster_stats.number_of_inversions|pluralize }}
|
|
||||||
{% else %}
|
|
||||||
Inversions
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ride.has_launches %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200">
|
|
||||||
<i class="fas fa-rocket mr-1"></i>
|
|
||||||
{% if ride.rollercoaster_stats.number_of_launches %}
|
|
||||||
{{ ride.rollercoaster_stats.number_of_launches }} Launch{{ ride.rollercoaster_stats.number_of_launches|pluralize }}
|
|
||||||
{% else %}
|
|
||||||
Launched
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ride.rollercoaster_stats.track_type %}
|
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
|
||||||
{{ ride.rollercoaster_stats.track_type|title }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Opening date -->
|
|
||||||
{% if ride.opened_date %}
|
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
|
||||||
<i class="fas fa-calendar mr-1"></i>
|
|
||||||
Opened {{ ride.opened_date|date:"F j, Y" }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Manufacturer -->
|
|
||||||
{% if ride.manufacturer %}
|
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
<i class="fas fa-industry mr-1"></i>
|
|
||||||
{{ ride.manufacturer.name }}
|
|
||||||
{% if ride.designer and ride.designer != ride.manufacturer %}
|
|
||||||
• Designed by {{ ride.designer.name }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user