diff --git a/src/App.tsx b/src/App.tsx
index 45348a62..5f3e77b1 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,6 +14,7 @@ import RideDetail from "./pages/RideDetail";
import Rides from "./pages/Rides";
import Manufacturers from "./pages/Manufacturers";
import Designers from "./pages/Designers";
+import ParkOwners from "./pages/ParkOwners";
import Auth from "./pages/Auth";
import Profile from "./pages/Profile";
import UserSettings from "./pages/UserSettings";
@@ -44,6 +45,7 @@ function AppContent() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/src/components/park-owners/ParkOwnerCard.tsx b/src/components/park-owners/ParkOwnerCard.tsx
new file mode 100644
index 00000000..cd4db822
--- /dev/null
+++ b/src/components/park-owners/ParkOwnerCard.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import { Building2, Star, MapPin } from 'lucide-react';
+import { Company } from '@/types/database';
+
+interface ParkOwnerCardProps {
+ company: Company;
+}
+
+const ParkOwnerCard = ({ company }: ParkOwnerCardProps) => {
+ const navigate = useNavigate();
+
+ const handleClick = () => {
+ navigate(`/owners/${company.slug}/parks/`);
+ };
+
+ const getCompanyIcon = () => {
+ return ;
+ };
+
+ return (
+
+
+
+
+
+ {company.logo_url ? (
+

+ ) : (
+ getCompanyIcon()
+ )}
+
+
+
+ {company.name}
+
+
+
+ Property Owner
+
+ {company.founded_year && (
+
+ Est. {company.founded_year}
+
+ )}
+
+
+
+
+
+
+
+ {company.description && (
+
+ {company.description}
+
+ )}
+
+
+ {company.headquarters_location && (
+
+
+ {company.headquarters_location}
+
+ )}
+
+
+
+
+ {company.average_rating > 0
+ ? `${company.average_rating.toFixed(1)} rating`
+ : 'No ratings yet'
+ }
+ {company.review_count > 0 && (
+
+ ({company.review_count} review{company.review_count !== 1 ? 's' : ''})
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default ParkOwnerCard;
\ No newline at end of file
diff --git a/src/pages/ParkOwners.tsx b/src/pages/ParkOwners.tsx
new file mode 100644
index 00000000..90f598de
--- /dev/null
+++ b/src/pages/ParkOwners.tsx
@@ -0,0 +1,175 @@
+import React, { useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
+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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
+import { Search, Filter, Building2 } from 'lucide-react';
+import { supabase } from '@/integrations/supabase/client';
+import ParkOwnerCard from '@/components/park-owners/ParkOwnerCard';
+import { Company } from '@/types/database';
+
+const ParkOwners = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [sortBy, setSortBy] = useState('name');
+ const [filterBy, setFilterBy] = useState('all');
+
+ const { data: parkOwners, isLoading } = useQuery({
+ queryKey: ['park-owners'],
+ queryFn: async () => {
+ // Get companies that are property owners
+ const { data, error } = await supabase
+ .from('companies')
+ .select('*')
+ .in('id',
+ await supabase
+ .from('parks')
+ .select('property_owner_id')
+ .not('property_owner_id', 'is', null)
+ .then(({ data }) => data?.map(park => park.property_owner_id) || [])
+ )
+ .order('name');
+
+ if (error) throw error;
+ return data as Company[];
+ },
+ });
+
+ const filteredAndSortedOwners = React.useMemo(() => {
+ if (!parkOwners) return [];
+
+ let filtered = parkOwners.filter(owner => {
+ const matchesSearch = owner.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ (owner.description && owner.description.toLowerCase().includes(searchTerm.toLowerCase()));
+
+ if (filterBy === 'all') return matchesSearch;
+ if (filterBy === 'with-rating') return matchesSearch && owner.average_rating > 0;
+ if (filterBy === 'established') return matchesSearch && owner.founded_year;
+
+ return matchesSearch;
+ });
+
+ // 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;
+ }, [parkOwners, searchTerm, sortBy, filterBy]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
Property Owners
+
+ Discover companies that own and manage theme parks
+
+
+
+
+ {/* Search and Filters */}
+
+
+
+ setSearchTerm(e.target.value)}
+ className="pl-10"
+ />
+
+
+
+
+
+
+
+ {/* Results Count */}
+
+
+
+ {filteredAndSortedOwners?.length || 0} property owners
+
+ {searchTerm && (
+
+ Searching: "{searchTerm}"
+
+ )}
+
+
+
+ {/* Loading State */}
+ {isLoading && (
+
+
+
Loading property owners...
+
+ )}
+
+ {/* Property Owners Grid */}
+ {!isLoading && filteredAndSortedOwners && (
+
+ {filteredAndSortedOwners.map((owner) => (
+
+ ))}
+
+ )}
+
+ {/* Empty State */}
+ {!isLoading && filteredAndSortedOwners?.length === 0 && (
+
+
+
No property owners found
+
+ {searchTerm
+ ? "Try adjusting your search terms or filters"
+ : "No property owners are currently available"
+ }
+
+
+ )}
+
+
+ );
+};
+
+export default ParkOwners;
\ No newline at end of file