import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Header } from '@/components/layout/Header'; 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 { MapPin, Star, Clock, Zap, ArrowLeft, Users, Ruler, Timer, TrendingUp, TrendingDown, Camera, Heart, RotateCcw, AlertTriangle, FerrisWheel, Waves, Theater, Train, Edit } 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 { supabase } from '@/integrations/supabase/client'; import { RideForm } from '@/components/admin/RideForm'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; import { toast } from '@/hooks/use-toast'; export default function RideDetail() { const { parkSlug, rideSlug } = useParams<{ parkSlug: string; rideSlug: string }>(); const navigate = useNavigate(); const [ride, setRide] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState("overview"); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const { user } = useAuth(); const { isModerator } = useUserRole(); useEffect(() => { if (parkSlug && rideSlug) { fetchRideData(); } }, [parkSlug, rideSlug]); const fetchRideData = async () => { try { // First get park to find park_id const { data: parkData } = await supabase .from('parks') .select('id') .eq('slug', parkSlug) .maybeSingle(); if (parkData) { // Then get ride details with park_id stored separately const { data: rideData } = await supabase .from('rides') .select(` *, park:parks!inner(id, name, slug, location:locations(*)), manufacturer:companies!rides_manufacturer_id_fkey(*), designer:companies!rides_designer_id_fkey(*) `) .eq('park_id', parkData.id) .eq('slug', rideSlug) .maybeSingle(); if (rideData) { // Store park_id for easier access (rideData as any).currentParkId = parkData.id; } setRide(rideData); } } catch (error) { console.error('Error fetching ride data:', error); } finally { setLoading(false); } }; 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: any) { toast({ title: "Error", description: error.message || "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 && (
{ride.average_rating.toFixed(1)}
{ride.review_count} {ride.review_count === 1 ? "review" : "reviews"}
)}
{/* Quick Stats */}
{ride.max_speed_kmh && (
)} {ride.max_height_meters && (
)} {ride.length_meters && (
long
)} {ride.duration_seconds && (
{Math.floor(ride.duration_seconds / 60)}:{(ride.duration_seconds % 60).toString().padStart(2, '0')}
duration
)} {ride.capacity_per_hour && (
{ride.capacity_per_hour}
riders/hour
)} {ride.inversions !== null && ride.inversions !== undefined && (
{ride.inversions}
inversions
)} {/* New roller coaster specific stats */} {ride.drop_height_meters && (
drop
)} {ride.max_g_force && (
{ride.max_g_force}g
max G-force
)}
{/* Main Content */} Overview Specifications Reviews Photos
{/* 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('_', ' ')}
{/* 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} /> )}
); }