From ccd603bd8e8b223953ec7f10249496904d4f81a5 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:35:16 +0000 Subject: [PATCH] feat: Add park-specific rides list page --- src/App.tsx | 8 +- src/pages/ParkRides.tsx | 343 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+), 3 deletions(-) create mode 100644 src/pages/ParkRides.tsx diff --git a/src/App.tsx b/src/App.tsx index 05077585..2549212d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import { Footer } from "@/components/layout/Footer"; import Index from "./pages/Index"; import Parks from "./pages/Parks"; import ParkDetail from "./pages/ParkDetail"; +import ParkRides from "./pages/ParkRides"; import RideDetail from "./pages/RideDetail"; import Rides from "./pages/Rides"; import Manufacturers from "./pages/Manufacturers"; @@ -72,9 +73,10 @@ function AppContent() {
} /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/pages/ParkRides.tsx b/src/pages/ParkRides.tsx new file mode 100644 index 00000000..2588eaff --- /dev/null +++ b/src/pages/ParkRides.tsx @@ -0,0 +1,343 @@ +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 { Dialog, DialogContent } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Filter, SlidersHorizontal, FerrisWheel, Plus, ArrowLeft } from 'lucide-react'; +import { AutocompleteSearch } from '@/components/search/AutocompleteSearch'; +import { RideCard } from '@/components/rides/RideCard'; +import { RideForm } from '@/components/admin/RideForm'; +import { Ride, Park } from '@/types/database'; +import { supabase } from '@/integrations/supabase/client'; +import { useAuth } from '@/hooks/useAuth'; +import { toast } from '@/hooks/use-toast'; + +export default function ParkRides() { + const { parkSlug } = useParams<{ parkSlug: string }>(); + const navigate = useNavigate(); + const { user } = useAuth(); + const [park, setPark] = useState(null); + const [rides, setRides] = useState([]); + const [loading, setLoading] = useState(true); + const [searchQuery, setSearchQuery] = useState(''); + const [sortBy, setSortBy] = useState('name'); + const [filterCategory, setFilterCategory] = useState('all'); + const [filterStatus, setFilterStatus] = useState('all'); + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + + useEffect(() => { + if (parkSlug) { + fetchParkAndRides(); + } + }, [parkSlug, sortBy, filterCategory, filterStatus]); + + const fetchParkAndRides = async () => { + try { + setLoading(true); + + // Fetch park details first + const { data: parkData, error: parkError } = await supabase + .from('parks') + .select('*') + .eq('slug', parkSlug) + .maybeSingle(); + + if (parkError) throw parkError; + + if (!parkData) { + toast({ + title: "Park Not Found", + description: "The park you're looking for doesn't exist.", + variant: "destructive" + }); + navigate('/parks'); + return; + } + + setPark(parkData); + + // Fetch rides for this park + let query = supabase + .from('rides') + .select(` + *, + manufacturer:companies!rides_manufacturer_id_fkey(*) + `) + .eq('park_id', parkData.id); + + // Apply filters + if (filterCategory !== 'all') { + query = query.eq('category', filterCategory); + } + if (filterStatus !== 'all') { + query = query.eq('status', filterStatus); + } + + // Apply sorting + switch (sortBy) { + case 'rating': + query = query.order('average_rating', { ascending: false }); + break; + case 'speed': + query = query.order('max_speed_kmh', { ascending: false, nullsFirst: false }); + break; + case 'height': + query = query.order('height_meters', { ascending: false, nullsFirst: false }); + break; + case 'reviews': + query = query.order('review_count', { ascending: false }); + break; + default: + query = query.order('name'); + } + + const { data: ridesData, error: ridesError } = await query; + + if (ridesError) throw ridesError; + + setRides(ridesData || []); + } catch (error) { + console.error('Error fetching park and rides:', error); + toast({ + title: "Error", + description: "Failed to load park rides.", + variant: "destructive" + }); + } finally { + setLoading(false); + } + }; + + const handleCreateSubmit = async (data: any) => { + try { + if (!user) { + navigate('/auth'); + return; + } + + // Pre-fill park_id in the submission + const submissionData = { + ...data, + park_id: park?.id, + }; + + const { submitRideCreation } = await import('@/lib/entitySubmissionHelpers'); + await submitRideCreation(submissionData, user.id); + + toast({ + title: "Ride Submitted", + description: "Your ride submission has been sent for moderation review.", + }); + + setIsCreateModalOpen(false); + } catch (error: any) { + toast({ + title: "Submission Failed", + description: error.message || "Failed to submit ride.", + variant: "destructive" + }); + } + }; + + const filteredRides = rides.filter(ride => + ride.name.toLowerCase().includes(searchQuery.toLowerCase()) || + ride.manufacturer?.name?.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + 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) => ( +
+ ))} +
+
+
+
+ ); + } + + if (!park) { + return null; + } + + return ( +
+
+ +
+ {/* Back Button & Page Header */} +
+ + +
+ +

{park.name}

+
+

+ Rides & Attractions +

+ +
+
+ + {filteredRides.length} rides + + + {rides.filter(r => r.category === 'roller_coaster').length} coasters + +
+ +
+ +
+
+
+ + {/* Search and Filters */} +
+
+
+ setSearchQuery(query)} + showRecentSearches={false} + /> +
+
+ + + + + +
+
+
+ + {/* Rides Grid */} + {filteredRides.length > 0 ? ( +
+ {filteredRides.map((ride) => ( + + ))} +
+ ) : ( +
+ +

No rides found

+

+ {rides.length === 0 + ? `${park.name} doesn't have any rides yet.` + : 'Try adjusting your search criteria or filters' + } +

+ {rides.length === 0 && user && ( + + )} +
+ )} + + {/* Create Modal */} + + + setIsCreateModalOpen(false)} + isEditing={false} + initialData={{ park_id: park.id }} + /> + + +
+
+ ); +}