import { useState, useEffect, useMemo } from 'react'; import { Header } from '@/components/layout/Header'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { MapPin, Grid3X3, List, Map, Filter, SortAsc, Search, ChevronDown, Sliders, X, FerrisWheel, Plus } from 'lucide-react'; import { Park } from '@/types/database'; import { supabase } from '@/integrations/supabase/client'; import { useNavigate } from 'react-router-dom'; import { ParkFilters } from '@/components/parks/ParkFilters'; import { ParkGridView } from '@/components/parks/ParkGridView'; import { ParkListView } from '@/components/parks/ParkListView'; import { ParkSearch } from '@/components/parks/ParkSearch'; import { ParkSortOptions } from '@/components/parks/ParkSortOptions'; import { useToast } from '@/hooks/use-toast'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { ParkForm } from '@/components/admin/ParkForm'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; export interface FilterState { search: string; parkType: string; status: string; country: string; minRating: number; maxRating: number; minRides: number; maxRides: number; openingYearStart: number | null; openingYearEnd: number | null; } export interface SortState { field: string; direction: 'asc' | 'desc'; } const initialFilters: FilterState = { search: '', parkType: 'all', status: 'all', country: 'all', minRating: 0, maxRating: 5, minRides: 0, maxRides: 1000, openingYearStart: null, openingYearEnd: null, }; const initialSort: SortState = { field: 'name', direction: 'asc' }; export default function Parks() { const [parks, setParks] = useState([]); const [loading, setLoading] = useState(true); const [filters, setFilters] = useState(initialFilters); const [sort, setSort] = useState(initialSort); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [showFilters, setShowFilters] = useState(false); const [isAddParkModalOpen, setIsAddParkModalOpen] = useState(false); const navigate = useNavigate(); const { toast } = useToast(); const { user } = useAuth(); const { isModerator } = useUserRole(); useEffect(() => { fetchParks(); }, []); const fetchParks = async () => { try { setLoading(true); const { data, error } = await supabase .from('parks') .select(` *, location:locations(*), operator:companies!parks_operator_id_fkey(*), property_owner:companies!parks_property_owner_id_fkey(*) `) .order('name'); if (error) throw error; setParks(data || []); } catch (error: any) { console.error('Error fetching parks:', error); toast({ variant: "destructive", title: "Error loading parks", description: error.message, }); } finally { setLoading(false); } }; const filteredAndSortedParks = useMemo(() => { let filtered = parks.filter(park => { // Search filter if (filters.search) { const searchTerm = filters.search.toLowerCase(); const matchesSearch = park.name.toLowerCase().includes(searchTerm) || park.description?.toLowerCase().includes(searchTerm) || park.location?.city?.toLowerCase().includes(searchTerm) || park.location?.country?.toLowerCase().includes(searchTerm) || park.location?.state_province?.toLowerCase().includes(searchTerm); if (!matchesSearch) return false; } // Park type filter if (filters.parkType !== 'all' && park.park_type !== filters.parkType) { return false; } // Status filter if (filters.status !== 'all' && park.status !== filters.status) { return false; } // Country filter if (filters.country !== 'all' && park.location?.country !== filters.country) { return false; } // Rating filter const rating = park.average_rating || 0; if (rating < filters.minRating || rating > filters.maxRating) { return false; } // Ride count filter const rideCount = park.ride_count || 0; if (rideCount < filters.minRides || rideCount > filters.maxRides) { return false; } // Opening year filter if (filters.openingYearStart || filters.openingYearEnd) { const openingYear = park.opening_date ? new Date(park.opening_date).getFullYear() : null; if (openingYear) { if (filters.openingYearStart && openingYear < filters.openingYearStart) return false; if (filters.openingYearEnd && openingYear > filters.openingYearEnd) return false; } else if (filters.openingYearStart || filters.openingYearEnd) { return false; } } return true; }); // Apply sorting filtered.sort((a, b) => { let aValue: any, bValue: any; switch (sort.field) { case 'name': aValue = a.name; bValue = b.name; break; case 'rating': aValue = a.average_rating || 0; bValue = b.average_rating || 0; break; case 'rides': aValue = a.ride_count || 0; bValue = b.ride_count || 0; break; case 'coasters': aValue = a.coaster_count || 0; bValue = b.coaster_count || 0; break; case 'reviews': aValue = a.review_count || 0; bValue = b.review_count || 0; break; case 'opening': aValue = a.opening_date ? new Date(a.opening_date).getTime() : 0; bValue = b.opening_date ? new Date(b.opening_date).getTime() : 0; break; default: aValue = a.name; bValue = b.name; } if (typeof aValue === 'string' && typeof bValue === 'string') { const result = aValue.localeCompare(bValue); return sort.direction === 'asc' ? result : -result; } const result = aValue - bValue; return sort.direction === 'asc' ? result : -result; }); return filtered; }, [parks, filters, sort]); const activeFilterCount = useMemo(() => { let count = 0; if (filters.search) count++; if (filters.parkType !== 'all') count++; if (filters.status !== 'all') count++; if (filters.country !== 'all') count++; if (filters.minRating > 0 || filters.maxRating < 5) count++; if (filters.minRides > 0 || filters.maxRides < 1000) count++; if (filters.openingYearStart || filters.openingYearEnd) count++; return count; }, [filters]); const clearAllFilters = () => { setFilters(initialFilters); setSort(initialSort); }; const handleParkClick = (park: Park) => { navigate(`/parks/${park.slug}`); }; const handleAddParkClick = () => { if (!user) { navigate('/auth'); return; } setIsAddParkModalOpen(true); }; const handleParkSubmit = async (parkData: any) => { if (!user) { navigate('/auth'); return; } try { // All users submit for moderation const { error } = await supabase .from('content_submissions') .insert({ user_id: user.id, submission_type: 'park', status: 'pending', content: parkData }); if (error) throw error; toast({ title: "Park Submitted", description: "Your park submission has been sent for moderation review.", }); setIsAddParkModalOpen(false); } catch (error: any) { toast({ title: "Submission Failed", description: error.message || "Failed to submit park.", variant: "destructive" }); } }; if (loading) { return (
{[...Array(12)].map((_, i) => (
))}
); } return (
{/* Page Header */}

Theme Parks

Discover amazing theme parks, amusement parks, and attractions worldwide

{filteredAndSortedParks.length} parks {parks.reduce((sum, park) => sum + (park.ride_count || 0), 0)} total rides {parks.reduce((sum, park) => sum + (park.coaster_count || 0), 0)} coasters
{activeFilterCount > 0 && ( )}
{/* Search and Controls */}
setFilters(prev => ({ ...prev, search }))} />
setViewMode(v as any)} className="flex-1 sm:flex-none hidden md:inline-flex">
{/* Advanced Filters Panel */} {showFilters && ( )}
{/* Results */} {filteredAndSortedParks.length > 0 ? (
{viewMode === 'grid' ? ( ) : ( )}
) : (

No parks found

Try adjusting your search terms or filters

)} {/* Add Park Modal */} Add New Park Add a new park to the database. Your submission will be reviewed before being published. setIsAddParkModalOpen(false)} isEditing={false} />
); }