diff --git a/.replit b/.replit index 1a513a3c..5bd91547 100644 --- a/.replit +++ b/.replit @@ -55,7 +55,7 @@ localPort = 5000 externalPort = 80 [[ports]] -localPort = 35685 +localPort = 37689 externalPort = 3002 [[ports]] diff --git a/static/css/tailwind.css b/static/css/tailwind.css index 9e447eeb..590d1485 100644 --- a/static/css/tailwind.css +++ b/static/css/tailwind.css @@ -410,6 +410,9 @@ .top-0 { top: calc(var(--spacing) * 0); } + .top-1 { + top: calc(var(--spacing) * 1); + } .top-1\/2 { top: calc(1/2 * 100%); } @@ -449,6 +452,9 @@ .left-0 { left: calc(var(--spacing) * 0); } + .left-1 { + left: calc(var(--spacing) * 1); + } .left-1\/2 { left: calc(1/2 * 100%); } @@ -527,6 +533,9 @@ max-width: 96rem; } } + .-mx-1 { + margin-inline: calc(var(--spacing) * -1); + } .-mx-1\.5 { margin-inline: calc(var(--spacing) * -1.5); } @@ -545,6 +554,9 @@ .mx-auto { margin-inline: auto; } + .-my-1 { + margin-block: calc(var(--spacing) * -1); + } .-my-1\.5 { margin-block: calc(var(--spacing) * -1.5); } @@ -554,6 +566,9 @@ .my-auto { margin-block: auto; } + .mt-0 { + margin-top: calc(var(--spacing) * 0); + } .mt-0\.5 { margin-top: calc(var(--spacing) * 0.5); } @@ -626,6 +641,9 @@ .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); } @@ -797,6 +815,9 @@ .min-h-screen { min-height: 100vh; } + .w-1 { + width: calc(var(--spacing) * 1); + } .w-1\/2 { width: calc(1/2 * 100%); } @@ -932,6 +953,13 @@ .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); @@ -948,6 +976,10 @@ --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); @@ -1130,6 +1162,13 @@ 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; @@ -1394,6 +1433,9 @@ .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)) { @@ -1442,6 +1484,9 @@ .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)) { @@ -1496,6 +1541,9 @@ .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)) { @@ -1550,6 +1598,9 @@ .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)) { @@ -1607,6 +1658,9 @@ .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)) { @@ -1625,6 +1679,10 @@ --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)) { @@ -1685,6 +1743,10 @@ --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)) { @@ -1720,6 +1782,10 @@ --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)) { @@ -1849,6 +1915,9 @@ .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); } @@ -2185,6 +2254,9 @@ .italic { font-style: italic; } + .underline { + text-decoration-line: underline; + } .underline-offset-4 { text-underline-offset: 4px; } diff --git a/templates/cotton/ride_card.html b/templates/cotton/ride_card.html index 0e056337..ffce1f60 100644 --- a/templates/cotton/ride_card.html +++ b/templates/cotton/ride_card.html @@ -1,45 +1,61 @@ {% comment %} Ride Card Component - Django Cotton Version -A comprehensive ride card component with image handling, status badges, and feature displays. -Includes all ride statistics, special features, and manufacturer information. +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: +Basic usage (default global URL pattern): +Park-specific URL pattern: + + With custom CSS classes: With custom image fallback: 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 link to detail page +- 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 %} @@ -94,10 +110,21 @@ Features:

- + {% comment %}Robust URL generation with missing slug handling{% endcomment %} + {% if url_variant == 'park' and ride.park and ride.park.slug and ride.slug %} + + {{ ride.name }} + + {% elif url_variant == 'global' and ride.slug %} + + {{ ride.name }} + + {% else %} + {% comment %}No valid URL can be generated - render as plain text{% endcomment %} {{ ride.name }} - + {% endif %}