first commit
This commit is contained in:
@@ -1,122 +1,324 @@
|
||||
import { Accordion } from '@/shared/ui/accordion';
|
||||
'use client';
|
||||
|
||||
import formatPhone from '@/shared/lib/formatPhone';
|
||||
import { Button } from '@/shared/ui/button';
|
||||
import { Input } from '@/shared/ui/input';
|
||||
import { Popover, PopoverContent } from '@/shared/ui/popover';
|
||||
import { categoryList } from '@/widgets/welcome/lib/data';
|
||||
import { PopoverTrigger } from '@radix-ui/react-popover';
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
} from '@/shared/ui/navigation-menu';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from '@/shared/ui/sheet';
|
||||
import { Menu } from 'lucide-react';
|
||||
import { menu } from '../lib/data';
|
||||
import { PRODUCT_INFO } from '@/shared/constants/data';
|
||||
import RenderMenuItem from './RenderItem';
|
||||
import RenderMobileMenuItem from './RenderMobileMenuItem';
|
||||
ChevronRight,
|
||||
Facebook,
|
||||
Heart,
|
||||
Instagram,
|
||||
LayoutGrid,
|
||||
Mail,
|
||||
Phone,
|
||||
Search,
|
||||
Send,
|
||||
ShoppingCart,
|
||||
Twitter,
|
||||
User,
|
||||
XIcon,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useCategoryActive from '../lib/openCategory';
|
||||
import { ChangeLang } from './ChangeLang';
|
||||
import Link from 'next/link';
|
||||
import { MobileLanguageSelector } from './MobileLanguageSelector';
|
||||
import NavbarMobile from './NavbarMobile';
|
||||
import { SearchResult } from './SearchResult';
|
||||
|
||||
const Navbar = () => {
|
||||
const auth = {
|
||||
login: { title: 'Login', url: '#' },
|
||||
signup: { title: 'Sign up', url: '#' },
|
||||
};
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const queryFromUrl = searchParams.get('q') ?? '';
|
||||
|
||||
useEffect(() => {
|
||||
setQuery(queryFromUrl);
|
||||
}, [queryFromUrl]);
|
||||
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const { active, openToolbar, setActive, setOpenToolbar, setCloseToolbar } =
|
||||
useCategoryActive();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsSticky(window.scrollY > 40);
|
||||
};
|
||||
|
||||
handleScroll();
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<section className="py-4">
|
||||
<div className="custom-container">
|
||||
{/* Desktop Menu */}
|
||||
<nav className="hidden justify-between lg:flex">
|
||||
<div className="flex items-center gap-6">
|
||||
{/* Logo */}
|
||||
<Link href={'/'} className="flex items-center gap-2">
|
||||
<img
|
||||
src={PRODUCT_INFO.logo}
|
||||
className="max-h-8"
|
||||
alt={PRODUCT_INFO.name}
|
||||
/>
|
||||
<span className="text-lg font-semibold tracking-tighter">
|
||||
{PRODUCT_INFO.name}
|
||||
</span>
|
||||
</Link>
|
||||
<div className="flex items-center">
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
{menu.map((item) => RenderMenuItem(item))}
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<ChangeLang />
|
||||
<Button asChild variant="outline">
|
||||
<Link href={auth.login.url}>{auth.login.title}</Link>
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<Link href={auth.signup.url}>{auth.signup.title}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
<div className="block lg:hidden">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<Link href={'/'} className="flex items-center gap-2">
|
||||
<img
|
||||
src={PRODUCT_INFO.logo}
|
||||
className="max-h-8"
|
||||
alt={PRODUCT_INFO.name}
|
||||
/>
|
||||
</Link>
|
||||
<Sheet>
|
||||
<div className="space-x-2">
|
||||
<ChangeLang />
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Menu className="size-4" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
</div>
|
||||
<SheetContent className="overflow-y-auto">
|
||||
<SheetHeader>
|
||||
<SheetTitle>
|
||||
<Link href={'/'} className="flex items-center gap-2">
|
||||
<img
|
||||
src={PRODUCT_INFO.logo}
|
||||
className="max-h-8"
|
||||
alt={PRODUCT_INFO.name}
|
||||
/>
|
||||
</Link>
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="flex flex-col gap-6 p-4">
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="flex w-full flex-col gap-4"
|
||||
>
|
||||
{menu.map((item) => RenderMobileMenuItem(item))}
|
||||
</Accordion>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<Button asChild variant="outline">
|
||||
<Link href={auth.login.url}>{auth.login.title}</Link>
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<Link href={auth.signup.url}>{auth.signup.title}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</div>
|
||||
<>
|
||||
<div className="w-full bg-blue-600 h-10 max-lg:hidden">
|
||||
<div className="custom-container h-full flex justify-between items-center">
|
||||
<ul className="text-sm flex items-center justify-center gap-4">
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Send className="size-4" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Instagram className="size-4" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Facebook className="size-4" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Twitter className="size-4" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Mail className="size-4" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-white transition-colors cursor-pointer">
|
||||
<a href={'#'} className="flex items-center gap-2">
|
||||
<Phone className="size-4" />
|
||||
<p>{formatPhone('+998901234567')}</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ChangeLang />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
className={`py-4 shadow-sm z-50 w-full bg-white transition-all duration-300 mb-5 border-b border-slate-200
|
||||
${isSticky ? 'fixed top-0 shadow-md' : 'relative'}
|
||||
`}
|
||||
>
|
||||
<div className="custom-container h-6 flex justify-between items-center gap-3">
|
||||
<div
|
||||
className="p-2 flex gap-2 rounded-xl cursor-pointer"
|
||||
onClick={() => router.push('/')}
|
||||
>
|
||||
<Image
|
||||
src="/favicon.png"
|
||||
alt="logo"
|
||||
width={40}
|
||||
height={20}
|
||||
className="w-6 h-6"
|
||||
/>
|
||||
<h3>Fias</h3>
|
||||
</div>
|
||||
<div className="w-full flex justify-end lg:hidden gap-2">
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
size={'icon'}
|
||||
onClick={() => router.push('/search')}
|
||||
>
|
||||
<Search className="size-5" />
|
||||
</Button>
|
||||
<MobileLanguageSelector />
|
||||
</div>
|
||||
<div className="flex-1 flex gap-3">
|
||||
<Button
|
||||
variant={'outline'}
|
||||
className="h-10 max-lg:hidden cursor-pointer"
|
||||
onClick={() => {
|
||||
if (openToolbar) {
|
||||
setCloseToolbar(false);
|
||||
} else if (!openToolbar) {
|
||||
setOpenToolbar(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{openToolbar ? (
|
||||
<XIcon className="text-foreground" />
|
||||
) : (
|
||||
<LayoutGrid className="size-4 text-foreground" />
|
||||
)}
|
||||
<p className="text-foreground">Kataloglar</p>
|
||||
</Button>
|
||||
|
||||
<div className="relative w-full max-lg:hidden">
|
||||
<Input
|
||||
placeholder="Mahsulot nomi"
|
||||
value={query}
|
||||
onFocus={() => setSearchOpen(true)}
|
||||
onBlur={() => setTimeout(() => setSearchOpen(false), 200)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && query.trim()) {
|
||||
router.push(`/search?q=${encodeURIComponent(query)}`);
|
||||
setSearchOpen(false);
|
||||
}
|
||||
}}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="border border-slate-200 focus:border-blue-400 focus:ring-blue-400 px-9 h-10"
|
||||
/>
|
||||
|
||||
<Search className="absolute top-1/2 -translate-y-1/2 left-2 size-5 text-muted-foreground" />
|
||||
|
||||
{/* Search Results Dropdown */}
|
||||
{searchOpen && (
|
||||
<div className="absolute top-full left-0 right-0 mt-2 bg-white border border-slate-200 rounded-xl shadow-lg min-h-[300px] max-h-[600px] overflow-y-auto scrollbar-thin z-50">
|
||||
<div className="p-4">
|
||||
<SearchResult query={query} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="h-10 max-lg:hidden cursor-pointer border border-slate-200"
|
||||
onClick={() => router.push('/favourite')}
|
||||
>
|
||||
<Heart className="size-4 text-foreground" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
onClick={() => router.push('/cart')}
|
||||
className="h-10 max-lg:hidden cursor-pointer border border-slate-200"
|
||||
>
|
||||
<ShoppingCart className="size-4 text-foreground" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
onClick={() => {
|
||||
const user = localStorage.getItem('user');
|
||||
if (user === 'true') {
|
||||
router.push('/profile');
|
||||
} else {
|
||||
router.push('/auth');
|
||||
}
|
||||
}}
|
||||
className="h-10 max-lg:hidden cursor-pointer border border-slate-200"
|
||||
>
|
||||
<User className="size-4 text-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NavbarMobile />
|
||||
|
||||
<Popover open={openToolbar} modal={false}>
|
||||
<PopoverTrigger className="!h-0 absolute top-12 w-screen">
|
||||
<div />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-[100vw] !p-0 h-[100vh] rounded-b-xl border-t-2 border-blue-500"
|
||||
onInteractOutside={() => setOpenToolbar(false)}
|
||||
>
|
||||
<div className="flex h-[90vh]">
|
||||
<div className="border-r border-slate-200 w-[20%] py-3 flex flex-col gap-2 px-3 overflow-y-auto scrollbar-thin bg-slate-50">
|
||||
{categoryList.map((e, index) => {
|
||||
const isActive = active?.name === e.name;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={index}
|
||||
variant="secondary"
|
||||
onMouseEnter={() => setActive(e)}
|
||||
onClick={() => setActive(e)}
|
||||
className={`flex justify-between items-center cursor-pointer px-4 h-14 rounded-xl transition-all duration-300 ${
|
||||
isActive
|
||||
? 'bg-blue-100 border border-blue-400'
|
||||
: 'hover:bg-slate-100 border border-transparent'
|
||||
}`}
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div
|
||||
className={`p-1.5 rounded-lg ${isActive ? 'bg-white' : 'bg-white'}`}
|
||||
>
|
||||
<Image
|
||||
src={e.image || '/placeholder.svg'}
|
||||
alt={e.name}
|
||||
className="w-5 h-5 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={`text-sm font-medium ${isActive ? 'text-blue-700' : 'text-slate-700'}`}
|
||||
>
|
||||
{e.name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ChevronRight
|
||||
className={`size-5 transition-transform duration-300 ${
|
||||
isActive
|
||||
? 'rotate-90 text-blue-600'
|
||||
: 'text-slate-400'
|
||||
}`}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="w-[80%] overflow-y-auto p-8 scrollbar-thin bg-white">
|
||||
<h3 className="text-2xl font-bold mb-6 text-blue-600">
|
||||
{active?.name}
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{active?.subCategories.map((sub, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setCloseToolbar(false);
|
||||
router.push(`/category/${active.name}/${sub.name}`);
|
||||
}}
|
||||
variant="outline"
|
||||
className="justify-start h-12 cursor-pointer border border-slate-200 hover:border-blue-400 hover:bg-blue-50 transition-all text-slate-700 hover:text-blue-700 font-medium rounded-xl bg-transparent"
|
||||
>
|
||||
{sub.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</section>
|
||||
|
||||
{isSticky && <div className="h-[72px]" />}
|
||||
|
||||
<style jsx global>{`
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: #f1f5f9;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cbd5e1 #f1f5f9;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user