Refactor: Improve mobile header and navigation

This commit is contained in:
gpt-engineer-app[bot]
2025-09-30 11:59:31 +00:00
parent 556d1c5a97
commit 28ab241bb7
8 changed files with 523 additions and 239 deletions

View File

@@ -1,5 +1,5 @@
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 { Input } from '@/components/ui/input';
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 { Link, useNavigate } from 'react-router-dom';
import { SearchDropdown } from '@/components/search/SearchDropdown';
import { MobileSearch } from '@/components/search/MobileSearch';
import { AuthButtons } from '@/components/auth/AuthButtons';
import { ThemeToggle } from '@/components/theme/ThemeToggle';
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() {
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
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">
<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 */}
<nav className="hidden md:flex items-center gap-6 ml-8">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors">
<Search className="w-4 h-4 mr-2" />
Explore
<ChevronDown className="w-4 h-4 ml-1" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48 bg-background border border-border shadow-lg">
<DropdownMenuItem onClick={() => navigate('/parks')} className="hover:!bg-accent hover:!text-accent-foreground">
<MapPin className="w-4 h-4 mr-2" />
Parks
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/rides')} className="hover:!bg-accent hover:!text-accent-foreground">
<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>
const isMobile = useIsMobile();
{/* 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
return (
<>
<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">
<div className="container flex h-14 md:h-16 items-center justify-between px-3 md:px-4">
{/* Mobile: Menu + Logo */}
<div className="flex items-center gap-2 md:gap-6 flex-1 md:flex-initial">
{/* Mobile Menu */}
<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 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 />
</SheetTrigger>
<SheetContent side="left" className="w-[280px] sm:w-[320px]">
<nav className="flex flex-col gap-1 mt-8">
<div className="mb-4">
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2 px-3">
Explore
</h3>
</div>
<Button variant="outline" className="w-full mb-2">
Sign In
</Button>
<Button className="w-full bg-gradient-to-r from-primary to-accent">
Join ThrillWiki
</Button>
</div>
</div>
</SheetContent>
</Sheet>
<Link
to="/parks"
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
onClick={() => setOpen(false)}
>
Parks
</Link>
<Link
to="/rides"
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>
</header>;
</header>
{/* Mobile Search Modal */}
<MobileSearch open={mobileSearchOpen} onOpenChange={setMobileSearchOpen} />
</>
);
}

View File

@@ -35,7 +35,7 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
return (
<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}
>
{/* 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" />
{/* Company Type Badge */}
<div className="absolute top-3 right-3 z-10">
<Badge variant="outline" className="bg-background/80 backdrop-blur-sm">
<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 text-xs">
{formatCompanyType(company.company_type)}
</Badge>
</div>
@@ -52,7 +52,7 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
{/* Logo Display */}
<div className="absolute inset-0 flex items-center justify-center">
{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
src={company.logo_url}
alt={`${company.name} logo`}
@@ -60,28 +60,28 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
/>
</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)}
</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 */}
<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}
</h3>
{/* 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}
</p>
)}
{/* 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 && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">Founded:</span>
@@ -90,8 +90,8 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
)}
{company.headquarters_location && (
<div className="flex items-center gap-1">
<MapPin className="w-3 h-3 text-muted-foreground" />
<div className="flex items-center gap-1 min-w-0">
<MapPin className="w-3 h-3 text-muted-foreground shrink-0" />
<span className="text-muted-foreground truncate">
{company.headquarters_location}
</span>
@@ -102,16 +102,16 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
{/* Rating */}
{company.average_rating > 0 && (
<div className="flex items-center gap-1">
<Star className="w-4 h-4 fill-yellow-400 text-yellow-400" />
<span className="text-sm font-medium">{company.average_rating.toFixed(1)}</span>
<Star className="w-3.5 h-3.5 md:w-4 md:h-4 fill-yellow-400 text-yellow-400" />
<span className="text-xs md:text-sm font-medium">{company.average_rating.toFixed(1)}</span>
{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>
)}
{/* 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 && (
<div className="flex items-center gap-1">
<FerrisWheel className="w-3 h-3 text-muted-foreground" />

View 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>
);
}

View 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,
}

View File

@@ -126,35 +126,32 @@ export default function Designers() {
</div>
{/* Search and Filters */}
<div className="mb-8 space-y-4">
<div className="flex flex-col md:flex-row gap-4">
<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
placeholder="Search designers by name, location, or description..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
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 className="space-y-3 mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="Search designers..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 h-11"
/>
</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>
{/* Companies Grid */}
{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) => (
<DesignerCard key={company.id} company={company} />
))}

