import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Header } from '@/components/layout/Header'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Filter, SlidersHorizontal, FerrisWheel, Plus, ChevronDown, PanelLeftClose, PanelLeftOpen, Grid3X3, List } from 'lucide-react'; import { cn } from '@/lib/utils'; import { AutocompleteSearch } from '@/components/search/AutocompleteSearch'; import { RideCard } from '@/components/rides/RideCard'; import { RideListView } from '@/components/rides/RideListView'; import { RideForm } from '@/components/admin/RideForm'; import { RideFilters, RideFilterState, defaultRideFilters } from '@/components/rides/RideFilters'; import { Ride, Park } from '@/types/database'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; import { toast } from '@/hooks/use-toast'; import { getErrorMessage } from '@/lib/errorHandler'; import { useAuthModal } from '@/hooks/useAuthModal'; import { useDocumentTitle } from '@/hooks/useDocumentTitle'; import { useOpenGraph } from '@/hooks/useOpenGraph'; import { useRides } from '@/hooks/rides/useRides'; import { Pagination } from '@/components/common/Pagination'; export default function Rides() { useDocumentTitle('Rides & Attractions'); const navigate = useNavigate(); const { user } = useAuth(); const { isModerator } = useUserRole(); const { requireAuth } = useAuthModal(); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState('name'); const [filters, setFilters] = useState(defaultRideFilters); const [showFilters, setShowFilters] = useState(false); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [sidebarCollapsed, setSidebarCollapsed] = useState(() => { const saved = localStorage.getItem('rides-sidebar-collapsed'); return saved ? JSON.parse(saved) : false; }); // Use TanStack Query hook for data fetching with caching const { data: rides = [], isLoading: loading, error } = useRides(); useEffect(() => { localStorage.setItem('rides-sidebar-collapsed', JSON.stringify(sidebarCollapsed)); }, [sidebarCollapsed]); // Show error toast if query fails useEffect(() => { if (error) { toast({ title: "Error loading rides", description: error instanceof Error ? error.message : 'Failed to load rides', variant: "destructive" }); } }, [error]); const handleCreateSubmit = async (data: any) => { try { const { submitRideCreation } = await import('@/lib/entitySubmissionHelpers'); await submitRideCreation(data, user!.id); toast({ title: "Ride Submitted", description: "Your ride submission has been sent for moderation review.", }); setIsCreateModalOpen(false); } catch (error) { const errorMsg = getErrorMessage(error); toast({ title: "Submission Failed", description: errorMsg || "Failed to submit ride.", variant: "destructive" }); } }; const filteredAndSortedRides = React.useMemo(() => { let filtered = rides.filter(ride => { // Search filter if (searchQuery) { const searchTerm = searchQuery.toLowerCase(); const matchesSearch = ride.name.toLowerCase().includes(searchTerm) || ride.park?.name?.toLowerCase().includes(searchTerm) || ride.manufacturer?.name?.toLowerCase().includes(searchTerm) || ride.designer?.name?.toLowerCase().includes(searchTerm); if (!matchesSearch) return false; } // Category filter if (filters.categories.length > 0 && !filters.categories.includes(ride.category)) { return false; } // Status filter if (filters.status !== 'all' && ride.status !== filters.status) { return false; } // Country filter if (filters.countries.length > 0) { if (!ride.park?.location?.country || !filters.countries.includes(ride.park.location.country)) { return false; } } // States filter if (filters.statesProvinces.length > 0) { if (!ride.park?.location?.state_province || !filters.statesProvinces.includes(ride.park.location.state_province)) { return false; } } // Cities filter if (filters.cities.length > 0) { if (!ride.park?.location?.city || !filters.cities.includes(ride.park.location.city)) { return false; } } // Parks filter if (filters.parks.length > 0) { // Use park_id from the ride object, not the nested park if (!ride.park_id || !filters.parks.includes(ride.park_id)) { return false; } } // Manufacturer filter if (filters.manufacturers.length > 0) { if (!ride.manufacturer?.id || !filters.manufacturers.includes(ride.manufacturer.id)) { return false; } } // Designer filter if (filters.designers.length > 0) { if (!ride.designer?.id || !filters.designers.includes(ride.designer.id)) { return false; } } // Coaster type filter if (filters.coasterTypes.length > 0) { // Assuming coaster_type is a field on the ride if (!ride.coaster_type || !filters.coasterTypes.includes(ride.coaster_type)) { return false; } } // Seating type filter if (filters.seatingTypes.length > 0) { if (!ride.seating_type || !filters.seatingTypes.includes(ride.seating_type)) { return false; } } // Intensity level filter if (filters.intensityLevels.length > 0) { if (!ride.intensity_level || !filters.intensityLevels.includes(ride.intensity_level)) { return false; } } // Track material filter (array field) if (filters.trackMaterials.length > 0) { if (!ride.track_material || ride.track_material.length === 0 || !ride.track_material.some(material => filters.trackMaterials.includes(material))) { return false; } } // Support material filter (array field) if (filters.supportMaterials.length > 0) { if (!ride.support_material || ride.support_material.length === 0 || !ride.support_material.some(material => filters.supportMaterials.includes(material))) { return false; } } // Propulsion method filter (array field) if (filters.propulsionMethods.length > 0) { if (!ride.propulsion_method || ride.propulsion_method.length === 0 || !ride.propulsion_method.some(method => filters.propulsionMethods.includes(method))) { return false; } } // Speed filter if (filters.minSpeed > 0 || filters.maxSpeed < 200) { const speed = ride.max_speed_kmh || 0; if (speed < filters.minSpeed || speed > filters.maxSpeed) { return false; } } // Height filter if (filters.minHeight > 0 || filters.maxHeight < 150) { const height = ride.max_height_meters || 0; if (height < filters.minHeight || height > filters.maxHeight) { return false; } } // Length filter if (filters.minLength > 0 || filters.maxLength < 3000) { const length = ride.length_meters || 0; if (length < filters.minLength || length > filters.maxLength) { return false; } } // Inversions filter if (filters.minInversions > 0 || filters.maxInversions < 14) { const inversions = ride.inversions || 0; if (inversions < filters.minInversions || inversions > filters.maxInversions) { return false; } } // Has inversions checkbox if (filters.hasInversions && (!ride.inversions || ride.inversions === 0)) { return false; } // Opening date filter if (filters.openingDateFrom || filters.openingDateTo) { if (!ride.opening_date) { return false; } const openingDate = new Date(ride.opening_date); if (filters.openingDateFrom && openingDate < filters.openingDateFrom) { return false; } if (filters.openingDateTo && openingDate > filters.openingDateTo) { return false; } } // Operating only filter if (filters.operatingOnly && ride.status !== 'operating') { return false; } return true; }); // Apply sorting filtered.sort((a, b) => { switch (sortBy) { case 'name': return a.name.localeCompare(b.name); case 'rating': return (b.average_rating || 0) - (a.average_rating || 0); case 'speed': return (b.max_speed_kmh || 0) - (a.max_speed_kmh || 0); case 'height': return (b.max_height_meters || 0) - (a.max_height_meters || 0); case 'reviews': return (b.review_count || 0) - (a.review_count || 0); default: return 0; } }); return filtered; }, [rides, searchQuery, sortBy, filters]); // Pagination for display const ITEMS_PER_PAGE = 24; const paginatedRides = React.useMemo(() => { const start = (currentPage - 1) * ITEMS_PER_PAGE; const end = start + ITEMS_PER_PAGE; return filteredAndSortedRides.slice(start, end); }, [filteredAndSortedRides, currentPage]); const totalPages = Math.ceil(filteredAndSortedRides.length / ITEMS_PER_PAGE); // Reset to page 1 when filters change useEffect(() => { setCurrentPage(1); }, [filters, sortBy, searchQuery]); const generateDescription = () => { if (!filteredAndSortedRides.length) return 'Discover thrilling rides and roller coasters worldwide'; const activeFilters: string[] = []; if (filters.categories.length > 0) activeFilters.push(...filters.categories); if (filters.status !== 'all') activeFilters.push(filters.status); if (activeFilters.length > 0) { return `Explore ${filteredAndSortedRides.length} ${activeFilters.join(' ')} rides and attractions`; } return `Explore ${filteredAndSortedRides.length} rides and roller coasters worldwide`; }; useOpenGraph({ title: 'Rides & Attractions - ThrillWiki', description: generateDescription(), imageUrl: filteredAndSortedRides[0]?.banner_image_url ?? undefined, imageId: filteredAndSortedRides[0]?.banner_image_id ?? undefined, type: 'website', enabled: !loading }); const categories = [ { value: 'all', label: 'All Categories' }, { value: 'roller_coaster', label: 'Roller Coasters' }, { value: 'flat_ride', label: 'Flat Rides' }, { value: 'water_ride', label: 'Water Rides' }, { value: 'dark_ride', label: 'Dark Rides' }, { value: 'kiddie_ride', label: 'Kiddie Rides' }, { value: 'transportation', label: 'Transportation' } ]; const statusOptions = [ { value: 'all', label: 'All Status' }, { value: 'operating', label: 'Operating' }, { value: 'seasonal', label: 'Seasonal' }, { value: 'under_construction', label: 'Under Construction' }, { value: 'closed', label: 'Closed' } ]; if (loading) { return (
{[...Array(8)].map((_, i) => (
))}
); } return (
{/* Page Header */}

Rides & Attractions

Explore amazing rides and attractions from theme parks worldwide

{filteredAndSortedRides.length} rides {rides.filter(r => r.category === 'roller_coaster').length} coasters
{/* Search and Filters */}
{/* Desktop: Filter toggle on the left */} {/* Search bar takes remaining space */}
setSearchQuery(query)} showRecentSearches={false} />
{/* Sort controls - more compact */}
{/* Mobile filter toggle */} setViewMode(v as 'grid' | 'list')} className="hidden md:inline-flex">
{/* Mobile Filters */}
{/* Main Content Area with Sidebar */}
{/* Desktop Filter Sidebar */} {!sidebarCollapsed && ( )} {/* Results Area */}
{filteredAndSortedRides.length > 0 ? (
{viewMode === 'grid' ? (
{paginatedRides.map((ride) => ( ))}
) : ( navigate(`/parks/${ride.park?.slug}/rides/${ride.slug}`)} /> )}
) : (

No rides found

Try adjusting your search criteria or filters

)}
{/* Create Modal */} setIsCreateModalOpen(false)} isEditing={false} />
); }