This commit is contained in:
@@ -1,30 +1,24 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { CartQuantityControl } from '@/components/CartQuantityControl'
|
||||
import { VariableWeightSlider } from '@/components/product/VariableWeightSlider'
|
||||
import PlusIcon from '@/components/icons/PlusIcon'
|
||||
import { ef } from '@/lib/helpers/utfNumbers'
|
||||
import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart'
|
||||
import {
|
||||
getPrimaryVariantId,
|
||||
getPurchaseUnitLabel,
|
||||
getVariableQuantityRange,
|
||||
isVariablePricing,
|
||||
} from '@/app/[name]/(Main)/types/Types'
|
||||
import { ArrowLeft, Cup, Heart, TruckTick } from 'iconsax-react'
|
||||
import Image from 'next/image'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useGetProduct, useToggleFavorite } from './hooks/useProductData'
|
||||
import { useGetAbout } from '../about/hooks/useAboutData'
|
||||
import { useGetProfile } from '../../(Profile)/profile/hooks/userProfileData'
|
||||
import { toast } from '@/components/Toast'
|
||||
import { getToken } from '@/lib/api/func'
|
||||
import { useCart } from "@/app/[name]/(Dialogs)/cart/hook/useCart";
|
||||
import { getPrimaryVariantId, getPurchaseUnitLabel, getVariableQuantityRange, isVariablePricing } from "@/app/[name]/(Main)/types/Types";
|
||||
import { CartQuantityControl } from "@/components/CartQuantityControl";
|
||||
import PlusIcon from "@/components/icons/PlusIcon";
|
||||
import { VariableWeightSlider } from "@/components/product/VariableWeightSlider";
|
||||
import { toast } from "@/components/Toast";
|
||||
import { getToken } from "@/lib/api/func";
|
||||
import { ef } from "@/lib/helpers/utfNumbers";
|
||||
import { ArrowLeft, Cup, Heart, TruckTick } from "iconsax-react";
|
||||
import Image from "next/image";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useGetProfile } from "../../(Profile)/profile/hooks/userProfileData";
|
||||
import { useGetAbout } from "../about/hooks/useAboutData";
|
||||
import { useGetProduct, useToggleFavorite } from "./hooks/useProductData";
|
||||
|
||||
type Props = object
|
||||
type Props = object;
|
||||
|
||||
function ProductPage({}: Props) {
|
||||
|
||||
const { id, name } = useParams();
|
||||
const router = useRouter();
|
||||
const { isSuccess } = useGetProfile();
|
||||
@@ -39,7 +33,7 @@ function ProductPage({ }: Props) {
|
||||
const pendingCartWeightRef = useRef<number | null>(null);
|
||||
const { mutate: toggleFavorite } = useToggleFavorite();
|
||||
|
||||
const productId = product?.data?.id || id as string;
|
||||
const productId = product?.data?.id || (id as string);
|
||||
const cartId = useMemo(() => {
|
||||
if (!product?.data) return null;
|
||||
return selectedVariantId ?? getPrimaryVariantId(product.data) ?? productId;
|
||||
@@ -47,7 +41,7 @@ function ProductPage({ }: Props) {
|
||||
const quantity = useMemo(() => {
|
||||
if (!cartId) return 0;
|
||||
const item = items?.[cartId] ?? items?.[String(cartId)];
|
||||
if (item && typeof item === 'object' && 'quantity' in item) {
|
||||
if (item && typeof item === "object" && "quantity" in item) {
|
||||
return item.quantity || 0;
|
||||
}
|
||||
return 0;
|
||||
@@ -77,14 +71,18 @@ function ProductPage({ }: Props) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleVariableWeightChange = useCallback((weight: number) => {
|
||||
const handleVariableWeightChange = useCallback(
|
||||
(weight: number) => {
|
||||
isUserInteractingRef.current = true;
|
||||
setSelectedWeight(weight);
|
||||
clearCartSyncTimer();
|
||||
pendingCartWeightRef.current = null;
|
||||
}, [clearCartSyncTimer]);
|
||||
},
|
||||
[clearCartSyncTimer],
|
||||
);
|
||||
|
||||
const handleVariableWeightCommit = useCallback((weight: number) => {
|
||||
const handleVariableWeightCommit = useCallback(
|
||||
(weight: number) => {
|
||||
if (!isSuccess || !cartId || quantity <= 0) {
|
||||
isUserInteractingRef.current = false;
|
||||
return;
|
||||
@@ -104,16 +102,16 @@ function ProductPage({ }: Props) {
|
||||
}
|
||||
|
||||
const currentItem = items?.[cartId] ?? items?.[String(cartId)];
|
||||
const currentQty = currentItem && typeof currentItem === 'object' && 'quantity' in currentItem
|
||||
? currentItem.quantity
|
||||
: 0;
|
||||
const currentQty = currentItem && typeof currentItem === "object" && "quantity" in currentItem ? currentItem.quantity : 0;
|
||||
|
||||
if (Math.abs(pendingWeight - currentQty) > 0.0001) {
|
||||
setCartQuantity(cartId, pendingWeight);
|
||||
}
|
||||
isUserInteractingRef.current = false;
|
||||
}, 1000);
|
||||
}, [isSuccess, cartId, quantity, clearCartSyncTimer, items, setCartQuantity]);
|
||||
},
|
||||
[isSuccess, cartId, quantity, clearCartSyncTimer, items, setCartQuantity],
|
||||
);
|
||||
|
||||
const handleRemoveFromCart = () => {
|
||||
if (cartId) {
|
||||
@@ -126,7 +124,7 @@ function ProductPage({ }: Props) {
|
||||
|
||||
const token = await getToken();
|
||||
if (!token || !isSuccess) {
|
||||
toast('ابتدا لاگین کنید', 'error');
|
||||
toast("ابتدا لاگین کنید", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,7 +139,7 @@ function ProductPage({ }: Props) {
|
||||
onError: () => {
|
||||
// بازگرداندن به حالت قبلی در صورت خطا
|
||||
setIsFavorite(previousFavorite);
|
||||
toast('خطا در تغییر وضعیت علاقهمندی', 'error');
|
||||
toast("خطا در تغییر وضعیت علاقهمندی", "error");
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -168,65 +166,59 @@ function ProductPage({ }: Props) {
|
||||
|
||||
const { min } = getVariableQuantityRange(product.data);
|
||||
const targetWeight = quantity > 0 ? quantity : min;
|
||||
setSelectedWeight((prev) => (
|
||||
Math.abs(prev - targetWeight) < 0.0001 ? prev : targetWeight
|
||||
));
|
||||
setSelectedWeight((prev) => (Math.abs(prev - targetWeight) < 0.0001 ? prev : targetWeight));
|
||||
}, [product?.data, cartId, quantity]);
|
||||
|
||||
useEffect(() => () => {
|
||||
useEffect(
|
||||
() => () => {
|
||||
clearCartSyncTimer();
|
||||
}, [clearCartSyncTimer]);
|
||||
},
|
||||
[clearCartSyncTimer],
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-[400px]'>
|
||||
<p className='text-disabled-text'>در حال بارگذاری...</p>
|
||||
<div className="flex items-center justify-center min-h-100">
|
||||
<p className="text-disabled-text">در حال بارگذاری...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!product?.data) {
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-[400px]'>
|
||||
<p className='text-disabled-text'>کالا یافت نشد</p>
|
||||
<div className="flex items-center justify-center min-h-100">
|
||||
<p className="text-disabled-text">کالا یافت نشد</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const productData = product.data;
|
||||
const productName = productData.title || productData.name || '';
|
||||
const productImage = typeof productData.image === 'string'
|
||||
const productName = productData.title || productData.name || "";
|
||||
const productImage =
|
||||
typeof productData.image === "string"
|
||||
? productData.image
|
||||
: Array.isArray(productData.images) && productData.images.length > 0
|
||||
? typeof productData.images[0] === 'string'
|
||||
? typeof productData.images[0] === "string"
|
||||
? productData.images[0]
|
||||
: typeof productData.images[0] === 'object' && productData.images[0] !== null && 'url' in productData.images[0]
|
||||
: typeof productData.images[0] === "object" && productData.images[0] !== null && "url" in productData.images[0]
|
||||
? (productData.images[0] as { url: string }).url
|
||||
: '/assets/images/no-image.png'
|
||||
: '/assets/images/no-image.png';
|
||||
: "/assets/images/no-image.png"
|
||||
: "/assets/images/no-image.png";
|
||||
const categoryName = productData.category?.title;
|
||||
const variants = productData.variants ?? [];
|
||||
const hasVariants = Boolean(productData.attribute?.trim());
|
||||
const selectedVariant = selectedVariantId
|
||||
? variants.find((v) => v.id === selectedVariantId) ?? variants[0]
|
||||
: variants[0];
|
||||
const price = hasVariants
|
||||
? (selectedVariant?.price ?? 0)
|
||||
: (productData.price ?? selectedVariant?.price ?? 0);
|
||||
const selectedVariant = selectedVariantId ? (variants.find((v) => v.id === selectedVariantId) ?? variants[0]) : variants[0];
|
||||
const price = hasVariants ? (selectedVariant?.price ?? 0) : (productData.price ?? selectedVariant?.price ?? 0);
|
||||
const isVariable = isVariablePricing(productData);
|
||||
const weightRange = isVariable ? getVariableQuantityRange(productData) : null;
|
||||
const unitPriceLabel = isVariable
|
||||
? getPurchaseUnitLabel(productData.purchaseUnit)
|
||||
: '';
|
||||
const unitPriceLabel = isVariable ? getPurchaseUnitLabel(productData.purchaseUnit) : "";
|
||||
const calculatedPrice = isVariable ? Math.round(price * selectedWeight) : price;
|
||||
// const prepareTime = productData.prepareTime || 0;
|
||||
const content = Array.isArray(productData.content)
|
||||
? productData.content.join('، ')
|
||||
: productData.content || productData.desc || '';
|
||||
const content = Array.isArray(productData.content) ? productData.content.join("، ") : productData.content || productData.desc || "";
|
||||
|
||||
const handleBackToMenu = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const categoryParam = urlParams.get('category') || productData.category?.id;
|
||||
const categoryParam = urlParams.get("category") || productData.category?.id;
|
||||
if (categoryParam) {
|
||||
router.push(`/${name}?category=${categoryParam}`);
|
||||
} else {
|
||||
@@ -235,42 +227,24 @@ function ProductPage({ }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`lg:absolute lg:top-1/2 not-lg:max-w-lg not-lg:place-self-center lg:-translate-y-1/2 xl:right-[285px] lg:left-5 lg:right-4 lg:bottom-4 not-lg:w-full not-md:-translate-y-2 pt-6 flex flex-col lg:grid grid-cols-2 bottom-10`}>
|
||||
<div
|
||||
className={`lg:absolute lg:top-1/2 not-lg:max-w-lg not-lg:place-self-center lg:-translate-y-1/2 xl:right-71.25 lg:left-5 lg:right-4 lg:bottom-4 not-lg:w-full not-md:-translate-y-2 pt-6 flex flex-col lg:grid grid-cols-2 bottom-10`}
|
||||
>
|
||||
<div className="relative w-full h-full not-lg:bg-container rounded-2xl overflow-hidden lg:rounded-r-none lg:order-1">
|
||||
<Image
|
||||
className='w-full object-cover bg-[#F2F2F9] h-full'
|
||||
src={productImage}
|
||||
alt={productName}
|
||||
height={100}
|
||||
width={100}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
<Image className="w-full object-cover bg-[#F2F2F9] h-full" src={productImage} alt={productName} height={100} width={100} unoptimized priority />
|
||||
<div className="absolute top-4 left-0 pe-5.5 ps-7 w-full inline-flex justify-between items-center">
|
||||
<div className="bg-container whitespace-nowrap rounded-[34px] py-1.5 px-4 w-min text-xs text-disabled2 dark:text-disabled-text font-bold">
|
||||
{categoryName || '-'}
|
||||
</div>
|
||||
<button onClick={handleBackToMenu} className='p-2 rounded-full bg-container/40'>
|
||||
<ArrowLeft size={18} className='stroke-primary dark:stroke-foreground' />
|
||||
<div className="bg-container whitespace-nowrap rounded-[34px] py-1.5 px-4 w-min text-xs text-disabled2 dark:text-disabled-text font-bold">{categoryName || "-"}</div>
|
||||
<button onClick={handleBackToMenu} className="p-2 rounded-full bg-container/40">
|
||||
<ArrowLeft size={18} className="stroke-primary dark:stroke-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative px-6 pt-6 pb-7.5 lg:flex lg:flex-col lg:justify-center bg-container w-full rounded-3xl overflow-hidden not-lg:-translate-y-6 lg:rounded-l-none">
|
||||
<div className="w-full inline-flex justify-between items-center">
|
||||
<h5 className="text-base font-bold">
|
||||
{productName}
|
||||
</h5>
|
||||
<button
|
||||
onClick={handleToggleFavorite}
|
||||
className='p-2 bg-[#EAECF0] dark:bg-neutral-600 rounded-lg'
|
||||
>
|
||||
<Heart
|
||||
variant={isFavorite ? 'Bold' : 'Outline'}
|
||||
size={24}
|
||||
color='currentColor'
|
||||
className={isFavorite ? 'fill-primary dark:fill-foreground' : ''}
|
||||
/>
|
||||
<h5 className="text-base font-bold">{productName}</h5>
|
||||
<button onClick={handleToggleFavorite} className="p-2 bg-[#EAECF0] dark:bg-neutral-600 rounded-lg">
|
||||
<Heart variant={isFavorite ? "Bold" : "Outline"} size={24} color="currentColor" className={isFavorite ? "fill-primary dark:fill-foreground" : ""} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -281,24 +255,17 @@ function ProductPage({ }: Props) {
|
||||
زمان پخت و آماده سازی: {prepareTime > 0 ? `${ef(String(prepareTime))} دقیقه` : '-'}
|
||||
</span>
|
||||
</div> */}
|
||||
<div className='flex items-center gap-2 mt-2'>
|
||||
<Cup size={14} className='stroke-disabled-text' />
|
||||
<span className='text-disabled-text flex gap-1'>
|
||||
{about?.data?.plan === 'base' ? (
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<Cup size={14} className="stroke-disabled-text" />
|
||||
<span className="text-disabled-text flex gap-1">
|
||||
{about?.data?.plan === "base" ? (
|
||||
<>
|
||||
<div>۰</div>
|
||||
<span>امتیاز برای هر بار خرید</span>
|
||||
</>
|
||||
) : about?.data?.score?.purchaseScore && about?.data?.score?.purchaseAmount ? (
|
||||
<>
|
||||
<div>
|
||||
{ef(
|
||||
Math.round(
|
||||
price *
|
||||
(Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount))
|
||||
).toLocaleString('en-US')
|
||||
)}
|
||||
</div>
|
||||
<div>{ef(Math.round(price * (Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount))).toLocaleString("en-US"))}</div>
|
||||
<span>امتیاز برای هر بار خرید</span>
|
||||
</>
|
||||
) : (
|
||||
@@ -306,32 +273,27 @@ function ProductPage({ }: Props) {
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-2 mt-2'>
|
||||
<TruckTick size={14} className='stroke-disabled-text' />
|
||||
<span className='text-disabled-text'>
|
||||
{productData.pickupServe ? 'ارسال با پیک' : '-'}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<TruckTick size={14} className="stroke-disabled-text" />
|
||||
<span className="text-disabled-text">{productData.pickupServe ? "ارسال با پیک" : "-"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-7 text-xs">
|
||||
<p className='font-bold'>محتویات:</p>
|
||||
<p className='mt-2 leading-6'>{content || '-'}</p>
|
||||
<p className="font-bold">محتویات:</p>
|
||||
<p className="mt-2 leading-6">{content || "-"}</p>
|
||||
</div>
|
||||
|
||||
{hasVariants && variants.length > 0 && (
|
||||
<div className='mt-6 text-xs'>
|
||||
<p className='font-bold'>{productData.attribute}</p>
|
||||
<div className='flex flex-wrap gap-2 mt-2'>
|
||||
<div className="mt-6 text-xs">
|
||||
<p className="font-bold">{productData.attribute}</p>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
{variants.map((variant) => (
|
||||
<button
|
||||
key={variant.id}
|
||||
type="button"
|
||||
onClick={() => setSelectedVariantId(variant.id)}
|
||||
className={`px-3 py-1 rounded-full border text-xs ${variant.id === selectedVariant?.id
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'bg-background text-foreground border-border'
|
||||
}`}
|
||||
className={`px-3 py-1 rounded-full border text-xs ${variant.id === selectedVariant?.id ? "bg-primary text-white border-primary" : "bg-background text-foreground border-border"}`}
|
||||
>
|
||||
{variant.value}
|
||||
</button>
|
||||
@@ -341,14 +303,13 @@ function ProductPage({ }: Props) {
|
||||
)}
|
||||
|
||||
{isVariable && weightRange ? (
|
||||
<div className='mt-8'>
|
||||
<p className='text-sm font-bold'>
|
||||
قیمت هر {unitPriceLabel}{' '}
|
||||
<span dir='ltr'>T {ef(price.toLocaleString('en-US'))}</span>
|
||||
<div className="mt-8">
|
||||
<p className="text-sm font-bold">
|
||||
قیمت هر {unitPriceLabel} <span dir="ltr">T {ef(price.toLocaleString("en-US"))}</span>
|
||||
</p>
|
||||
|
||||
<VariableWeightSlider
|
||||
className='mt-10 mb-8'
|
||||
className="mt-10 mb-8"
|
||||
value={selectedWeight}
|
||||
min={weightRange.min}
|
||||
max={weightRange.max}
|
||||
@@ -358,43 +319,30 @@ function ProductPage({ }: Props) {
|
||||
onCommit={handleVariableWeightCommit}
|
||||
/>
|
||||
|
||||
<div className='flex items-center gap-3'>
|
||||
<div
|
||||
dir='ltr'
|
||||
className='flex-1 rounded-xl bg-[#EAECF0] dark:bg-neutral-700 px-4 py-3.5 text-sm font-semibold text-center'
|
||||
>
|
||||
{ef(calculatedPrice.toLocaleString('en-US'))} T
|
||||
<div className="flex items-center gap-3">
|
||||
<div dir="ltr" className="flex-1 rounded-xl bg-[#EAECF0] dark:bg-neutral-700 px-4 py-3.5 text-sm font-semibold text-center">
|
||||
{ef(calculatedPrice.toLocaleString("en-US"))} T
|
||||
</div>
|
||||
<button
|
||||
type='button'
|
||||
type="button"
|
||||
onClick={handleAddVariableToCart}
|
||||
disabled={isCartMutating || !cartId}
|
||||
className='flex-1 inline-flex items-center justify-center gap-2 rounded-xl bg-black text-white px-4 py-3.5 text-sm font-semibold disabled:opacity-60 disabled:cursor-not-allowed'
|
||||
className="flex-1 inline-flex items-center justify-center gap-2 rounded-xl bg-black text-white px-4 py-3.5 text-sm font-semibold disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isCartMutating ? (
|
||||
<span className="size-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
) : (
|
||||
<PlusIcon className='text-white' />
|
||||
)}
|
||||
<span>{quantity > 0 ? 'بروزرسانی' : 'افزودن'}</span>
|
||||
{isCartMutating ? <span className="size-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> : <PlusIcon className="text-white" />}
|
||||
<span>{quantity > 0 ? "بروزرسانی" : "افزودن"}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='mt-12 flex justify-between items-center'>
|
||||
<span dir='ltr'>{ef(price.toLocaleString('en-US'))} T</span>
|
||||
<CartQuantityControl
|
||||
quantity={quantity}
|
||||
onAdd={handleAddToCart}
|
||||
onRemove={handleRemoveFromCart}
|
||||
isMutating={isCartMutating}
|
||||
addDisabled={!cartId}
|
||||
/>
|
||||
<div className="mt-12 flex justify-between items-center">
|
||||
<span dir="ltr">{ef(price.toLocaleString("en-US"))} T</span>
|
||||
<CartQuantityControl quantity={quantity} onAdd={handleAddToCart} onRemove={handleRemoveFromCart} isMutating={isCartMutating} addDisabled={!cartId} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default ProductPage
|
||||
export default ProductPage;
|
||||
|
||||
@@ -106,9 +106,7 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu
|
||||
const isSpecialOffer = product.isSpecialOffer === true;
|
||||
|
||||
const specialOfferBadge = isSpecialOffer ? (
|
||||
<span className="inline-block rounded-md bg-primary text-primary-foreground text-[10px] font-medium px-1.5 py-0.5 leading-tight">
|
||||
پیشنهاد ویژه
|
||||
</span>
|
||||
<span className="inline-block rounded-md bg-primary text-primary-foreground text-[10px] font-medium px-1.5 py-0.5 leading-tight">پیشنهاد ویژه</span>
|
||||
) : null;
|
||||
|
||||
const quantityLabel = useMemo(() => {
|
||||
@@ -135,7 +133,7 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu
|
||||
return (
|
||||
<div className="relative flex flex-col w-full h-full">
|
||||
{specialOfferBadge && <div className="absolute top-0 left-0 z-10">{specialOfferBadge}</div>}
|
||||
<Link href={productDetailUrl} className="cursor-pointer rounded-xl aspect-square overflow-hidden flex items-center justify-center max-h-[100px]">
|
||||
<Link href={productDetailUrl} className="cursor-pointer rounded-xl aspect-square overflow-hidden flex items-center justify-center max-h-25">
|
||||
<img className="rounded-xl max-w-full max-h-full object-center" src={resolvedImage} alt={productName} />
|
||||
</Link>
|
||||
<Link href={productDetailUrl} className="cursor-pointer min-w-0 mt-4 flex items-center justify-between gap-2">
|
||||
@@ -211,14 +209,14 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu
|
||||
) : !readOnly ? (
|
||||
<div className="flex flex-col justify-end shrink-0">
|
||||
{showDetailsInsteadOfAdd ? (
|
||||
<div className="w-[115px]">
|
||||
<div className="w-28.75">
|
||||
<Link href={productDetailUrl} className={`${actionButtonClass} w-full`}>
|
||||
<span className={actionButtonLabelClass}>جزئیات</span>
|
||||
<ArrowLeft size={16} className="stroke-foreground" />
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-[115px]">
|
||||
<div className="w-28.75">
|
||||
<CartQuantityControl quantity={quantity} onAdd={handleAddToCart} onRemove={handleRemoveFromCart} isMutating={isCartMutating} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user