View File

@@ -146,36 +146,32 @@ export default function Manufacturers() {
</div>
{/* Search and Filters */}
<div className="mb-8 space-y-4">
<div className="flex flex-col md:flex-row gap-4">
<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
placeholder="Search manufacturers by name, location, or description..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
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 className="space-y-3 mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="Search manufacturers..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 h-11"
/>
</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>
{/* Companies Grid */}
{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) => (
<ManufacturerCard key={company.id} company={company} />
))}

View File

@@ -97,40 +97,42 @@ const Operators = () => {
</div>
{/* Search and Filters */}
<div className="flex flex-col sm:flex-row gap-4 mb-8">
<div className="relative flex-1">
<div className="space-y-3 mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="Search park operators..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
className="pl-10 h-11"
/>
</div>
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-full sm:w-48">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="name">Name</SelectItem>
<SelectItem value="rating">Rating</SelectItem>
<SelectItem value="founded">Founded Year</SelectItem>
<SelectItem value="reviews">Review Count</SelectItem>
</SelectContent>
</Select>
<div className="flex gap-2">
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="flex-1 h-10">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="name">Name</SelectItem>
<SelectItem value="rating">Rating</SelectItem>
<SelectItem value="founded">Founded Year</SelectItem>
<SelectItem value="reviews">Review Count</SelectItem>
</SelectContent>
</Select>
<Select value={filterBy} onValueChange={setFilterBy}>
<SelectTrigger className="w-full sm:w-48">
<Filter className="h-4 w-4 mr-2" />
<SelectValue placeholder="Filter" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Operators</SelectItem>
<SelectItem value="with-rating">With Ratings</SelectItem>
<SelectItem value="established">Established</SelectItem>
</SelectContent>
</Select>
<Select value={filterBy} onValueChange={setFilterBy}>
<SelectTrigger className="flex-1 h-10">
<Filter className="h-4 w-4 mr-2" />
<SelectValue placeholder="Filter" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All</SelectItem>
<SelectItem value="with-rating">Rated</SelectItem>
<SelectItem value="established">Est.</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Results Count */}
@@ -157,7 +159,7 @@ const Operators = () => {
{/* Operators Grid */}
{!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) => (
<OperatorCard key={operator.id} company={operator} />
))}

View File

@@ -97,40 +97,42 @@ const ParkOwners = () => {
</div>
{/* Search and Filters */}
<div className="flex flex-col sm:flex-row gap-4 mb-8">
<div className="relative flex-1">
<div className="space-y-3 mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="Search property owners..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
className="pl-10 h-11"
/>
</div>
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-full sm:w-48">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="name">Name</SelectItem>
<SelectItem value="rating">Rating</SelectItem>
<SelectItem value="founded">Founded Year</SelectItem>
<SelectItem value="reviews">Review Count</SelectItem>
</SelectContent>
</Select>
<div className="flex gap-2">
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="flex-1 h-10">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="name">Name</SelectItem>
<SelectItem value="rating">Rating</SelectItem>
<SelectItem value="founded">Founded Year</SelectItem>
<SelectItem value="reviews">Review Count</SelectItem>
</SelectContent>
</Select>
<Select value={filterBy} onValueChange={setFilterBy}>
<SelectTrigger className="w-full sm:w-48">
<Filter className="h-4 w-4 mr-2" />
<SelectValue placeholder="Filter" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Owners</SelectItem>
<SelectItem value="with-rating">With Ratings</SelectItem>
<SelectItem value="established">Established</SelectItem>
</SelectContent>
</Select>
<Select value={filterBy} onValueChange={setFilterBy}>
<SelectTrigger className="flex-1 h-10">
<Filter className="h-4 w-4 mr-2" />
<SelectValue placeholder="Filter" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All</SelectItem>
<SelectItem value="with-rating">Rated</SelectItem>
<SelectItem value="established">Est.</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Results Count */}
@@ -157,7 +159,7 @@ const ParkOwners = () => {
{/* Property Owners Grid */}
{!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) => (
<ParkOwnerCard key={owner.id} company={owner} />
))}