Files
thrillwiki_django_no_react/templates/cotton/ride_card.html
pac7 ffebd5ce01 Standardize park and ride cards with django-cotton component
Updates CSS with new Tailwind classes and refactors ride card template to use django-cotton, implementing park-specific URL generation and graceful handling of missing slugs.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0bdea3fb-49ea-4863-b501-fa6f5af0cbf0
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
2025-09-22 03:35:47 +00:00

242 lines
11 KiB
HTML

{% comment %}
Ride Card Component - Django Cotton Version
A comprehensive ride card component with image handling, status badges, feature displays,
and robust URL generation that supports both global and park-specific URL patterns.
Includes graceful handling of missing slugs to prevent 500 errors.
Usage Examples:
Basic usage (default global URL pattern):
<c-ride_card ride=ride />
Park-specific URL pattern:
<c-ride_card ride=ride url_variant="park" />
With custom CSS classes:
<c-ride_card
ride=ride
url_variant="global"
class="custom-class"
/>
With custom image fallback:
<c-ride_card
ride=ride
url_variant="park"
fallback_gradient="from-red-500 to-blue-600"
/>
Parameters:
- ride: Ride object (required)
- url_variant: URL pattern type - 'global' (default) or 'park' (optional)
- class: Additional CSS classes (optional)
- fallback_gradient: Custom gradient for image fallback (default: "from-blue-500 to-purple-600")
URL Pattern Logic:
- If url_variant='global' and ride.slug exists: uses rides:ride_detail with ride.slug
- If url_variant='park' and both ride.park.slug and ride.slug exist: uses parks:rides:ride_detail with ride.park.slug, ride.slug
- If no valid URL can be generated: renders ride name as plain text (no link)
Features:
- Graceful handling of missing slugs (prevents NoReverseMatch errors)
- Support for both global and park-specific URL patterns
- Image handling with gradient fallback backgrounds
- Status badges with proper color coding (operating, closed_temporarily, closed_permanently, under_construction)
- Ride name with conditional linking based on slug availability
- Category and park information display
- Statistics grid for height, speed, capacity, duration
- Special features badges (inversions, launches, track_type)
- Opening date and manufacturer/designer information
- Responsive design with hover effects
- Modern Tailwind styling and animations
- Backwards compatibility with existing usage
{% endcomment %}
<c-vars
ride=""
url_variant="global"
class=""
fallback_gradient="from-blue-500 to-purple-600"
/>
{% if ride %}
<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 {{ class }}">
<!-- Ride image -->
<div class="relative h-48 bg-gradient-to-br {{ fallback_gradient }}">
{% 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' or 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' or ride.operating_status == 'CLOSED_TEMP' %}
<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' or ride.operating_status == 'CLOSED_PERM' %}
<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' or 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>
{% elif ride.operating_status == 'sbno' or ride.operating_status == 'SBNO' %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200">
<i class="fas fa-pause-circle mr-1"></i>
SBNO
</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">
{% comment %}Robust URL generation with missing slug handling{% endcomment %}
{% if url_variant == 'park' and ride.park and ride.park.slug and ride.slug %}
<a href="{% url 'parks:rides:ride_detail' ride.park.slug ride.slug %}"
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
{{ ride.name }}
</a>
{% elif url_variant == 'global' and ride.slug %}
<a href="{% url 'rides:ride_detail' ride.slug %}"
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
{{ ride.name }}
</a>
{% else %}
{% comment %}No valid URL can be generated - render as plain text{% endcomment %}
{{ ride.name }}
{% endif %}
</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 grid -->
<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.track_type or ride.track_type %}
<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>
{% elif ride.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.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 and designer -->
{% if ride.manufacturer or ride.designer %}
<div class="text-sm text-gray-600 dark:text-gray-400">
{% if ride.manufacturer %}
<div class="flex items-center mb-1">
<i class="fas fa-industry mr-1"></i>
<span>{{ ride.manufacturer.name }}</span>
</div>
{% endif %}
{% if ride.designer and ride.designer != ride.manufacturer %}
<div class="flex items-center">
<i class="fas fa-drafting-compass mr-1"></i>
<span>Designed by {{ ride.designer.name }}</span>
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% endif %}