mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 01:11:13 -05:00
Refactor: Improve mobile header and navigation
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Search, Menu, Zap, MapPin, Star, ChevronDown, Building, Users, Crown, Palette, Shield, FerrisWheel, Factory } from 'lucide-react';
|
import { Search, Menu, Sparkles, MapPin, Star, ChevronDown, Building, Users, Crown, Palette, Shield, FerrisWheel, Factory } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
||||||
@@ -7,137 +7,240 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { SearchDropdown } from '@/components/search/SearchDropdown';
|
import { SearchDropdown } from '@/components/search/SearchDropdown';
|
||||||
|
import { MobileSearch } from '@/components/search/MobileSearch';
|
||||||
import { AuthButtons } from '@/components/auth/AuthButtons';
|
import { AuthButtons } from '@/components/auth/AuthButtons';
|
||||||
import { ThemeToggle } from '@/components/theme/ThemeToggle';
|
import { ThemeToggle } from '@/components/theme/ThemeToggle';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
|
import {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
} from "@/components/ui/navigation-menu";
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
return <header className="sticky top-0 z-50 w-full border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
const isMobile = useIsMobile();
|
||||||
<div className="container flex h-16 items-center justify-between px-4">
|
|
||||||
{/* Logo and Brand */}
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<Link to="/" className="flex items-center">
|
|
||||||
<h1 className="text-xl font-bold bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent">
|
|
||||||
ThrillWiki
|
|
||||||
</h1>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Desktop Navigation */}
|
return (
|
||||||
<nav className="hidden md:flex items-center gap-6 ml-8">
|
<>
|
||||||
<DropdownMenu>
|
<header className="sticky top-0 z-40 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||||
<DropdownMenuTrigger asChild>
|
<div className="container flex h-14 md:h-16 items-center justify-between px-3 md:px-4">
|
||||||
<Button variant="ghost" className="text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors">
|
{/* Mobile: Menu + Logo */}
|
||||||
<Search className="w-4 h-4 mr-2" />
|
<div className="flex items-center gap-2 md:gap-6 flex-1 md:flex-initial">
|
||||||
Explore
|
{/* Mobile Menu */}
|
||||||
<ChevronDown className="w-4 h-4 ml-1" />
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" className="md:hidden h-9 w-9">
|
||||||
|
<Menu className="h-5 w-5" />
|
||||||
|
<span className="sr-only">Toggle menu</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</SheetTrigger>
|
||||||
<DropdownMenuContent align="start" className="w-48 bg-background border border-border shadow-lg">
|
<SheetContent side="left" className="w-[280px] sm:w-[320px]">
|
||||||
<DropdownMenuItem onClick={() => navigate('/parks')} className="hover:!bg-accent hover:!text-accent-foreground">
|
<nav className="flex flex-col gap-1 mt-8">
|
||||||
<MapPin className="w-4 h-4 mr-2" />
|
<div className="mb-4">
|
||||||
Parks
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2 px-3">
|
||||||
</DropdownMenuItem>
|
Explore
|
||||||
<DropdownMenuItem onClick={() => navigate('/rides')} className="hover:!bg-accent hover:!text-accent-foreground">
|
</h3>
|
||||||
<FerrisWheel className="w-4 h-4 mr-2" />
|
|
||||||
Rides
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => navigate('/manufacturers')} className="hover:!bg-accent hover:!text-accent-foreground">
|
|
||||||
<Factory className="w-4 h-4 mr-2" />
|
|
||||||
Manufacturers
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => navigate('/operators')} className="hover:!bg-accent hover:!text-accent-foreground">
|
|
||||||
<Building className="w-4 h-4 mr-2" />
|
|
||||||
Operators
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => navigate('/owners')} className="hover:!bg-accent hover:!text-accent-foreground">
|
|
||||||
<Crown className="w-4 h-4 mr-2" />
|
|
||||||
Owners
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => navigate('/designers')} className="hover:!bg-accent hover:!text-accent-foreground">
|
|
||||||
<Palette className="w-4 h-4 mr-2" />
|
|
||||||
Designers
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Bar */}
|
|
||||||
<div className="flex-1 max-w-xl mx-4">
|
|
||||||
<SearchDropdown />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* User Actions */}
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{isModerator() && (
|
|
||||||
<Button variant="ghost" size="sm" asChild>
|
|
||||||
<Link to="/admin" className="flex items-center gap-2">
|
|
||||||
<Shield className="w-4 h-4" />
|
|
||||||
Admin
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<ThemeToggle />
|
|
||||||
<AuthButtons />
|
|
||||||
|
|
||||||
{/* Mobile Menu */}
|
|
||||||
<Sheet>
|
|
||||||
<SheetTrigger asChild>
|
|
||||||
<Button variant="ghost" size="sm" className="md:hidden">
|
|
||||||
<Menu className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</SheetTrigger>
|
|
||||||
<SheetContent side="right" className="w-[300px] sm:w-[400px]">
|
|
||||||
<div className="flex flex-col gap-4 mt-8">
|
|
||||||
<div className="text-sm font-semibold text-muted-foreground mb-2">Explore</div>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/parks')}>
|
|
||||||
<MapPin className="w-4 h-4 mr-2" />
|
|
||||||
Parks
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/rides')}>
|
|
||||||
<FerrisWheel className="w-4 h-4 mr-2" />
|
|
||||||
Rides
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/manufacturers')}>
|
|
||||||
<Factory className="w-4 h-4 mr-2" />
|
|
||||||
Manufacturers
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/operators')}>
|
|
||||||
<Building className="w-4 h-4 mr-2" />
|
|
||||||
Operators
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/owners')}>
|
|
||||||
<Crown className="w-4 h-4 mr-2" />
|
|
||||||
Owners
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" className="justify-start" onClick={() => navigate('/designers')}>
|
|
||||||
<Palette className="w-4 h-4 mr-2" />
|
|
||||||
Designers
|
|
||||||
</Button>
|
|
||||||
<div className="border-t pt-4 mt-4">
|
|
||||||
<Button variant="ghost" className="justify-start">
|
|
||||||
<Star className="w-4 h-4 mr-2" />
|
|
||||||
Reviews
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="border-t pt-4 mt-4">
|
|
||||||
<div className="mb-3">
|
|
||||||
<ThemeToggle />
|
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" className="w-full mb-2">
|
<Link
|
||||||
Sign In
|
to="/parks"
|
||||||
</Button>
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
<Button className="w-full bg-gradient-to-r from-primary to-accent">
|
onClick={() => setOpen(false)}
|
||||||
Join ThrillWiki
|
>
|
||||||
</Button>
|
Parks
|
||||||
</div>
|
</Link>
|
||||||
</div>
|
<Link
|
||||||
</SheetContent>
|
to="/rides"
|
||||||
</Sheet>
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Rides
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/manufacturers"
|
||||||
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Manufacturers
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/designers"
|
||||||
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Designers
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/operators"
|
||||||
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Operators
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/owners"
|
||||||
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Property Owners
|
||||||
|
</Link>
|
||||||
|
{isModerator() && (
|
||||||
|
<>
|
||||||
|
<div className="my-2 border-t border-border" />
|
||||||
|
<Link
|
||||||
|
to="/admin"
|
||||||
|
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Admin
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
|
||||||
|
{/* Logo */}
|
||||||
|
<Link to="/" className="flex items-center space-x-2 hover:opacity-80 transition-opacity">
|
||||||
|
<Sparkles className="h-5 w-5 md:h-6 md:w-6 text-primary" />
|
||||||
|
<span className="font-bold text-lg md:text-xl bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent">
|
||||||
|
ThrillWiki
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Desktop Navigation */}
|
||||||
|
<nav className="hidden md:flex items-center space-x-1">
|
||||||
|
<NavigationMenu>
|
||||||
|
<NavigationMenuList>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuTrigger className="h-9">Explore</NavigationMenuTrigger>
|
||||||
|
<NavigationMenuContent>
|
||||||
|
<ul className="grid w-[400px] gap-3 p-4">
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/parks"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Parks</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
Browse theme parks around the world
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/rides"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Rides</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
Discover exciting rides and attractions
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/manufacturers"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Manufacturers</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
Explore ride manufacturers
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/designers"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Designers</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
View ride designers
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/operators"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Operators</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
Find park operators
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<Link
|
||||||
|
to="/owners"
|
||||||
|
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">Property Owners</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
View property owners
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</NavigationMenuContent>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
</NavigationMenuList>
|
||||||
|
</NavigationMenu>
|
||||||
|
|
||||||
|
{isModerator() && (
|
||||||
|
<Button variant="ghost" size="sm" className="h-9" asChild>
|
||||||
|
<Link to="/admin">Admin</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side: Search, Theme, Auth */}
|
||||||
|
<div className="flex items-center gap-1 md:gap-2">
|
||||||
|
{/* Desktop Search */}
|
||||||
|
<div className="hidden lg:block w-80 xl:w-96">
|
||||||
|
<SearchDropdown />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Search Button */}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="lg:hidden h-9 w-9"
|
||||||
|
onClick={() => setMobileSearchOpen(true)}
|
||||||
|
>
|
||||||
|
<Search className="h-5 w-5" />
|
||||||
|
<span className="sr-only">Search</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<ThemeToggle />
|
||||||
|
<AuthButtons />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
</header>;
|
|
||||||
|
{/* Mobile Search Modal */}
|
||||||
|
<MobileSearch open={mobileSearchOpen} onOpenChange={setMobileSearchOpen} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="group overflow-hidden border-border/50 bg-gradient-to-br from-card via-card to-card/80 hover:shadow-2xl hover:shadow-primary/20 transition-all duration-300 cursor-pointer hover:scale-[1.02]"
|
className="group overflow-hidden border-border/50 bg-gradient-to-br from-card via-card to-card/80 hover:shadow-xl hover:shadow-primary/10 transition-all duration-300 cursor-pointer active:scale-[0.98] md:hover:scale-[1.02]"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
{/* Logo/Image Section */}
|
{/* Logo/Image Section */}
|
||||||
@@ -43,8 +43,8 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-t from-background/80 via-transparent to-transparent" />
|
<div className="absolute inset-0 bg-gradient-to-t from-background/80 via-transparent to-transparent" />
|
||||||
|
|
||||||
{/* Company Type Badge */}
|
{/* Company Type Badge */}
|
||||||
<div className="absolute top-3 right-3 z-10">
|
<div className="absolute top-2 right-2 md:top-3 md:right-3 z-10">
|
||||||
<Badge variant="outline" className="bg-background/80 backdrop-blur-sm">
|
<Badge variant="outline" className="bg-background/80 backdrop-blur-sm text-xs">
|
||||||
{formatCompanyType(company.company_type)}
|
{formatCompanyType(company.company_type)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,7 +52,7 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
{/* Logo Display */}
|
{/* Logo Display */}
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
{company.logo_url ? (
|
{company.logo_url ? (
|
||||||
<div className="w-20 h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
<div className="w-16 h-16 md:w-20 md:h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
||||||
<img
|
<img
|
||||||
src={company.logo_url}
|
src={company.logo_url}
|
||||||
alt={`${company.name} logo`}
|
alt={`${company.name} logo`}
|
||||||
@@ -60,28 +60,28 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 bg-background/90 rounded-xl shadow-lg backdrop-blur-sm border border-border/50 flex items-center justify-center">
|
<div className="w-16 h-16 md:w-20 md:h-20 bg-background/90 rounded-xl shadow-lg backdrop-blur-sm border border-border/50 flex items-center justify-center">
|
||||||
{getCompanyIcon(company.company_type)}
|
{getCompanyIcon(company.company_type)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CardContent className="p-4 space-y-3">
|
<CardContent className="p-3 md:p-4 space-y-2 md:space-y-3">
|
||||||
{/* Company Name */}
|
{/* Company Name */}
|
||||||
<h3 className="text-lg font-semibold group-hover:text-primary transition-colors line-clamp-2">
|
<h3 className="text-base md:text-lg font-semibold group-hover:text-primary transition-colors line-clamp-2 min-h-[2.5rem] md:min-h-[3rem]">
|
||||||
{company.name}
|
{company.name}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{company.description && (
|
{company.description && (
|
||||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
<p className="text-xs md:text-sm text-muted-foreground line-clamp-2">
|
||||||
{company.description}
|
{company.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Company Info */}
|
{/* Company Info */}
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-sm">
|
<div className="flex flex-wrap gap-x-3 md:gap-x-4 gap-y-1 text-xs md:text-sm">
|
||||||
{company.founded_year && (
|
{company.founded_year && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="text-muted-foreground">Founded:</span>
|
<span className="text-muted-foreground">Founded:</span>
|
||||||
@@ -90,8 +90,8 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{company.headquarters_location && (
|
{company.headquarters_location && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1 min-w-0">
|
||||||
<MapPin className="w-3 h-3 text-muted-foreground" />
|
<MapPin className="w-3 h-3 text-muted-foreground shrink-0" />
|
||||||
<span className="text-muted-foreground truncate">
|
<span className="text-muted-foreground truncate">
|
||||||
{company.headquarters_location}
|
{company.headquarters_location}
|
||||||
</span>
|
</span>
|
||||||
@@ -102,16 +102,16 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
{/* Rating */}
|
{/* Rating */}
|
||||||
{company.average_rating > 0 && (
|
{company.average_rating > 0 && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Star className="w-4 h-4 fill-yellow-400 text-yellow-400" />
|
<Star className="w-3.5 h-3.5 md:w-4 md:h-4 fill-yellow-400 text-yellow-400" />
|
||||||
<span className="text-sm font-medium">{company.average_rating.toFixed(1)}</span>
|
<span className="text-xs md:text-sm font-medium">{company.average_rating.toFixed(1)}</span>
|
||||||
{company.review_count > 0 && (
|
{company.review_count > 0 && (
|
||||||
<span className="text-xs text-muted-foreground">({company.review_count} reviews)</span>
|
<span className="text-xs text-muted-foreground">({company.review_count})</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Stats Display */}
|
{/* Stats Display */}
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-sm">
|
<div className="flex flex-wrap gap-x-3 md:gap-x-4 gap-y-1 text-xs md:text-sm">
|
||||||
{(company as any).ride_count > 0 && (
|
{(company as any).ride_count > 0 && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<FerrisWheel className="w-3 h-3 text-muted-foreground" />
|
<FerrisWheel className="w-3 h-3 text-muted-foreground" />
|
||||||
|
|||||||
69
src/components/search/MobileSearch.tsx
Normal file
69
src/components/search/MobileSearch.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Search, X } from 'lucide-react';
|
||||||
|
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { SearchResults } from './SearchResults';
|
||||||
|
|
||||||
|
interface MobileSearchProps {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MobileSearch({ open, onOpenChange }: MobileSearchProps) {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setQuery('');
|
||||||
|
onOpenChange(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="h-screen max-w-full p-0 gap-0 rounded-none">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
{/* Search Header */}
|
||||||
|
<div className="sticky top-0 z-10 bg-background border-b border-border p-4 space-y-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={handleClose}
|
||||||
|
className="shrink-0"
|
||||||
|
>
|
||||||
|
<X className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
<div className="relative flex-1">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
placeholder="Search parks, rides, manufacturers..."
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
className="pl-10 h-11 text-base"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Search Results */}
|
||||||
|
<div className="flex-1 overflow-auto">
|
||||||
|
{query.length >= 1 ? (
|
||||||
|
<SearchResults query={query} onClose={handleClose} />
|
||||||
|
) : (
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<Search className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||||
|
<h3 className="text-lg font-semibold mb-2">Start searching</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Find parks, rides, and manufacturers
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
115
src/components/ui/bottom-sheet.tsx
Normal file
115
src/components/ui/bottom-sheet.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const BottomSheet = ({
|
||||||
|
shouldScaleBackground = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||||
|
<DrawerPrimitive.Root
|
||||||
|
shouldScaleBackground={shouldScaleBackground}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
BottomSheet.displayName = "BottomSheet"
|
||||||
|
|
||||||
|
const BottomSheetTrigger = DrawerPrimitive.Trigger
|
||||||
|
|
||||||
|
const BottomSheetPortal = DrawerPrimitive.Portal
|
||||||
|
|
||||||
|
const BottomSheetClose = DrawerPrimitive.Close
|
||||||
|
|
||||||
|
const BottomSheetOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DrawerPrimitive.Overlay
|
||||||
|
ref={ref}
|
||||||
|
className={cn("fixed inset-0 z-50 bg-background/80 backdrop-blur-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BottomSheetOverlay.displayName = DrawerPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const BottomSheetContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<BottomSheetPortal>
|
||||||
|
<BottomSheetOverlay />
|
||||||
|
<DrawerPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border border-border bg-card",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||||
|
{children}
|
||||||
|
</DrawerPrimitive.Content>
|
||||||
|
</BottomSheetPortal>
|
||||||
|
))
|
||||||
|
BottomSheetContent.displayName = "BottomSheetContent"
|
||||||
|
|
||||||
|
const BottomSheetHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
BottomSheetHeader.displayName = "BottomSheetHeader"
|
||||||
|
|
||||||
|
const BottomSheetFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
BottomSheetFooter.displayName = "BottomSheetFooter"
|
||||||
|
|
||||||
|
const BottomSheetTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DrawerPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-lg font-semibold leading-none tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BottomSheetTitle.displayName = DrawerPrimitive.Title.displayName
|
||||||
|
|
||||||
|
const BottomSheetDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DrawerPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BottomSheetDescription.displayName = DrawerPrimitive.Description.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
BottomSheet,
|
||||||
|
BottomSheetPortal,
|
||||||
|
BottomSheetOverlay,
|
||||||
|
BottomSheetTrigger,
|
||||||
|
BottomSheetClose,
|
||||||
|
BottomSheetContent,
|
||||||
|
BottomSheetHeader,
|
||||||
|
BottomSheetFooter,
|
||||||
|
BottomSheetTitle,
|
||||||
|
BottomSheetDescription,
|
||||||
|
}
|
||||||
@@ -126,35 +126,32 @@ export default function Designers() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search and Filters */}
|
{/* Search and Filters */}
|
||||||
<div className="mb-8 space-y-4">
|
<div className="space-y-3 mb-6">
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
<div className="relative">
|
||||||
<div className="relative flex-1">
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
<Input
|
||||||
<Input
|
placeholder="Search designers..."
|
||||||
placeholder="Search designers by name, location, or description..."
|
value={searchQuery}
|
||||||
value={searchQuery}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
className="pl-10 h-11"
|
||||||
className="pl-10"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Select value={sortBy} onValueChange={setSortBy}>
|
|
||||||
<SelectTrigger className="w-[180px]">
|
|
||||||
<SlidersHorizontal className="w-4 h-4 mr-2" />
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="name">Name A-Z</SelectItem>
|
|
||||||
<SelectItem value="founded">Founded (Newest)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Select value={sortBy} onValueChange={setSortBy}>
|
||||||
|
<SelectTrigger className="w-full h-10">
|
||||||
|
<SlidersHorizontal className="h-4 w-4 mr-2" />
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="name">Name A-Z</SelectItem>
|
||||||
|
<SelectItem value="founded">Founded (Newest)</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Companies Grid */}
|
{/* Companies Grid */}
|
||||||
{filteredCompanies.length > 0 ? (
|
{filteredCompanies.length > 0 ? (
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
||||||
{filteredCompanies.map((company) => (
|
{filteredCompanies.map((company) => (
|
||||||
<DesignerCard key={company.id} company={company} />
|
<DesignerCard key={company.id} company={company} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -146,36 +146,32 @@ export default function Manufacturers() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search and Filters */}
|
{/* Search and Filters */}
|
||||||
<div className="mb-8 space-y-4">
|
<div className="space-y-3 mb-6">
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
<div className="relative">
|
||||||
<div className="relative flex-1">
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
<Input
|
||||||
<Input
|
placeholder="Search manufacturers..."
|
||||||
placeholder="Search manufacturers by name, location, or description..."
|
value={searchQuery}
|
||||||
value={searchQuery}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
className="pl-10 h-11"
|
||||||
className="pl-10"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Select value={sortBy} onValueChange={setSortBy}>
|
|
||||||
<SelectTrigger className="w-[180px]">
|
|
||||||
<SlidersHorizontal className="w-4 h-4 mr-2" />
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="name">Name A-Z</SelectItem>
|
|
||||||
<SelectItem value="founded">Founded (Newest)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Select value={sortBy} onValueChange={setSortBy}>
|
||||||
|
<SelectTrigger className="w-full h-10">
|
||||||
|
<SlidersHorizontal className="h-4 w-4 mr-2" />
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="name">Name A-Z</SelectItem>
|
||||||
|
<SelectItem value="founded">Founded (Newest)</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Companies Grid */}
|
{/* Companies Grid */}
|
||||||
{filteredCompanies.length > 0 ? (
|
{filteredCompanies.length > 0 ? (
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
||||||
{filteredCompanies.map((company) => (
|
{filteredCompanies.map((company) => (
|
||||||
<ManufacturerCard key={company.id} company={company} />
|
<ManufacturerCard key={company.id} company={company} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -97,40 +97,42 @@ const Operators = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search and Filters */}
|
{/* Search and Filters */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
<div className="space-y-3 mb-6">
|
||||||
<div className="relative flex-1">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
||||||
<Input
|
<Input
|
||||||
placeholder="Search park operators..."
|
placeholder="Search park operators..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="pl-10"
|
className="pl-10 h-11"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select value={sortBy} onValueChange={setSortBy}>
|
<div className="flex gap-2">
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<Select value={sortBy} onValueChange={setSortBy}>
|
||||||
<SelectValue placeholder="Sort by" />
|
<SelectTrigger className="flex-1 h-10">
|
||||||
</SelectTrigger>
|
<SelectValue placeholder="Sort by" />
|
||||||
<SelectContent>
|
</SelectTrigger>
|
||||||
<SelectItem value="name">Name</SelectItem>
|
<SelectContent>
|
||||||
<SelectItem value="rating">Rating</SelectItem>
|
<SelectItem value="name">Name</SelectItem>
|
||||||
<SelectItem value="founded">Founded Year</SelectItem>
|
<SelectItem value="rating">Rating</SelectItem>
|
||||||
<SelectItem value="reviews">Review Count</SelectItem>
|
<SelectItem value="founded">Founded Year</SelectItem>
|
||||||
</SelectContent>
|
<SelectItem value="reviews">Review Count</SelectItem>
|
||||||
</Select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<Select value={filterBy} onValueChange={setFilterBy}>
|
<Select value={filterBy} onValueChange={setFilterBy}>
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<SelectTrigger className="flex-1 h-10">
|
||||||
<Filter className="h-4 w-4 mr-2" />
|
<Filter className="h-4 w-4 mr-2" />
|
||||||
<SelectValue placeholder="Filter" />
|
<SelectValue placeholder="Filter" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all">All Operators</SelectItem>
|
<SelectItem value="all">All</SelectItem>
|
||||||
<SelectItem value="with-rating">With Ratings</SelectItem>
|
<SelectItem value="with-rating">Rated</SelectItem>
|
||||||
<SelectItem value="established">Established</SelectItem>
|
<SelectItem value="established">Est.</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Results Count */}
|
{/* Results Count */}
|
||||||
@@ -157,7 +159,7 @@ const Operators = () => {
|
|||||||
|
|
||||||
{/* Operators Grid */}
|
{/* Operators Grid */}
|
||||||
{!isLoading && filteredAndSortedOperators && (
|
{!isLoading && filteredAndSortedOperators && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
||||||
{filteredAndSortedOperators.map((operator) => (
|
{filteredAndSortedOperators.map((operator) => (
|
||||||
<OperatorCard key={operator.id} company={operator} />
|
<OperatorCard key={operator.id} company={operator} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -97,40 +97,42 @@ const ParkOwners = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search and Filters */}
|
{/* Search and Filters */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
<div className="space-y-3 mb-6">
|
||||||
<div className="relative flex-1">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
||||||
<Input
|
<Input
|
||||||
placeholder="Search property owners..."
|
placeholder="Search property owners..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="pl-10"
|
className="pl-10 h-11"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select value={sortBy} onValueChange={setSortBy}>
|
<div className="flex gap-2">
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<Select value={sortBy} onValueChange={setSortBy}>
|
||||||
<SelectValue placeholder="Sort by" />
|
<SelectTrigger className="flex-1 h-10">
|
||||||
</SelectTrigger>
|
<SelectValue placeholder="Sort by" />
|
||||||
<SelectContent>
|
</SelectTrigger>
|
||||||
<SelectItem value="name">Name</SelectItem>
|
<SelectContent>
|
||||||
<SelectItem value="rating">Rating</SelectItem>
|
<SelectItem value="name">Name</SelectItem>
|
||||||
<SelectItem value="founded">Founded Year</SelectItem>
|
<SelectItem value="rating">Rating</SelectItem>
|
||||||
<SelectItem value="reviews">Review Count</SelectItem>
|
<SelectItem value="founded">Founded Year</SelectItem>
|
||||||
</SelectContent>
|
<SelectItem value="reviews">Review Count</SelectItem>
|
||||||
</Select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<Select value={filterBy} onValueChange={setFilterBy}>
|
<Select value={filterBy} onValueChange={setFilterBy}>
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<SelectTrigger className="flex-1 h-10">
|
||||||
<Filter className="h-4 w-4 mr-2" />
|
<Filter className="h-4 w-4 mr-2" />
|
||||||
<SelectValue placeholder="Filter" />
|
<SelectValue placeholder="Filter" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all">All Owners</SelectItem>
|
<SelectItem value="all">All</SelectItem>
|
||||||
<SelectItem value="with-rating">With Ratings</SelectItem>
|
<SelectItem value="with-rating">Rated</SelectItem>
|
||||||
<SelectItem value="established">Established</SelectItem>
|
<SelectItem value="established">Est.</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Results Count */}
|
{/* Results Count */}
|
||||||
@@ -157,7 +159,7 @@ const ParkOwners = () => {
|
|||||||
|
|
||||||
{/* Property Owners Grid */}
|
{/* Property Owners Grid */}
|
||||||
{!isLoading && filteredAndSortedOwners && (
|
{!isLoading && filteredAndSortedOwners && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
||||||
{filteredAndSortedOwners.map((owner) => (
|
{filteredAndSortedOwners.map((owner) => (
|
||||||
<ParkOwnerCard key={owner.id} company={owner} />
|
<ParkOwnerCard key={owner.id} company={owner} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user