295 lines
9.9 KiB
TypeScript
295 lines
9.9 KiB
TypeScript
'use client';
|
|
|
|
import CategoryImage from '@/assets/water-bottle.png';
|
|
import { category_api } from '@/shared/config/api/category/api';
|
|
import { product_api } from '@/shared/config/api/product/api';
|
|
import { BASE_URL } from '@/shared/config/api/URLs';
|
|
import { Link } from '@/shared/config/i18n/navigation';
|
|
import { cn } from '@/shared/lib/utils';
|
|
import { AspectRatio } from '@/shared/ui/aspect-ratio';
|
|
import { Button } from '@/shared/ui/button';
|
|
import { Card } from '@/shared/ui/card';
|
|
import {
|
|
Carousel,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
type CarouselApi,
|
|
} from '@/shared/ui/carousel';
|
|
import { Skeleton } from '@/shared/ui/skeleton';
|
|
import { CategoryCarousel } from '@/widgets/categories/ui/category-carousel';
|
|
import { ProductCard } from '@/widgets/categories/ui/product-card';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
import Image from 'next/image';
|
|
import { useEffect, useState } from 'react';
|
|
import 'swiper/css';
|
|
import { banner_api } from '../lib/api';
|
|
|
|
const Welcome = () => {
|
|
const [api, setApi] = useState<CarouselApi>();
|
|
const [apiPro, setApiPro] = useState<CarouselApi>();
|
|
const [canScrollPrev, setCanScrollPrev] = useState(false);
|
|
const [canScrollNext, setCanScrollNext] = useState(false);
|
|
const [apiCat, setApiCat] = useState<CarouselApi>();
|
|
|
|
const { data, isLoading, isError } = useQuery({
|
|
queryKey: ['banner_list'],
|
|
queryFn: () => banner_api.getBanner(),
|
|
select(data) {
|
|
return data.data;
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!apiPro) return;
|
|
|
|
const updateButtons = () => {
|
|
setCanScrollPrev(apiPro.canScrollPrev());
|
|
setCanScrollNext(apiPro.canScrollNext());
|
|
};
|
|
|
|
updateButtons();
|
|
apiPro.on('select', updateButtons);
|
|
apiPro.on('reInit', updateButtons);
|
|
|
|
return () => {
|
|
apiPro.off('select', updateButtons);
|
|
apiPro.off('reInit', updateButtons);
|
|
};
|
|
}, [apiPro]);
|
|
|
|
const scrollPrevPro = () => {
|
|
if (apiPro) {
|
|
apiPro?.scrollPrev();
|
|
}
|
|
};
|
|
|
|
const scrollNextPro = () => {
|
|
if (apiPro) {
|
|
apiPro?.scrollNext();
|
|
}
|
|
};
|
|
|
|
const { data: category } = useQuery({
|
|
queryKey: ['category_list'],
|
|
queryFn: () => category_api.getCategory({ page: 1, page_size: 99 }),
|
|
select(data) {
|
|
return data.data.results;
|
|
},
|
|
});
|
|
|
|
const scrollPrev = () => {
|
|
if (api?.canScrollPrev()) {
|
|
api?.scrollPrev();
|
|
} else if (!api?.canScrollPrev()) {
|
|
api?.scrollTo(api?.slideNodes().length - 1);
|
|
}
|
|
};
|
|
|
|
const scrollPrevCar = () => {
|
|
apiCat?.scrollPrev();
|
|
};
|
|
|
|
const scrollNext = () => {
|
|
if (api?.canScrollNext()) {
|
|
api?.scrollNext();
|
|
} else if (!api?.canScrollNext()) {
|
|
api?.scrollTo(0);
|
|
}
|
|
};
|
|
|
|
const scrollNextCat = () => {
|
|
apiCat?.scrollNext();
|
|
};
|
|
|
|
const { data: product, isLoading: productLoading } = useQuery({
|
|
queryKey: ['list'],
|
|
queryFn: () =>
|
|
product_api.list({
|
|
page: 1,
|
|
page_size: 16,
|
|
}),
|
|
select(data) {
|
|
return data.data;
|
|
},
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<div className="custom-container">
|
|
<Carousel className="w-full" setApi={setApi}>
|
|
<CarouselContent>
|
|
{isLoading && (
|
|
<CarouselItem className="relative">
|
|
<Skeleton className="w-full h-[200px]" />
|
|
</CarouselItem>
|
|
)}
|
|
{isError && (
|
|
<CarouselItem className="relative gap-2 bg-gray-300/20 rounded-xl flex flex-col justify-center items-center">
|
|
<AlertCircle className="size-10 text-red-500" />
|
|
<p className="text-red-500">Banner yuklanmadi. Xatolik yuz</p>
|
|
</CarouselItem>
|
|
)}
|
|
{data &&
|
|
data.map((banner, index) => (
|
|
<CarouselItem key={index} className="relative">
|
|
<AspectRatio
|
|
ratio={16 / 7}
|
|
className="relative overflow-hidden rounded-2xl"
|
|
>
|
|
<Image
|
|
src={BASE_URL + banner.banner}
|
|
alt={banner.id}
|
|
width={900}
|
|
unoptimized
|
|
height={900}
|
|
className="object-cover w-full h-full"
|
|
/>
|
|
</AspectRatio>
|
|
<Button
|
|
onClick={scrollNext}
|
|
className="absolute max-lg:w-6 max-lg:h-6 top-1/2 -translate-y-1/2 right-2 cursor-pointer"
|
|
variant={'secondary'}
|
|
size={'icon'}
|
|
aria-label="next images"
|
|
>
|
|
<ChevronRight className="size-6 max-lg:size-5" />
|
|
</Button>
|
|
<Button
|
|
onClick={scrollPrev}
|
|
className="absolute max-lg:w-6 max-lg:h-6 top-1/2 -translate-y-1/2 left-5 cursor-pointer"
|
|
variant={'secondary'}
|
|
size={'icon'}
|
|
aria-label="prev images"
|
|
>
|
|
<ChevronLeft className="size-6 max-lg:size-5" />
|
|
</Button>
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
</Carousel>
|
|
|
|
<Carousel className="w-full mt-5" setApi={setApiCat}>
|
|
<CarouselContent className="py-2 px-1 pr-[12%]">
|
|
{category &&
|
|
category.map((banner, index) => (
|
|
<CarouselItem
|
|
key={index}
|
|
className="basis-1/5 max-lg:basis-1/3 max-md:basis-1/2 max-xs:basis-1/1"
|
|
>
|
|
<Link href={`/category/${banner.id}`}>
|
|
<div className="flex flex-col gap-1 items-center justify-start bg-white p-3 rounded-lg shadow-md cursor-pointer space-x-3">
|
|
<Image
|
|
src={
|
|
banner.image ? BASE_URL + banner.image : CategoryImage
|
|
}
|
|
alt={banner.name}
|
|
unoptimized
|
|
width={500}
|
|
height={500}
|
|
className="w-full h-16 object-contain"
|
|
/>
|
|
<p className="text-sm font-bold line-clamp-1 leading-tight text-slate-700">
|
|
{banner.name}
|
|
</p>
|
|
</div>
|
|
</Link>
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
<Button
|
|
onClick={scrollNextCat}
|
|
className="absolute max-lg:w-8 max-lg:h-8 top-1/2 -translate-y-1/2 -right-2 cursor-pointer"
|
|
variant={'secondary'}
|
|
size={'icon'}
|
|
aria-label="next images"
|
|
>
|
|
<ChevronRight className="size-6 max-lg:size-6" />
|
|
</Button>
|
|
<Button
|
|
onClick={scrollPrevCar}
|
|
className="absolute max-lg:w-8 max-lg:h-8 top-1/2 -translate-y-1/2 -left-2 cursor-pointer"
|
|
variant={'secondary'}
|
|
size={'icon'}
|
|
aria-label="prev images"
|
|
>
|
|
<ChevronLeft className="size-6 max-lg:size-6" />
|
|
</Button>
|
|
</Carousel>
|
|
</div>
|
|
<section className="relative custom-container mt-5 justify-center items-center border-b border-slate-200">
|
|
<div className="flex items-center justify-between pb-3">
|
|
<div className="flex items-center gap-2 group cursor-pointer">
|
|
<div className="p-1.5 bg-slate-100 rounded-full group-hover:bg-blue-100 transition-all"></div>
|
|
</div>
|
|
</div>
|
|
<Carousel className="w-full mt-5" setApi={setApiPro}>
|
|
<CarouselContent className="pr-[12%] sm:pr-0">
|
|
{productLoading &&
|
|
Array.from({ length: 6 }).map((__, index) => (
|
|
<CarouselItem
|
|
key={index}
|
|
className="basis-1/2 sm:basis-1/3 md:basis-1/4 lg:basis-1/5 xl:basis-1/6 pb-2"
|
|
>
|
|
<Card className="p-3 space-y-3 rounded-xl">
|
|
<Skeleton className="h-40 sm:h-48 md:h-56 w-full rounded-lg" />
|
|
<Skeleton className="h-4 w-3/4" />
|
|
<Skeleton className="h-4 w-1/2" />
|
|
<Skeleton className="h-10 w-full rounded-lg" />
|
|
</Card>
|
|
</CarouselItem>
|
|
))}
|
|
{product &&
|
|
!isLoading &&
|
|
product.results
|
|
.filter((product) => product.state === 'A')
|
|
.map((product) => (
|
|
<CarouselItem
|
|
key={product.id}
|
|
className="basis-1/2 sm:basis-1/3 md:basis-1/4 lg:basis-1/5 xl:basis-1/6 pb-2"
|
|
>
|
|
<ProductCard product={product} />
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
</Carousel>{' '}
|
|
<Button
|
|
onClick={scrollNextPro}
|
|
className={cn(
|
|
'absolute top-0 right-4 max-lg:hidden text-white cursor-pointer',
|
|
canScrollNext
|
|
? 'bg-green-600 hover:bg-green-600/70'
|
|
: 'bg-green-600/50 cursor-not-allowed',
|
|
)}
|
|
disabled={!canScrollNext}
|
|
aria-label="next images"
|
|
size="icon"
|
|
>
|
|
<ChevronRight className="size-6" />
|
|
</Button>
|
|
<Button
|
|
onClick={scrollPrevPro}
|
|
className={cn(
|
|
'absolute top-0 right-16 max-lg:hidden text-white cursor-pointer',
|
|
canScrollPrev
|
|
? 'bg-green-600 hover:bg-green-600/70'
|
|
: 'bg-green-600/50 cursor-not-allowed',
|
|
)}
|
|
aria-label="prev images"
|
|
disabled={!canScrollPrev}
|
|
size="icon"
|
|
>
|
|
<ChevronLeft className="size-6" />
|
|
</Button>
|
|
</section>
|
|
|
|
{category &&
|
|
category
|
|
.slice(0, 6)
|
|
.map((e) => <CategoryCarousel category={e} key={e.id} />)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Welcome;
|