bug fixed
This commit is contained in:
@@ -81,16 +81,18 @@ const ProductDetail = () => {
|
|||||||
? 0
|
? 0
|
||||||
: Number(String(quantity).replace(',', '.'));
|
: Number(String(quantity).replace(',', '.'));
|
||||||
|
|
||||||
/* ---------------- HELPERS ---------------- */
|
|
||||||
const clampQuantity = (value: number) => {
|
const clampQuantity = (value: number) => {
|
||||||
if (isNaN(value)) return MIN_QTY;
|
if (isNaN(value)) return MIN_QTY;
|
||||||
|
|
||||||
let safe = Math.max(value, MIN_QTY);
|
let safe = value;
|
||||||
safe = Math.min(safe);
|
|
||||||
|
if (isGram) {
|
||||||
|
safe = Math.max(value, MIN_QTY);
|
||||||
|
safe = Math.ceil(safe / STEP) * STEP;
|
||||||
|
}
|
||||||
|
|
||||||
return safe;
|
return safe;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getQuantityMessage = (qty: number, measurement: string | null) => {
|
const getQuantityMessage = (qty: number, measurement: string | null) => {
|
||||||
if (!measurement) return `${qty} dona`;
|
if (!measurement) return `${qty} dona`;
|
||||||
return `${qty} ${measurement}`;
|
return `${qty} ${measurement}`;
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
import { useRouter } from '@/shared/config/i18n/navigation';
|
import { useRouter } from '@/shared/config/i18n/navigation';
|
||||||
import { cn } from '@/shared/lib/utils';
|
import { cn } from '@/shared/lib/utils';
|
||||||
import { Button } from '@/shared/ui/button';
|
import { Button } from '@/shared/ui/button';
|
||||||
|
import { Card } from '@/shared/ui/card';
|
||||||
import {
|
import {
|
||||||
Carousel,
|
Carousel,
|
||||||
CarouselContent,
|
CarouselContent,
|
||||||
CarouselItem,
|
CarouselItem,
|
||||||
type CarouselApi,
|
type CarouselApi,
|
||||||
} from '@/shared/ui/carousel';
|
} from '@/shared/ui/carousel';
|
||||||
|
import { Skeleton } from '@/shared/ui/skeleton';
|
||||||
import { ProductCard } from '@/widgets/categories/ui/product-card';
|
import { ProductCard } from '@/widgets/categories/ui/product-card';
|
||||||
import { ProductRes } from '@/widgets/welcome/lib/api';
|
import { ProductRes } from '@/widgets/welcome/lib/api';
|
||||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
@@ -20,11 +22,13 @@ import { memo, useEffect, useRef, useState } from 'react';
|
|||||||
interface CategoryCarouselProps {
|
interface CategoryCarouselProps {
|
||||||
category: ProductRes;
|
category: ProductRes;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
isError: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryCarousel = memo(function CategoryCarousel({
|
const CategoryCarousel = memo(function CategoryCarousel({
|
||||||
category,
|
category,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
isError,
|
||||||
}: CategoryCarouselProps) {
|
}: CategoryCarouselProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [api, setApi] = useState<CarouselApi>();
|
const [api, setApi] = useState<CarouselApi>();
|
||||||
@@ -36,7 +40,6 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
// Intersection Observer
|
// Intersection Observer
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!sectionRef.current) return;
|
if (!sectionRef.current) return;
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
@@ -48,7 +51,6 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
},
|
},
|
||||||
{ rootMargin: '100px', threshold: 0.1 },
|
{ rootMargin: '100px', threshold: 0.1 },
|
||||||
);
|
);
|
||||||
|
|
||||||
observer.observe(sectionRef.current);
|
observer.observe(sectionRef.current);
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -72,7 +74,6 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
const scrollPrev = () => api?.scrollPrev();
|
const scrollPrev = () => api?.scrollPrev();
|
||||||
const scrollNext = () => api?.scrollNext();
|
const scrollNext = () => api?.scrollNext();
|
||||||
|
|
||||||
// Shartli renderlar
|
|
||||||
if (!isVisible) {
|
if (!isVisible) {
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
@@ -82,11 +83,8 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isLoading && !category) return null;
|
|
||||||
|
|
||||||
const activeProducts =
|
const activeProducts =
|
||||||
category?.products.filter((p) => p.state === 'A') ?? [];
|
category?.products?.filter((p) => p.state === 'A') ?? [];
|
||||||
if (!isLoading && activeProducts.length === 0) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
@@ -99,7 +97,7 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
onClick={() => router.push(`/category/${category.id}/`)}
|
onClick={() => router.push(`/category/${category.id}/`)}
|
||||||
>
|
>
|
||||||
<h2 className="text-2xl font-bold text-slate-800 group-hover:text-blue-600 transition-colors">
|
<h2 className="text-2xl font-bold text-slate-800 group-hover:text-blue-600 transition-colors">
|
||||||
{category.name}
|
{category.name || '---'}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="p-1.5 bg-slate-100 rounded-full group-hover:bg-blue-100 transition-all">
|
<div className="p-1.5 bg-slate-100 rounded-full group-hover:bg-blue-100 transition-all">
|
||||||
<ChevronRight className="text-slate-600 group-hover:text-blue-600 group-hover:translate-x-0.5 transition-all" />
|
<ChevronRight className="text-slate-600 group-hover:text-blue-600 group-hover:translate-x-0.5 transition-all" />
|
||||||
@@ -108,22 +106,33 @@ const CategoryCarousel = memo(function CategoryCarousel({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Carousel
|
<Carousel
|
||||||
opts={{
|
opts={{ align: 'start', dragFree: true }}
|
||||||
align: 'start',
|
|
||||||
dragFree: true, // 🔥 free scroll
|
|
||||||
}}
|
|
||||||
className="w-full mt-2"
|
className="w-full mt-2"
|
||||||
setApi={setApi}
|
setApi={setApi}
|
||||||
>
|
>
|
||||||
<CarouselContent className="pr-[12%] sm:pr-0">
|
<CarouselContent className="pr-[12%] sm:pr-0">
|
||||||
{activeProducts.map((product) => (
|
{isLoading || isError || activeProducts.length === 0
|
||||||
<CarouselItem
|
? Array.from({ length: 6 }).map((__, index) => (
|
||||||
key={product.id}
|
<CarouselItem
|
||||||
className="basis-1/2 sm:basis-1/3 md:basis-1/4 lg:basis-1/5 xl:basis-1/6 pb-2"
|
key={index}
|
||||||
>
|
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>
|
<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>
|
||||||
|
))
|
||||||
|
: activeProducts.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>
|
</CarouselContent>
|
||||||
</Carousel>
|
</Carousel>
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,11 @@ const Welcome = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: allProducts, isLoading: allProductsLoading } = useQuery({
|
const {
|
||||||
|
data: allProducts,
|
||||||
|
isLoading: allProductsLoading,
|
||||||
|
isError: allProductsError,
|
||||||
|
} = useQuery({
|
||||||
queryKey: ['all_products'],
|
queryKey: ['all_products'],
|
||||||
queryFn: () => banner_api.getAllProducts(),
|
queryFn: () => banner_api.getAllProducts(),
|
||||||
select(data) {
|
select(data) {
|
||||||
@@ -300,14 +304,43 @@ const Welcome = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{allProducts &&
|
{productLoading || allProductsLoading ? (
|
||||||
allProducts.map((e) => (
|
<section className="relative custom-container mt-5 justify-center items-center border-b border-slate-200">
|
||||||
|
<Carousel
|
||||||
|
opts={{
|
||||||
|
align: 'start',
|
||||||
|
dragFree: true,
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
setApi={setApiPro}
|
||||||
|
>
|
||||||
|
<CarouselContent className="pr-[12%] sm:pr-0">
|
||||||
|
{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>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
</Carousel>
|
||||||
|
</section>
|
||||||
|
) : (
|
||||||
|
allProducts?.map((e) => (
|
||||||
<CategoryCarousel
|
<CategoryCarousel
|
||||||
category={e}
|
category={e}
|
||||||
key={e.id}
|
key={e.id}
|
||||||
isLoading={allProductsLoading}
|
isLoading={allProductsLoading}
|
||||||
|
isError={allProductsError}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user