diff --git a/src/pages/Parks.tsx b/src/pages/Parks.tsx index fda9b3fe..8c3c9484 100644 --- a/src/pages/Parks.tsx +++ b/src/pages/Parks.tsx @@ -1,16 +1,16 @@ -import { useState, useEffect, useMemo } from 'react'; -import { Header } from '@/components/layout/Header'; -import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { cn } from '@/lib/utils'; -import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible'; -import { Badge } from '@/components/ui/badge'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { - MapPin, - Grid3X3, - List, - Map, +import { useState, useEffect, useMemo } from "react"; +import { Header } from "@/components/layout/Header"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible"; +import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + MapPin, + Grid3X3, + List, + Map, Filter, SortAsc, Search, @@ -20,25 +20,25 @@ import { FerrisWheel, Plus, PanelLeftClose, - PanelLeftOpen -} 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'; -import { useAuthModal } from '@/hooks/useAuthModal'; -import { useOpenGraph } from '@/hooks/useOpenGraph'; -import { useParks } from '@/hooks/parks/useParks'; -import { Pagination } from '@/components/common/Pagination'; + PanelLeftOpen, +} 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"; +import { useAuthModal } from "@/hooks/useAuthModal"; +import { useOpenGraph } from "@/hooks/useOpenGraph"; +import { useParks } from "@/hooks/parks/useParks"; +import { Pagination } from "@/components/common/Pagination"; export interface FilterState { search: string; @@ -63,11 +63,11 @@ export interface FilterState { export interface SortState { field: string; - direction: 'asc' | 'desc'; + direction: "asc" | "desc"; } const initialFilters: FilterState = { - search: '', + search: "", parkType: [], status: [], country: [], @@ -88,19 +88,19 @@ const initialFilters: FilterState = { }; const initialSort: SortState = { - field: 'name', - direction: 'asc' + field: "name", + direction: "asc", }; export default function Parks() { const [filters, setFilters] = useState(initialFilters); const [sort, setSort] = useState(initialSort); - const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); + const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const [showFilters, setShowFilters] = useState(false); const [isAddParkModalOpen, setIsAddParkModalOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [sidebarCollapsed, setSidebarCollapsed] = useState(() => { - const saved = localStorage.getItem('parks-sidebar-collapsed'); + const saved = localStorage.getItem("parks-sidebar-collapsed"); return saved ? JSON.parse(saved) : false; }); const navigate = useNavigate(); @@ -113,7 +113,7 @@ export default function Parks() { const { data: parks = [], isLoading: loading, error } = useParks(); useEffect(() => { - localStorage.setItem('parks-sidebar-collapsed', JSON.stringify(sidebarCollapsed)); + localStorage.setItem("parks-sidebar-collapsed", JSON.stringify(sidebarCollapsed)); }, [sidebarCollapsed]); // Show error toast if query fails @@ -122,23 +122,23 @@ export default function Parks() { toast({ variant: "destructive", title: "Error loading parks", - description: error instanceof Error ? error.message : 'Failed to load parks', + description: error instanceof Error ? error.message : "Failed to load parks", }); } }, [error, toast]); const filteredAndSortedParks = useMemo(() => { - let filtered = parks.filter(park => { + let filtered = parks.filter((park) => { // Search filter if (filters.search) { const searchTerm = filters.search.toLowerCase(); - const matchesSearch = + 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; } @@ -153,7 +153,7 @@ export default function Parks() { } // Country filter - if (filters.country.length > 0 && !filters.country.includes(park.location?.country || '')) { + if (filters.country.length > 0 && !filters.country.includes(park.location?.country || "")) { return false; } @@ -215,7 +215,7 @@ export default function Parks() { // Opening year filter if (filters.openingYearStart || filters.openingYearEnd) { - const openingYear = park.opening_date ? parseInt(park.opening_date.split('-')[0]) : null; + const openingYear = park.opening_date ? parseInt(park.opening_date.split("-")[0]) : null; if (openingYear) { if (filters.openingYearStart && openingYear < filters.openingYearStart) return false; if (filters.openingYearEnd && openingYear > filters.openingYearEnd) return false; @@ -230,29 +230,29 @@ export default function Parks() { // Apply sorting filtered.sort((a, b) => { let aValue: any, bValue: any; - + switch (sort.field) { - case 'name': + case "name": aValue = a.name; bValue = b.name; break; - case 'rating': + case "rating": aValue = a.average_rating || 0; bValue = b.average_rating || 0; break; - case 'rides': + case "rides": aValue = a.ride_count || 0; bValue = b.ride_count || 0; break; - case 'coasters': + case "coasters": aValue = a.coaster_count || 0; bValue = b.coaster_count || 0; break; - case 'reviews': + case "reviews": aValue = a.review_count || 0; bValue = b.review_count || 0; break; - case 'opening': + 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; @@ -261,39 +261,39 @@ export default function Parks() { bValue = b.name; } - if (typeof aValue === 'string' && typeof bValue === 'string') { + if (typeof aValue === "string" && typeof bValue === "string") { const result = aValue.localeCompare(bValue); - return sort.direction === 'asc' ? result : -result; + return sort.direction === "asc" ? result : -result; } - + const result = aValue - bValue; - return sort.direction === 'asc' ? result : -result; + return sort.direction === "asc" ? result : -result; }); return filtered; }, [parks, filters, sort]); const generateDescription = () => { - if (!parks.length) return 'Browse theme parks worldwide on ThrillWiki'; - + if (!parks.length) return "Browse theme parks worldwide on ThrillWiki"; + const activeFilters = []; if (filters.country.length === 1) activeFilters.push(`in ${filters.country[0]}`); - if (filters.parkType.length > 0) activeFilters.push(filters.parkType.join(', ')); - if (filters.status.length > 0) activeFilters.push(filters.status.join(', ')); - + if (filters.parkType.length > 0) activeFilters.push(filters.parkType.join(", ")); + if (filters.status.length > 0) activeFilters.push(filters.status.join(", ")); + if (activeFilters.length > 0) { - return `Browse ${filteredAndSortedParks.length} ${activeFilters.join(' ')} theme parks`; + return `Browse ${filteredAndSortedParks.length} ${activeFilters.join(" ")} theme parks`; } - + return `Browse ${parks.length} theme parks worldwide`; }; useOpenGraph({ - title: 'Theme Parks - ThrillWiki', + title: "Theme Parks - ThrillWiki", description: generateDescription(), imageUrl: filteredAndSortedParks[0]?.banner_image_url, imageId: filteredAndSortedParks[0]?.banner_image_id, - type: 'website' + type: "website", }); const activeFilterCount = useMemo(() => { @@ -334,7 +334,7 @@ export default function Parks() { const handleParkSubmit = async (parkData: any) => { try { - const { submitParkCreation } = await import('@/lib/entitySubmissionHelpers'); + const { submitParkCreation } = await import("@/lib/entitySubmissionHelpers"); await submitParkCreation(parkData, user.id); toast({ @@ -347,7 +347,7 @@ export default function Parks() { toast({ title: "Submission Failed", description: error instanceof Error ? error.message : "Failed to submit park.", - variant: "destructive" + variant: "destructive", }); } }; @@ -378,46 +378,51 @@ export default function Parks() { return (
- +
{/* Page Header */}
-

