import { useState, lazy, Suspense, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Header } from '@/components/layout/Header'; import { getBannerUrls } from '@/lib/cloudflareImageUtils'; import { trackPageView } from '@/lib/viewTracking'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Separator } from '@/components/ui/separator'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { AdminFormSkeleton } from '@/components/loading/PageSkeletons'; import { MapPin, Star, Clock, Zap, ArrowLeft, Users, Ruler, Timer, TrendingUp, TrendingDown, Camera, Heart, RotateCcw, AlertTriangle, FerrisWheel, Waves, Theater, Train, Edit, Wrench, Sparkles, Repeat, Baby } from 'lucide-react'; import { ReviewsSection } from '@/components/reviews/ReviewsSection'; import { MeasurementDisplay } from '@/components/ui/measurement-display'; import { EntityPhotoGallery } from '@/components/upload/EntityPhotoGallery'; import { RatingDistribution } from '@/components/rides/RatingDistribution'; import { RideHighlights } from '@/components/rides/RideHighlights'; import { SimilarRides } from '@/components/rides/SimilarRides'; import { FormerNames } from '@/components/rides/FormerNames'; import { RecentPhotosPreview } from '@/components/rides/RecentPhotosPreview'; import { ParkLocationMap } from '@/components/maps/ParkLocationMap'; import { Ride } from '@/types/database'; import { useAuth } from '@/hooks/useAuth'; import { useRideDetail } from '@/hooks/rides/useRideDetail'; import { usePhotoCount } from '@/hooks/photos/usePhotoCount'; // Lazy load admin forms const RideForm = lazy(() => import('@/components/admin/RideForm').then(m => ({ default: m.RideForm }))); import { useUserRole } from '@/hooks/useUserRole'; import { toast } from '@/hooks/use-toast'; import { getErrorMessage } from '@/lib/errorHandler'; import { VersionIndicator } from '@/components/versioning/VersionIndicator'; import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs'; import { useAuthModal } from '@/hooks/useAuthModal'; import { MetaTags } from '@/components/seo'; // Extended Ride type with additional properties for easier access interface RideWithParkId extends Ride { currentParkId?: string; } export default function RideDetail() { const { parkSlug, rideSlug } = useParams<{ parkSlug: string; rideSlug: string }>(); const navigate = useNavigate(); const { user } = useAuth(); const { isModerator } = useUserRole(); const { requireAuth } = useAuthModal(); const [activeTab, setActiveTab] = useState("overview"); const [isEditModalOpen, setIsEditModalOpen] = useState(false); // Fetch ride data with caching const { data: rideData, isLoading: loading } = useRideDetail(parkSlug, rideSlug); // Cast to RideWithParkId to include currentParkId const ride = rideData as RideWithParkId | null; // Fetch photo count with caching const { data: photoCount = 0, isLoading: statsLoading } = usePhotoCount('ride', ride?.id, !!ride?.id); // Track page view when ride is loaded useEffect(() => { if (ride?.id) { trackPageView('ride', ride.id); } }, [ride?.id]); const getStatusColor = (status: string) => { switch (status) { case 'operating': return 'bg-green-500/20 text-green-400 border-green-500/30'; case 'seasonal': return 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'; case 'under_construction': return 'bg-blue-500/20 text-blue-400 border-blue-500/30'; default: return 'bg-red-500/20 text-red-400 border-red-500/30'; } }; const getRideIcon = (category: string) => { switch (category) { case 'roller_coaster': return ; case 'water_ride': return ; case 'dark_ride': return ; case 'flat_ride': return ; case 'kiddie_ride': return ; case 'transportation': return ; default: return ; } }; const formatCategory = (category: string) => { return category.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(' '); }; const handleEditSubmit = async (data: any) => { if (!user || !ride) return; try { // Everyone goes through submission queue const { submitRideUpdate } = await import('@/lib/entitySubmissionHelpers'); await submitRideUpdate(ride.id, data, user.id); toast({ title: "Edit Submitted", description: isModerator() ? "Your edit has been submitted. You can approve it in the moderation queue." : "Your ride edit has been submitted for review." }); setIsEditModalOpen(false); } catch (error) { const errorMsg = getErrorMessage(error); toast({ title: "Error", description: errorMsg || "Failed to submit edit.", variant: "destructive" }); } }; if (loading) { return (
); } if (!ride || !ride.park) { return (

Ride Not Found

The ride you're looking for doesn't exist or has been removed.

); } return (
{/* Back Button and Edit Button */}
{/* Hero Section */}
{(ride.banner_image_url || ride.banner_image_id) ? ( {ride.name} ) : (
{getRideIcon(ride.category)}
)}
{/* Ride Title Overlay */}
{ride.status.replace('_', ' ').toUpperCase()} {formatCategory(ride.category)}

{ride.name}

{ride.park.name}
{(ride.average_rating ?? 0) > 0 && (
{(ride.average_rating ?? 0).toFixed(1)}
{ride.review_count} {ride.review_count === 1 ? "review" : "reviews"}
)}
{/* Quick Stats */}
{ride.max_speed_kmh !== null && ride.max_speed_kmh !== undefined && ride.max_speed_kmh > 0 && (
)} {ride.max_height_meters !== null && ride.max_height_meters !== undefined && ride.max_height_meters > 0 && (
)} {ride.length_meters !== null && ride.length_meters !== undefined && ride.length_meters > 0 && (
long
)} {ride.duration_seconds !== null && ride.duration_seconds !== undefined && ride.duration_seconds > 0 && (
{Math.floor(ride.duration_seconds / 60)}:{(ride.duration_seconds % 60).toString().padStart(2, '0')}
duration
)} {ride.capacity_per_hour !== null && ride.capacity_per_hour !== undefined && ride.capacity_per_hour > 0 && (
{ride.capacity_per_hour}
riders/hour
)} {ride.inversions !== null && ride.inversions !== undefined && (ride.category === 'roller_coaster' || ride.category?.toLowerCase().includes('coaster')) && (
{ride.inversions}
inversions
)} {/* New roller coaster specific stats */} {ride.drop_height_meters !== null && ride.drop_height_meters !== undefined && ride.drop_height_meters > 0 && (
drop
)} {ride.max_g_force !== null && ride.max_g_force !== undefined && ride.max_g_force > 0 && (
{ride.max_g_force}g
max G-force
)}
{/* Main Content */} Overview Specifications Reviews {(ride.review_count ?? 0) > 0 && `(${ride.review_count})`} Photos {!statsLoading && photoCount > 0 && `(${photoCount})`} History
{/* Description */} {ride.description && ( About {ride.name}

{ride.description}

)} {ride.name_history && ride.name_history.length > 0 && ( )} setActiveTab("photos")} />
{/* Ride Information */} Ride Information
Category
{formatCategory(ride.category)}
{ride.opening_date && (
Opened
{ride.opening_date.split('-')[0]}
)} {ride.manufacturer && (
Manufacturer
{ride.manufacturer.name}
)} {ride.designer && (
Designer
{ride.designer.name}
)} {/* Roller Coaster Specific Info */} {ride.category === 'roller_coaster' && (ride.coaster_type || ride.seating_type || ride.intensity_level) && ( <>
Coaster Details
{ride.coaster_type && (
🎢
Type
{ride.coaster_type.charAt(0).toUpperCase() + ride.coaster_type.slice(1)}
)} {ride.seating_type && (
💺
Seating
{ride.seating_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.intensity_level && (
🔥
Intensity
{ride.intensity_level.charAt(0).toUpperCase() + ride.intensity_level.slice(1)}
)}
)}
Located at
{/* Performance Stats */} Performance {ride.max_speed_kmh && (
Maximum Speed
)} {ride.max_height_meters && (
Maximum Height
)} {ride.length_meters && (
Track Length
)} {ride.duration_seconds && (
Ride Duration {Math.floor(ride.duration_seconds / 60)}:{(ride.duration_seconds % 60).toString().padStart(2, '0')}
)} {ride.inversions && ride.inversions > 0 && (
Inversions {ride.inversions}
)} {ride.drop_height_meters && (
Drop Height
)} {ride.max_g_force && (
Maximum G-Force {ride.max_g_force}g
)}
{/* Operational Info */} Operational Details {ride.capacity_per_hour && (
Capacity {ride.capacity_per_hour} riders/hour
)} {ride.height_requirement && (
Height Requirement minimum
)} {ride.age_requirement && (
Age Requirement {ride.age_requirement}+ years
)}
Status {ride.status.replace('_', ' ')}
{/* Coaster Materials & Propulsion */} {ride.category === 'roller_coaster' && ( (ride.track_material?.length ?? 0) > 0 || (ride.support_material?.length ?? 0) > 0 || (ride.propulsion_method?.length ?? 0) > 0 ) && ( Coaster Construction {ride.track_material && ride.track_material.length > 0 && (
Track Material
{ride.track_material.map(material => ( {material.replace('_', ' ').toUpperCase()} ))}
)} {ride.support_material && ride.support_material.length > 0 && (
Support Material
{ride.support_material.map(material => ( {material.replace('_', ' ').toUpperCase()} ))}
)} {ride.propulsion_method && ride.propulsion_method.length > 0 && (
Propulsion Method
{ride.propulsion_method.map(method => ( {method.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())} ))}
)}
)} {/* Water Ride Details */} {ride.category === 'water_ride' && ( ride.water_depth_cm || ride.splash_height_meters || ride.wetness_level || ride.flume_type || ride.boat_capacity ) && ( Water Ride Details {ride.water_depth_cm && (
Water Depth
)} {ride.splash_height_meters && (
Splash Height
)} {ride.wetness_level && (
Wetness Level {ride.wetness_level.charAt(0).toUpperCase() + ride.wetness_level.slice(1)}
)} {ride.flume_type && (
Flume Type {ride.flume_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.boat_capacity && (
Boat Capacity {ride.boat_capacity} riders
)}
)} {/* Dark Ride Details */} {ride.category === 'dark_ride' && ( ride.theme_name || ride.story_description || ride.show_duration_seconds || ride.animatronics_count || ride.projection_type || ride.ride_system || ride.scenes_count ) && ( Dark Ride Details {ride.theme_name && (
Theme {ride.theme_name}
)} {ride.story_description && (
Story

{ride.story_description}

)} {ride.show_duration_seconds && (
Show Duration {Math.floor(ride.show_duration_seconds / 60)}:{(ride.show_duration_seconds % 60).toString().padStart(2, '0')}
)} {ride.animatronics_count && (
Animatronics {ride.animatronics_count}
)} {ride.scenes_count && (
Scenes {ride.scenes_count}
)} {ride.projection_type && (
Projection Type {ride.projection_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.ride_system && (
Ride System {ride.ride_system.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)}
)} {/* Flat Ride Details */} {ride.category === 'flat_ride' && ( ride.rotation_type || ride.motion_pattern || ride.platform_count || ride.swing_angle_degrees || ride.rotation_speed_rpm || ride.arm_length_meters || ride.max_height_reached_meters ) && ( Flat Ride Details {ride.rotation_type && (
Rotation Type {ride.rotation_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.motion_pattern && (
Motion Pattern {ride.motion_pattern.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.platform_count && (
Platforms {ride.platform_count}
)} {ride.swing_angle_degrees && (
Swing Angle {ride.swing_angle_degrees}°
)} {ride.rotation_speed_rpm && (
Rotation Speed {ride.rotation_speed_rpm} RPM
)} {ride.arm_length_meters && (
Arm Length
)} {ride.max_height_reached_meters && (
Max Height Reached
)}
)} {/* Kiddie Ride Details */} {ride.category === 'kiddie_ride' && ( ride.min_age || ride.max_age || ride.educational_theme || ride.character_theme ) && ( Kiddie Ride Details {(ride.min_age || ride.max_age) && (
Age Range {ride.min_age && ride.max_age ? `${ride.min_age}-${ride.max_age} years` : ride.min_age ? `${ride.min_age}+ years` : ride.max_age ? `Up to ${ride.max_age} years` : 'N/A'}
)} {ride.educational_theme && (
Educational Theme {ride.educational_theme}
)} {ride.character_theme && (
Character Theme {ride.character_theme}
)}
)} {/* Transportation Details */} {ride.category === 'transportation' && ( ride.transport_type || ride.route_length_meters || ride.stations_count || ride.vehicle_capacity || ride.vehicles_count || ride.round_trip_duration_seconds ) && ( Transportation Details {ride.transport_type && (
Transport Type {ride.transport_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)} {ride.route_length_meters && (
Route Length
)} {ride.stations_count && (
Stations {ride.stations_count}
)} {ride.vehicle_capacity && (
Vehicle Capacity {ride.vehicle_capacity} passengers
)} {ride.vehicles_count && (
Number of Vehicles {ride.vehicles_count}
)} {ride.round_trip_duration_seconds && (
Round Trip Duration {Math.floor(ride.round_trip_duration_seconds / 60)}:{(ride.round_trip_duration_seconds % 60).toString().padStart(2, '0')}
)}
)}
{/* Edit Ride Modal */} Edit Ride {isModerator() ? "Make changes to this ride. Changes will be applied immediately." : "Submit changes to this ride for review. A moderator will review your submission."} }> {ride && ( setIsEditModalOpen(false)} isEditing={true} /> )}
); }