Files
gastro-bot/src/widgets/navbar/ui/index.tsx
Samandar Turgunboyev 855600cfe2 update ui
2026-03-07 10:13:03 +05:00

631 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { cart_api } from '@/features/cart/lib/api';
import { category_api } from '@/shared/config/api/category/api';
import { Link, useRouter } from '@/shared/config/i18n/navigation';
import { useCartId } from '@/shared/hooks/cartId';
import formatPhone from '@/shared/lib/formatPhone';
import { getToken } from '@/shared/lib/token';
import { Badge } from '@/shared/ui/badge';
import { Button } from '@/shared/ui/button';
import { Input } from '@/shared/ui/input';
import { Popover, PopoverContent } from '@/shared/ui/popover';
import {
Sheet,
SheetClose,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/shared/ui/sheet';
import { banner_api } from '@/widgets/welcome/lib/api';
import { userStore } from '@/widgets/welcome/lib/hook';
import { PopoverTrigger } from '@radix-ui/react-popover';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
ChevronRight,
Facebook,
Heart,
Instagram,
LayoutGrid,
Mail,
MenuIcon,
Phone,
Search,
Send,
ShoppingCart,
Twitter,
User,
XIcon,
} from 'lucide-react';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import useCategoryActive from '../lib/openCategory';
import { ChangeLang } from './ChangeLang';
import { MobileLanguageSelector } from './MobileLanguageSelector';
import NavbarMobile from './NavbarMobile';
import { SearchResult } from './SearchResult';
const Navbar = () => {
const [isSticky, setIsSticky] = useState(false);
const [query, setQuery] = useState('');
const searchParams = useSearchParams();
const token = getToken();
const t = useTranslations();
const { cart_id } = useCartId();
const [cartQuenty, setCartQuenty] = useState<number>(0);
const { setCartId } = useCartId();
const { setUser } = userStore();
const { mutate: cart } = useMutation({
mutationFn: () => cart_api.create(),
onSuccess: (data) => {
setCartId(data.data.cart_id);
},
});
const { data: me } = useQuery({
queryKey: ['get_me'],
queryFn: () => banner_api.getMe(),
});
const { data: category } = useQuery({
queryKey: ['category_list'],
queryFn: () => category_api.getCategory({ page: 1, page_size: 99 }),
select(data) {
return data.data;
},
});
useEffect(() => {
if (me) {
setUser(me.data);
}
}, [me]);
useEffect(() => {
if (token) {
cart();
}
}, [token]);
const queryFromUrl = searchParams.get('q') ?? '';
const { data: cartItems } = useQuery({
queryKey: ['cart_items', cart_id],
queryFn: () => cart_api.get_cart_items(cart_id!),
enabled: !!cart_id,
select(data) {
return data.data.cart_item;
},
});
useEffect(() => {
if (cartItems) {
const total = cartItems.length;
setCartQuenty(total > 9 ? 9 : total);
} else if (cart_id === null) {
setCartQuenty(0);
}
}, [cartItems, cart_id]);
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 (
<>
<div className="w-full bg-[#57A595] 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-6">
{/* Sahifalar */}
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<Link
href="/about"
className="flex items-center gap-1.5 font-medium"
>
{t('Biz haqimizda')}
</Link>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<Link
href="/privacy-policy"
className="flex items-center gap-1.5 font-medium"
>
{t('Maxfiylik siyosati')}
</Link>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<Link
href="/faq"
className="flex items-center gap-1.5 font-medium"
>
{t('Savol-javob')}
</Link>
</li>
{/* Divider */}
<li className="text-white/30">|</li>
{/* Ijtimoiy tarmoqlar */}
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'#'}
className="flex items-center gap-2"
aria-label="telegram"
>
<Send className="size-4" />
</a>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'#'}
className="flex items-center gap-2"
aria-label="Instagram"
>
<Instagram className="size-4" />
</a>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'#'}
className="flex items-center gap-2"
aria-label="Facebook"
>
<Facebook className="size-4" />
</a>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'#'}
className="flex items-center gap-2"
aria-label="Twitter"
>
<Twitter className="size-4" />
</a>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'#'}
className="flex items-center gap-2"
aria-label="Mail"
>
<Mail className="size-4" />
</a>
</li>
<li className="text-white hover:text-white/80 transition-colors cursor-pointer">
<a
href={'tel:+998901234567'}
className="flex items-center gap-2"
aria-label="Phone"
>
<Phone className="size-4" />
<p>{formatPhone('+998901234567')}</p>
</a>
</li>
</ul>
<ChangeLang />
</div>
</div>
<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 items-center"
onClick={() => router.push('/')}
>
<Image
src="/logos/logo.png"
alt="logo"
width={40}
unoptimized
height={20}
className="w-10 h-fit"
/>
<p className="font-semibold">GASTRO</p>
</div>
<div className="w-full flex justify-end items-center lg:hidden gap-2">
<Button
variant={'ghost'}
size={'icon'}
onClick={() => router.push('/search')}
>
<Search className="size-5" />
</Button>
<MobileLanguageSelector />
<Sheet>
<SheetTrigger asChild>
<Button variant="outline" size="icon">
<MenuIcon className="size-5" />
</Button>
</SheetTrigger>
<SheetContent className="w-[320px] p-0 flex flex-col">
<SheetHeader className="px-6 py-4 border-b border-slate-200 bg-gradient-to-r from-[#57A595] to-[#69b5a5]">
<SheetTitle className="flex items-center gap-3">
<div className="bg-white p-2 rounded-lg">
<Image
width={32}
unoptimized
height={32}
src={'/logos/logo.png'}
alt="logo"
className="w-8 h-8"
/>
</div>
<p className="text-white text-xl font-bold">GASTRO</p>
</SheetTitle>
</SheetHeader>
<div className="flex-1 overflow-y-auto">
{/* Menu Bo'limlari */}
<div className="px-4 py-6 space-y-6">
{/* Asosiy Sahifalar */}
<div>
<h3 className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-3 px-2">
{t('Sahifalar')}
</h3>
<nav className="space-y-1">
<SheetClose asChild>
<Link
href="/about"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-slate-100 hover:text-[#57A595] transition-all group"
>
<div className="w-1 h-1 rounded-full bg-slate-400 group-hover:bg-[#57A595]" />
<span className="text-sm font-medium">
{t('Biz haqimizda')}
</span>
<ChevronRight className="size-4 ml-auto opacity-0 group-hover:opacity-100 transition-opacity" />
</Link>
</SheetClose>
<SheetClose asChild>
<Link
href="/privacy-policy"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-slate-100 hover:text-[#57A595] transition-all group"
>
<div className="w-1 h-1 rounded-full bg-slate-400 group-hover:bg-[#57A595]" />
<span className="text-sm font-medium">
{t('Maxfiylik siyosati')}
</span>
<ChevronRight className="size-4 ml-auto opacity-0 group-hover:opacity-100 transition-opacity" />
</Link>
</SheetClose>
<SheetClose asChild>
<Link
href="/faq"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-slate-100 hover:text-[#57A595] transition-all group"
>
<div className="w-1 h-1 rounded-full bg-slate-400 group-hover:bg-[#57A595]" />
<span className="text-sm font-medium">
{t('Savol-javob')}
</span>
<ChevronRight className="size-4 ml-auto opacity-0 group-hover:opacity-100 transition-opacity" />
</Link>
</SheetClose>
</nav>
</div>
{/* Divider */}
<div className="h-px bg-slate-200" />
{/* Aloqa */}
<div>
<h3 className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-3 px-2">
{t("Biz bilan bog'laning")}
</h3>
<nav className="space-y-1">
<a
href="#"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-blue-50 hover:text-blue-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-blue-100 text-blue-600 group-hover:bg-blue-600 group-hover:text-white transition-colors">
<Send className="size-3.5" />
</div>
<span className="text-sm font-medium">Telegram</span>
</a>
<a
href="#"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-pink-50 hover:text-pink-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-pink-100 text-pink-600 group-hover:bg-pink-600 group-hover:text-white transition-colors">
<Instagram className="size-3.5" />
</div>
<span className="text-sm font-medium">Instagram</span>
</a>
<a
href="#"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-blue-50 hover:text-blue-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-blue-100 text-blue-600 group-hover:bg-blue-600 group-hover:text-white transition-colors">
<Facebook className="size-3.5" />
</div>
<span className="text-sm font-medium">Facebook</span>
</a>
<a
href="#"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-sky-50 hover:text-sky-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-sky-100 text-sky-600 group-hover:bg-sky-600 group-hover:text-white transition-colors">
<Twitter className="size-3.5" />
</div>
<span className="text-sm font-medium">Twitter</span>
</a>
<a
href="mailto:info@gastro.uz"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-purple-50 hover:text-purple-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-purple-100 text-purple-600 group-hover:bg-purple-600 group-hover:text-white transition-colors">
<Mail className="size-3.5" />
</div>
<span className="text-sm font-medium">
info@gastro.uz
</span>
</a>
<a
href="tel:+998901234567"
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-700 hover:bg-emerald-50 hover:text-emerald-600 transition-all group"
>
<div className="p-1.5 rounded-lg bg-emerald-100 text-emerald-600 group-hover:bg-emerald-600 group-hover:text-white transition-colors">
<Phone className="size-3.5" />
</div>
<span className="text-sm font-medium">
{formatPhone('+998901234567')}
</span>
</a>
</nav>
</div>
</div>
</div>
</SheetContent>
</Sheet>
</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">{t('Kataloglar')}</p>
</Button>
<div className="relative w-full max-lg:hidden">
<Input
placeholder={t('Mahsulot nomi')}
value={query}
onFocus={() => setSearchOpen(true)}
onBlur={() => setTimeout(() => setSearchOpen(false), 200)}
onKeyDown={(e) => {
if (e.key === 'Enter' && query.trim()) {
router.push(`/search?search=${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" />
{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={(e) => {
e.stopPropagation();
if (token) {
router.push('/favourite');
return;
}
router.push('/auth');
}}
aria-label="my favouurite product"
>
<Heart className="size-4 text-foreground" />
</Button>
<Button
variant={'ghost'}
id="cart-icon"
onClick={(e) => {
e.stopPropagation();
if (token) {
router.push('/cart');
return;
}
router.push('/auth');
}}
className="h-10 relative max-lg:hidden cursor-pointer border border-slate-200"
>
<ShoppingCart className="size-4 text-foreground" />
<Badge className="absolute -top-2 -right-2 line-clamp-1 w-6 flex justify-center items-center">
{cartQuenty === 9 ? cartQuenty + '+' : cartQuenty}
</Badge>
</Button>
<Button
variant={'ghost'}
onClick={() => {
if (token) {
router.push('/profile');
} else {
router.push('/auth');
}
}}
aria-label="my account"
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"
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">
{category &&
category.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 min-w-0">
<div className={`p-1.5 rounded-lg bg-white shrink-0`}>
<Image
src={e.image || '/placeholder.svg'}
alt={e.name}
width={500}
height={500}
unoptimized
className="w-5 h-5 object-contain"
/>
</div>
<p
className={`text-sm font-medium truncate ${
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>
{active && active?.product_types?.length > 0 ? (
<div className="grid grid-cols-3 gap-4">
{active.product_types.map((sub, index) => (
<Button
key={index}
onClick={() => {
setCloseToolbar(false);
router.push(`/category/${active.id}/${sub.id}`);
}}
variant="outline"
className="justify-start h-12 border border-slate-200 hover:border-blue-400 hover:bg-blue-50 transition-all rounded-xl"
>
{sub.name}
</Button>
))}
</div>
) : (
<div className="flex flex-col items-center justify-center h-[50vh] text-slate-400">
<p className="text-lg font-medium">
{t('Bu kategoriyada hozircha mahsulot yoq')}
</p>
<p className="text-sm mt-1">{t('Tez orada qoshiladi')}</p>
</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>
</>
);
};
export default Navbar;