import React, { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { Header } from '@/components/layout/Header'; import { Input } from '@/components/ui/input'; 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 } from '@/components/ui/collapsible'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Search, Filter, Building, Plus, ChevronDown, PanelLeftClose, PanelLeftOpen, Grid3X3, List } from 'lucide-react'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { cn } from '@/lib/utils'; import { supabase } from '@/lib/supabaseClient'; import OperatorCard from '@/components/operators/OperatorCard'; import { OperatorListView } from '@/components/operators/OperatorListView'; import { OperatorForm } from '@/components/admin/OperatorForm'; import { OperatorFilters, OperatorFilterState, defaultOperatorFilters } from '@/components/operators/OperatorFilters'; import { Company } from '@/types/database'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; import { toast } from '@/hooks/use-toast'; import { submitCompanyCreation } from '@/lib/companyHelpers'; import { useAuthModal } from '@/hooks/useAuthModal'; import { getErrorMessage } from '@/lib/errorHandler'; import { useDocumentTitle } from '@/hooks/useDocumentTitle'; import { useOpenGraph } from '@/hooks/useOpenGraph'; import { SubmissionErrorBoundary } from '@/components/error/SubmissionErrorBoundary'; const Operators = () => { useDocumentTitle('Operators'); const navigate = useNavigate(); const { user } = useAuth(); const { isModerator } = useUserRole(); const { requireAuth } = useAuthModal(); const [searchTerm, setSearchTerm] = useState(''); const [sortBy, setSortBy] = useState('name'); const [filters, setFilters] = useState(defaultOperatorFilters); const [showFilters, setShowFilters] = useState(false); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(() => { const saved = localStorage.getItem('operators-sidebar-collapsed'); return saved ? JSON.parse(saved) : false; }); useEffect(() => { localStorage.setItem('operators-sidebar-collapsed', JSON.stringify(sidebarCollapsed)); }, [sidebarCollapsed]); const { data: operators, isLoading } = useQuery({ queryKey: ['operators'], queryFn: async () => { // Get companies that are park operators with park counts const { data, error } = await supabase .from('companies') .select(` *, parks:parks!operator_id(count) `) .in('id', await supabase .from('parks') .select('operator_id') .not('operator_id', 'is', null) .then(({ data }) => data?.map(park => park.operator_id).filter((id): id is string => id !== null) || []) ) .order('name'); if (error) throw error; // Transform the data to include park_count const transformedData = data?.map(company => ({ ...company, park_count: company.parks?.[0]?.count || 0 })) || []; return transformedData as (Company & { park_count: number })[]; }, }); const handleCreateSubmit = async (data: any) => { try { await submitCompanyCreation( data, 'operator', user!.id ); toast({ title: "Operator Submitted", description: "Your submission has been sent for review." }); setIsCreateModalOpen(false); } catch (error) { const errorMsg = getErrorMessage(error); toast({ title: "Error", description: errorMsg, variant: "destructive" }); } }; const filteredAndSortedOperators = React.useMemo(() => { if (!operators) return []; let filtered = operators.filter(operator => { // Search filter const matchesSearch = operator.name.toLowerCase().includes(searchTerm.toLowerCase()) || (operator.description && operator.description.toLowerCase().includes(searchTerm.toLowerCase())); if (!matchesSearch) return false; // Country filter if (filters.countries.length > 0) { if (!operator.headquarters_location || !filters.countries.some(c => operator.headquarters_location?.includes(c))) { return false; } } // Rating filter const rating = operator.average_rating || 0; if (rating < filters.minRating || rating > filters.maxRating) { return false; } // Review count filter const reviewCount = operator.review_count || 0; if (reviewCount < filters.minReviewCount || reviewCount > filters.maxReviewCount) { return false; } // Park count filter const parkCount = operator.park_count || 0; if (parkCount < filters.minParkCount || parkCount > filters.maxParkCount) { return false; } // Founded date filter if (filters.foundedDateFrom || filters.foundedDateTo) { if (!operator.founded_year) { return false; } if (filters.foundedDateFrom && operator.founded_year < filters.foundedDateFrom.getFullYear()) { return false; } if (filters.foundedDateTo && operator.founded_year > filters.foundedDateTo.getFullYear()) { return false; } } // Has rating filter if (filters.hasRating && !operator.average_rating) { return false; } // Has founded date filter if (filters.hasFoundedDate && !operator.founded_year) { return false; } return true; }); // Sort 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 'founded': return (b.founded_year || 0) - (a.founded_year || 0); case 'reviews': return (b.review_count || 0) - (a.review_count || 0); default: return 0; } }); return filtered; }, [operators, searchTerm, sortBy, filters]); useOpenGraph({ title: 'Park Operators - ThrillWiki', description: `Browse ${filteredAndSortedOperators.length} theme park operators worldwide`, imageUrl: filteredAndSortedOperators[0]?.banner_image_url ?? undefined, imageId: filteredAndSortedOperators[0]?.banner_image_id ?? undefined, type: 'website', enabled: !isLoading }); return (
{/* Page Header */}

Park Operators

Discover companies that operate theme parks

{filteredAndSortedOperators?.length || 0} operators {searchTerm && ( Searching: "{searchTerm}" )}
{/* Search and Filters */}
{/* Desktop: Filter toggle on the left */} {/* Search bar takes remaining space */}
setSearchTerm(e.target.value)} className="pl-10 h-11" />
{/* 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 */}
{/* Loading State */} {isLoading && (

Loading park operators...

)} {/* Operators Grid/List */} {!isLoading && filteredAndSortedOperators && filteredAndSortedOperators.length > 0 && ( viewMode === 'grid' ? (
{filteredAndSortedOperators.map((operator) => ( ))}
) : ( navigate(`/operators/${op.slug}`)} /> ) )} {/* Empty State */} {!isLoading && filteredAndSortedOperators?.length === 0 && (

No park operators found

{searchTerm ? "Try adjusting your search terms or filters" : "No park operators are currently available" }

)}
{/* Create Modal */} setIsCreateModalOpen(false)} />
); }; export default Operators;