Theme Parks

+

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 && ( - @@ -436,11 +441,7 @@ export default function Parks() { className="shrink-0 gap-2 hidden lg:flex" title={sidebarCollapsed ? "Show filters" : "Hide filters"} > - {sidebarCollapsed ? ( - - ) : ( - - )} + {sidebarCollapsed ? : } Filters {activeFilterCount > 0 && ( @@ -451,19 +452,13 @@ export default function Parks() { {/* Search bar takes remaining space */}
- setFilters(prev => ({ ...prev, search }))} - /> + setFilters((prev) => ({ ...prev, search }))} />
- + {/* Sort controls - more compact */}
- - + + {/* Mobile filter toggle */} - setViewMode(v as 'grid' | 'list')} className="hidden md:inline-flex"> + setViewMode(v as "grid" | "list")} + className="hidden md:inline-flex" + > @@ -499,11 +498,7 @@ export default function Parks() { - + @@ -520,21 +515,12 @@ export default function Parks() { Filters - - +
@@ -545,17 +531,12 @@ export default function Parks() {
{filteredAndSortedParks.length > 0 ? (
- {viewMode === 'grid' ? ( - + {viewMode === "grid" ? ( + ) : ( - + )} - +

No parks found

-

- Try adjusting your search terms or filters -

+

Try adjusting your search terms or filters

@@ -587,14 +566,10 @@ export default function Parks() { Add a new park to the database. Your submission will be reviewed before being published. - setIsAddParkModalOpen(false)} - isEditing={false} - /> + setIsAddParkModalOpen(false)} isEditing={false} />
); -} \ No newline at end of file +}