diff --git a/.replit b/.replit
index 3520ba63..809f821d 100644
--- a/.replit
+++ b/.replit
@@ -54,6 +54,10 @@ outputType = "webview"
localPort = 5000
externalPort = 80
+[[ports]]
+localPort = 32933
+externalPort = 3002
+
[[ports]]
localPort = 41923
externalPort = 3000
diff --git a/replit.md b/replit.md
index 4ebceeb3..08beef24 100644
--- a/replit.md
+++ b/replit.md
@@ -56,6 +56,7 @@ Migrations: All applied successfully (including circular dependency resolution)
✅ **Spatial Data Support**: GeoDjango Point objects and spatial functionality working correctly
✅ **CloudflareImages Integration**: Avatar functionality preserved with proper foreign key relationships
✅ **Django-Cotton Integration**: Modern component-based template system with EXACT visual preservation
+✅ **Cotton Components**: Standardized park_card.html and ride_card.html components with full feature support
### API Endpoints Available
- `/api/v1/parks/` - Parks API with spatial data
diff --git a/static/css/tailwind.css b/static/css/tailwind.css
index 8481364f..9e447eeb 100644
--- a/static/css/tailwind.css
+++ b/static/css/tailwind.css
@@ -22,6 +22,7 @@
--color-orange-200: oklch(90.1% 0.076 70.697);
--color-orange-600: oklch(64.6% 0.222 41.116);
--color-orange-800: oklch(47% 0.157 37.304);
+ --color-orange-900: oklch(40.8% 0.123 38.172);
--color-amber-100: oklch(96.2% 0.059 95.617);
--color-amber-800: oklch(47.3% 0.137 46.201);
--color-yellow-50: oklch(98.7% 0.026 102.212);
@@ -1730,6 +1731,10 @@
--tw-gradient-to: var(--color-blue-50);
--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-blue-600 {
+ --tw-gradient-to: var(--color-blue-600);
+ --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-gray-100 {
--tw-gradient-to: var(--color-gray-100);
--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));
@@ -3907,6 +3912,11 @@
}
}
}
+ .dark\:bg-orange-900 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-orange-900);
+ }
+ }
.dark\:bg-purple-800 {
@media (prefers-color-scheme: dark) {
background-color: var(--color-purple-800);
@@ -4239,6 +4249,11 @@
color: var(--color-green-900);
}
}
+ .dark\:text-orange-200 {
+ @media (prefers-color-scheme: dark) {
+ color: var(--color-orange-200);
+ }
+ }
.dark\:text-primary {
@media (prefers-color-scheme: dark) {
color: var(--color-primary);
diff --git a/templates/cotton/park_card.html b/templates/cotton/park_card.html
new file mode 100644
index 00000000..ed8a4c8b
--- /dev/null
+++ b/templates/cotton/park_card.html
@@ -0,0 +1,190 @@
+{% comment %}
+Park Card Component - Django Cotton Version
+
+A reusable park card component that supports both list and grid view modes.
+Includes status badges, operator information, description, and ride/coaster statistics.
+
+Usage Examples:
+
+List View:
+
+
+Grid View:
+
+
+With custom CSS classes:
+
+
+Parameters:
+- park: Park object (required)
+- view_mode: "list" or "grid" (default: "grid")
+- class: Additional CSS classes (optional)
+
+Features:
+- Responsive design with hover effects
+- Status badge with proper color coding
+- Operator information display
+- Description with automatic truncation (30 words for list, 15 for grid)
+- Ride and coaster count statistics with icons
+- Gradient effects and modern styling
+- Links to park detail pages
+{% endcomment %}
+
+
+
+{% if park %}
+ {% if view_mode == 'list' %}
+ {# Enhanced List View Item #}
+
+
+
+ {# Main Content Section #}
+
+
+
+
+ {# Status Badge #}
+
+ {{ park.get_status_display }}
+
+
+
+ {% if park.operator %}
+
+ {{ park.operator.name }}
+
+ {% endif %}
+
+ {% if park.description %}
+
+ {{ park.description|truncatewords:30 }}
+
+ {% endif %}
+
+
+ {# Stats Section #}
+ {% if park.ride_count or park.coaster_count %}
+
+ {% if park.ride_count %}
+
+
+
{{ park.ride_count }}
+
rides
+
+ {% endif %}
+ {% if park.coaster_count %}
+
+
+
{{ park.coaster_count }}
+
coasters
+
+ {% endif %}
+
+ {% endif %}
+
+
+
+ {% else %}
+ {# Enhanced Grid View Item #}
+
+ {# Card Header with Gradient #}
+
+
+
+
+
+
+ {# Status Badge #}
+
+ {{ park.get_status_display }}
+
+
+
+ {% if park.operator %}
+
+ {{ park.operator.name }}
+
+ {% endif %}
+
+ {% if park.description %}
+
+ {{ park.description|truncatewords:15 }}
+
+ {% endif %}
+
+ {# Stats Footer #}
+ {% if park.ride_count or park.coaster_count %}
+
+
+ {% if park.ride_count %}
+
+
+
{{ park.ride_count }}
+
+ {% endif %}
+ {% if park.coaster_count %}
+
+
+
{{ park.coaster_count }}
+
+ {% endif %}
+
+
+ {# View Details Arrow #}
+
+
+ {% endif %}
+
+
+ {% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/cotton/ride_card.html b/templates/cotton/ride_card.html
new file mode 100644
index 00000000..da69497c
--- /dev/null
+++ b/templates/cotton/ride_card.html
@@ -0,0 +1,215 @@
+{% 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.
+
+Usage Examples:
+
+Basic usage:
+
+
+With custom CSS classes:
+
+
+With custom image fallback:
+
+
+Parameters:
+- ride: Ride object (required)
+- class: Additional CSS classes (optional)
+- fallback_gradient: Custom gradient for image fallback (default: "from-blue-500 to-purple-600")
+
+Features:
+- 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
+- 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
+{% endcomment %}
+
+
+
+{% if ride %}
+
+
+
+ {% if ride.image %}
+

+ {% else %}
+
+
+
+ {% endif %}
+
+
+
+ {% if ride.operating_status == 'operating' or ride.operating_status == 'OPERATING' %}
+
+
+ Operating
+
+ {% elif ride.operating_status == 'closed_temporarily' or ride.operating_status == 'CLOSED_TEMP' %}
+
+
+ Temporarily Closed
+
+ {% elif ride.operating_status == 'closed_permanently' or ride.operating_status == 'CLOSED_PERM' %}
+
+
+ Permanently Closed
+
+ {% elif ride.operating_status == 'under_construction' or ride.operating_status == 'UNDER_CONSTRUCTION' %}
+
+
+ Under Construction
+
+ {% elif ride.operating_status == 'sbno' or ride.operating_status == 'SBNO' %}
+
+
+ SBNO
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {{ ride.category|default:"Ride" }}
+
+ {% if ride.park %}
+
+
+ {{ ride.park.name }}
+
+ {% endif %}
+
+
+
+
+
+ {% if ride.height %}
+
+
{{ ride.height }}ft
+
Height
+
+ {% endif %}
+
+ {% if ride.rollercoaster_stats.max_speed %}
+
+
{{ ride.rollercoaster_stats.max_speed }}mph
+
Top Speed
+
+ {% elif ride.max_speed %}
+
+
{{ ride.max_speed }}mph
+
Max Speed
+
+ {% endif %}
+
+ {% if ride.capacity_per_hour %}
+
+
{{ ride.capacity_per_hour }}
+
Capacity/Hr
+
+ {% endif %}
+
+ {% if ride.duration %}
+
+
{{ ride.duration }}s
+
Duration
+
+ {% endif %}
+
+
+
+ {% if ride.has_inversions or ride.has_launches or ride.rollercoaster_stats.track_type or ride.track_type %}
+
+ {% if ride.has_inversions %}
+
+
+ {% if ride.rollercoaster_stats.number_of_inversions %}
+ {{ ride.rollercoaster_stats.number_of_inversions }} Inversion{{ ride.rollercoaster_stats.number_of_inversions|pluralize }}
+ {% else %}
+ Inversions
+ {% endif %}
+
+ {% endif %}
+
+ {% if ride.has_launches %}
+
+
+ {% if ride.rollercoaster_stats.number_of_launches %}
+ {{ ride.rollercoaster_stats.number_of_launches }} Launch{{ ride.rollercoaster_stats.number_of_launches|pluralize }}
+ {% else %}
+ Launched
+ {% endif %}
+
+ {% endif %}
+
+ {% if ride.rollercoaster_stats.track_type %}
+
+ {{ ride.rollercoaster_stats.track_type|title }}
+
+ {% elif ride.track_type %}
+
+ {{ ride.track_type|title }}
+
+ {% endif %}
+
+ {% endif %}
+
+
+ {% if ride.opened_date %}
+
+
+ Opened {{ ride.opened_date|date:"F j, Y" }}
+
+ {% endif %}
+
+
+ {% if ride.manufacturer or ride.designer %}
+
+ {% if ride.manufacturer %}
+
+
+ {{ ride.manufacturer.name }}
+
+ {% endif %}
+ {% if ride.designer and ride.designer != ride.manufacturer %}
+
+
+ Designed by {{ ride.designer.name }}
+
+ {% endif %}
+
+ {% endif %}
+
+
+{% endif %}
\ No newline at end of file