base variant

This commit is contained in:
hamid zarghami
2026-02-10 12:47:00 +03:30
parent 6996c4fd21
commit d1c8338c00
10 changed files with 283 additions and 147 deletions
@@ -23,20 +23,40 @@ const CartSummary = ({ isPremium }: CartSummaryProps) => {
const name = params.name as string; const name = params.name as string;
const { isSuccess } = useGetProfile(); const { isSuccess } = useGetProfile();
const { data: productsResponse } = useGetProducts(); const { data: productsResponse } = useGetProducts();
const products = React.useMemo(() => productsResponse?.data ?? [], [productsResponse?.data]); const products = React.useMemo(
() => productsResponse?.data ?? [],
[productsResponse?.data]
);
const { items } = useCart(); const { items } = useCart();
const { data: cartData } = useGetCartItems(isSuccess); const { data: cartData } = useGetCartItems(isSuccess);
const { t } = useTranslation('parallels', { keyPrefix: 'Cart' }); const { t } = useTranslation("parallels", { keyPrefix: "Cart" });
const { description, setDescription } = useCartStore(); const { description, setDescription } = useCartStore();
const cartProducts = React.useMemo(() => { const cartEntries = React.useMemo(() => {
if (!products.length) return []; if (products.length === 0) return [];
return Object.entries(items)
const result: { product: Product; variantId: string; quantity: number }[] =
[];
Object.entries(items)
.filter(([, detail]) => detail?.quantity && detail.quantity > 0) .filter(([, detail]) => detail?.quantity && detail.quantity > 0)
.map(([id]) => .forEach(([variantId, detail]) => {
products.find((productItem) => String(productItem.id) === String(id)) const product = products.find((productItem) =>
productItem.variants?.some(
(variant) => String(variant.id) === String(variantId)
) )
.filter((product): product is Product => Boolean(product)); );
if (!product) return;
result.push({
product,
variantId: String(variantId),
quantity: detail.quantity,
});
});
return result;
}, [products, items]); }, [products, items]);
const totalPrice = React.useMemo(() => { const totalPrice = React.useMemo(() => {
@@ -45,17 +65,19 @@ const CartSummary = ({ isPremium }: CartSummaryProps) => {
return cartData.data.total; return cartData.data.total;
} }
// در غیر این صورت، محاسبه محلی با در نظر گرفتن تخفیف // در غیر این صورت، محاسبه محلی با در نظر گرفتن تخفیف و تنوع‌ها
return cartProducts.reduce((sum, product) => { return cartEntries.reduce((sum, entry) => {
const qty = items[String(product.id)]?.quantity ?? 0; const variant = entry.product.variants?.find(
const basePrice = product.price ?? 0; (v) => String(v.id) === entry.variantId
const discount = product.discount ?? 0; );
const basePrice = variant?.price ?? entry.product.price ?? 0;
const discount = entry.product.discount ?? 0;
const priceAfterDiscount = basePrice * (1 - discount / 100); const priceAfterDiscount = basePrice * (1 - discount / 100);
return sum + priceAfterDiscount * qty; return sum + priceAfterDiscount * entry.quantity;
}, 0); }, 0);
}, [cartProducts, items, isSuccess, cartData?.data?.total]); }, [cartEntries, isSuccess, cartData?.data?.total]);
const isCartEmpty = cartProducts.length === 0; const isCartEmpty = cartEntries.length === 0;
const formatPrice = React.useCallback( const formatPrice = React.useCallback(
(value: number) => value.toLocaleString('fa-IR'), (value: number) => value.toLocaleString('fa-IR'),
@@ -96,7 +118,7 @@ const CartSummary = ({ isPremium }: CartSummaryProps) => {
{isPremium && ( {isPremium && (
<Link <Link
href={'order/checkout/0'} href={`/${name}/order/checkout/0`}
onClick={(event) => { onClick={(event) => {
if (!isSuccess) { if (!isSuccess) {
event.preventDefault(); event.preventDefault();
@@ -106,7 +128,9 @@ const CartSummary = ({ isPremium }: CartSummaryProps) => {
} }
}} }}
> >
<Button className='w-fit! px-10 dark:bg-white dark:text-black! dark:hover:bg-gray-100'>{t('ButtonCheckout')}</Button> <Button className='w-fit! px-10 dark:bg-white dark:text-black! dark:hover:bg-gray-100'>
{t('ButtonCheckout')}
</Button>
</Link> </Link>
)} )}
{!isPremium && ( {!isPremium && (
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/CartService"; import * as api from "../service/CartService";
import type { CartResponse, CartItem } from "../types/Types"; import type { CartResponse, CartItem } from "../types/Types";
import type { ProductsResponse } from "@/app/[name]/(Main)/types/Types"; import type { ProductsResponse } from "@/app/[name]/(Main)/types/Types";
import { getProductEffectivePrice } from "@/app/[name]/(Main)/types/Types";
export const useBulkCart = () => { export const useBulkCart = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -47,15 +48,22 @@ export const useIncrementCart = () => {
}); });
newTotalItems = previousCart.data.totalItems + 1; newTotalItems = previousCart.data.totalItems + 1;
} else { } else {
const product = productsData.data.find((f) => String(f.id) === String(id)); const variantId = String(id);
if (product) { const product = productsData.data.find((p) =>
p.variants?.some((v) => v.id === variantId)
);
const variant = product?.variants?.find((v) => v.id === variantId);
if (product && variant) {
const effectivePrice = getProductEffectivePrice(product);
const price = variant.price ?? effectivePrice;
const discount = product.discount ?? 0;
const newItem: CartItem = { const newItem: CartItem = {
productId: String(id), productId: variantId,
productTitle: product.title || product.name || "", productTitle: product.title || product.name || "",
quantity: 1, quantity: 1,
price: product.price, price,
discount: product.discount || 0, discount,
totalPrice: product.price * (1 - (product.discount || 0) / 100), totalPrice: price * (1 - discount / 100),
}; };
updatedItems = [...previousCart.data.items, newItem]; updatedItems = [...previousCart.data.items, newItem];
newTotalItems = previousCart.data.totalItems + 1; newTotalItems = previousCart.data.totalItems + 1;
+10 -7
View File
@@ -26,10 +26,13 @@ const CartIndex = () => {
if (!products.length) return []; if (!products.length) return [];
return Object.entries(items) return Object.entries(items)
.filter(([, detail]) => detail?.quantity && detail.quantity > 0) .filter(([, detail]) => detail?.quantity && detail.quantity > 0)
.map(([id]) => .map(([variantId]) => {
products.find((productItem) => String(productItem.id) === String(id)) const product = products.find((p) =>
) p.variants?.some((v) => v.id === variantId)
.filter((product): product is Product => Boolean(product)); );
return product ? { product, variantId } : null;
})
.filter((x): x is { product: Product; variantId: string } => Boolean(x));
}, [products, items]); }, [products, items]);
const isCartEmpty = cartProducts.length === 0; const isCartEmpty = cartProducts.length === 0;
@@ -95,9 +98,9 @@ const CartIndex = () => {
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-4 pb-24"> <div className="flex flex-col gap-4 pb-24">
{cartProducts.map((product) => ( {cartProducts.map(({ product, variantId }) => (
<MenuItemRenderer key={product.id}> <MenuItemRenderer key={variantId}>
<MenuItem product={product} /> <MenuItem product={product} variantId={variantId} />
</MenuItemRenderer> </MenuItemRenderer>
))} ))}
<CartSummary isPremium /> <CartSummary isPremium />
@@ -7,12 +7,12 @@ export const bulkCart = async (params: BulkCartItem) => {
}; };
export const increment = async (id: string | number) => { export const increment = async (id: string | number) => {
const { data } = await api.post(`/public/cart/product/${id}/increment`); const { data } = await api.post(`/public/cart/variant/${id}/increment`);
return data; return data;
}; };
export const decrement = async (id: string | number) => { export const decrement = async (id: string | number) => {
const { data } = await api.post(`/public/cart/product/${id}/decrement`); const { data } = await api.post(`/public/cart/variant/${id}/decrement`);
return data; return data;
}; };
+2 -1
View File
@@ -2,12 +2,13 @@ import { BaseResponse } from "@/app/[name]/(Main)/types/Types";
export type BulkCartItem = { export type BulkCartItem = {
items: { items: {
productId: string; variantId: string;
quantity: number; quantity: number;
}[]; }[];
}; };
export interface CartItem { export interface CartItem {
/** شناسه واریانت (در API ممکن است هنوز productId نامیده شود) */
productId: string; productId: string;
productTitle: string; productTitle: string;
quantity: number; quantity: number;
+53 -14
View File
@@ -26,26 +26,28 @@ function ProductPage({ }: Props) {
const { data: about } = useGetAbout(); const { data: about } = useGetAbout();
const { items, addToCart, removeFromCart } = useCart(); const { items, addToCart, removeFromCart } = useCart();
const [isFavorite, setIsFavorite] = useState<boolean>(false); const [isFavorite, setIsFavorite] = useState<boolean>(false);
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(null);
const { mutate: toggleFavorite } = useToggleFavorite(); const { mutate: toggleFavorite } = useToggleFavorite();
const productId = product?.data?.id || id as string; const productId = product?.data?.id || id as string;
const quantity = useMemo(() => { const quantity = useMemo(() => {
const item = items?.[productId]; if (!selectedVariantId) return 0;
const item = items?.[selectedVariantId];
if (item && typeof item === 'object' && 'quantity' in item) { if (item && typeof item === 'object' && 'quantity' in item) {
return item.quantity || 0; return item.quantity || 0;
} }
return 0; return 0;
}, [items, productId]); }, [items, selectedVariantId]);
const handleAddToCart = () => { const handleAddToCart = () => {
if (productId) { if (selectedVariantId) {
addToCart(productId); addToCart(selectedVariantId);
} }
}; };
const handleRemoveFromCart = () => { const handleRemoveFromCart = () => {
if (productId) { if (selectedVariantId) {
removeFromCart(productId); removeFromCart(selectedVariantId);
} }
}; };
@@ -75,12 +77,20 @@ function ProductPage({ }: Props) {
}; };
useEffect(() => { useEffect(() => {
if (product?.data?.isFavorite) { const typed = product as unknown as { data?: { isFavorite?: boolean } };
setIsFavorite(true); const isFav = Boolean(typed?.data?.isFavorite);
} else { setIsFavorite(isFav);
setIsFavorite(false); }, [product]);
useEffect(() => {
if (!product?.data) return;
const variants = product.data.variants ?? [];
if (!variants.length) {
setSelectedVariantId(null);
return;
} }
}, [product?.data?.isFavorite]); setSelectedVariantId((prev) => prev ?? variants[0].id);
}, [product?.data]);
if (isLoading) { if (isLoading) {
return ( return (
@@ -99,7 +109,7 @@ function ProductPage({ }: Props) {
} }
const productData = product.data; const productData = product.data;
const productName = productData.title || productData.name || productData.foodName || productData.productName || ''; const productName = productData.title || productData.name || '';
const productImage = typeof productData.image === 'string' const productImage = typeof productData.image === 'string'
? productData.image ? productData.image
: Array.isArray(productData.images) && productData.images.length > 0 : Array.isArray(productData.images) && productData.images.length > 0
@@ -110,8 +120,15 @@ function ProductPage({ }: Props) {
: '/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 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 prepareTime = productData.prepareTime || 0; const prepareTime = productData.prepareTime || 0;
const price = productData.price || 0;
const content = Array.isArray(productData.content) const content = Array.isArray(productData.content)
? productData.content.join('، ') ? productData.content.join('، ')
: productData.content || productData.desc || ''; : productData.content || productData.desc || '';
@@ -186,7 +203,7 @@ function ProductPage({ }: Props) {
<div> <div>
{ef( {ef(
Math.round( Math.round(
productData?.price * price *
(Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount)) (Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount))
).toLocaleString('en-US') ).toLocaleString('en-US')
)} )}
@@ -211,6 +228,28 @@ function ProductPage({ }: Props) {
<p className='mt-2 leading-6'>{content || '-'}</p> <p className='mt-2 leading-6'>{content || '-'}</p>
</div> </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'>
{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'
}`}
>
{variant.value}
</button>
))}
</div>
</div>
)}
<div className='mt-12 flex justify-between items-center'> <div className='mt-12 flex justify-between items-center'>
<span dir='ltr'>{ef(price.toLocaleString('en-US'))} T</span> <span dir='ltr'>{ef(price.toLocaleString('en-US'))} T</span>
<motion.div <motion.div
+50 -32
View File
@@ -20,64 +20,82 @@ export type CategoriesResponse = BaseResponse<Category[]>;
export type ProductImage = string | { url: string; alt?: string | null }; export type ProductImage = string | { url: string; alt?: string | null };
export interface ShopRef { /** Category as nested in product list API */
export interface ProductCategory {
id: string; id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
parent: string | null;
title: string;
isActive: boolean;
shop: string;
avatarUrl: string;
order: number;
} }
export interface InventoryRef { export interface ProductVariant {
id: string; id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
product: string;
value: string;
price: number;
} }
export type MealType = "breakfast" | "lunch" | "dinner" | "snack";
export interface Product { export interface Product {
id: string; id: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
restaurant: ShopRef; shop: string;
category: Category; category: ProductCategory;
inventory: InventoryRef; attribute: string;
title: string; title: string;
desc: string; desc: string;
content: string[]; order: number;
price: number;
order: number | null;
prepareTime: number | null;
weekDays: number[];
mealTypes: MealType[];
isActive: boolean; isActive: boolean;
images: string[]; images: string[];
inPlaceServe: boolean; score: number | null;
pickupServe: boolean;
score: number;
discount: number; discount: number;
isSpecialOffer: boolean; isSpecialOffer: boolean;
reviews: unknown[]; price: number | null;
favorites: unknown[]; variants: ProductVariant[];
isFavorite: boolean; /** Backward compatibility / other APIs */
// فیلدهای قدیمی برای سازگاری با API
name?: string; name?: string;
foodName?: string;
productName?: string;
description?: string; description?: string;
content?: string[] | string | null;
image?: string | null; image?: string | null;
inPlaceServe?: boolean;
pickupServe?: boolean;
points?: number | null; points?: number | null;
sat?: boolean; rate?: number | null;
sun?: boolean; prepareTime?: number | null;
mon?: boolean; isFavorite?: boolean;
breakfast?: boolean;
noon?: boolean;
dinner?: boolean;
stock?: number;
stockDefault?: number;
rate?: number;
[key: string]: unknown;
} }
export type ProductsResponse = BaseResponse<Product[]>; export type ProductsResponse = BaseResponse<Product[]>;
export type ProductResponse = BaseResponse<Product>; export type ProductResponse = BaseResponse<Product>;
/** محصول بدون تنوع: attribute خالی → قیمت از اولین واریانت */
export function hasVariants(product: Product): boolean {
return Boolean(product.attribute?.trim());
}
/** شناسه‌ای که برای سبد/API استفاده می‌شود: همیشه variantId (اگر بدون تنوع، همان واریانت اول) */
export function getPrimaryVariantId(product: Product): string | null {
const first = product.variants?.[0];
return first?.id ?? null;
}
/** قیمت مؤثر: بدون تنوع = قیمت واریانت اول، با تنوع = product.price یا اولین واریانت */
export function getProductEffectivePrice(product: Product): number {
if (product.price != null && product.price > 0) return product.price;
const first = product.variants?.[0];
return first?.price ?? 0;
}
export interface NotificationsCountData { export interface NotificationsCountData {
count: number; count: number;
} }
+26 -10
View File
@@ -8,7 +8,7 @@ import MenuItem from '@/components/listview/MenuItem'
import MenuItemRenderer from '@/components/listview/MenuItemRenderer' import MenuItemRenderer from '@/components/listview/MenuItemRenderer'
import VerticalScrollView from '@/components/listview/VerticalScrollView' import VerticalScrollView from '@/components/listview/VerticalScrollView'
import type { Favorite } from './types/Types' import type { Favorite } from './types/Types'
import type { Product } from '@/app/[name]/(Main)/types/Types' import type { Product, ProductCategory, ProductVariant } from '@/app/[name]/(Main)/types/Types'
function FavoritePage() { function FavoritePage() {
const router = useRouter() const router = useRouter()
@@ -22,22 +22,40 @@ function FavoritePage() {
return favorites.map((favorite: Favorite) => { return favorites.map((favorite: Favorite) => {
const favProduct = favorite.food const favProduct = favorite.food
// تبدیل FavoriteProduct به Product برای استفاده در MenuItem // تبدیل FavoriteProduct به Product برای استفاده در MenuItem
const category: ProductCategory = {
id: favProduct.category,
createdAt: '',
updatedAt: '',
deletedAt: null,
parent: null,
title: '',
isActive: true,
shop: '',
avatarUrl: '',
order: 0,
}
const singleVariant: ProductVariant = {
id: favProduct.id,
createdAt: favProduct.createdAt,
updatedAt: favProduct.updatedAt,
deletedAt: favProduct.deletedAt,
product: favProduct.id,
value: '',
price: favProduct.price,
}
const product: Product = { const product: Product = {
id: favProduct.id, id: favProduct.id,
createdAt: favProduct.createdAt, createdAt: favProduct.createdAt,
updatedAt: favProduct.updatedAt, updatedAt: favProduct.updatedAt,
deletedAt: favProduct.deletedAt, deletedAt: favProduct.deletedAt,
restaurant: favProduct.restaurant as unknown as Product['restaurant'], shop: favProduct.restaurant,
category: favProduct.category as unknown as Product['category'], category,
inventory: {} as Product['inventory'], attribute: '',
title: favProduct.title, title: favProduct.title,
desc: favProduct.desc, desc: favProduct.desc,
content: favProduct.content, content: favProduct.content,
price: favProduct.price, price: favProduct.price,
order: favProduct.order, order: favProduct.order,
prepareTime: favProduct.prepareTime,
weekDays: favProduct.weekDays,
mealTypes: favProduct.mealTypes,
isActive: favProduct.isActive, isActive: favProduct.isActive,
images: favProduct.images, images: favProduct.images,
inPlaceServe: favProduct.inPlaceServe, inPlaceServe: favProduct.inPlaceServe,
@@ -45,11 +63,9 @@ function FavoritePage() {
score: favProduct.score, score: favProduct.score,
discount: favProduct.discount, discount: favProduct.discount,
isSpecialOffer: favProduct.isSpecialOffer, isSpecialOffer: favProduct.isSpecialOffer,
reviews: [],
favorites: [],
isFavorite: true,
name: favProduct.title, name: favProduct.title,
description: favProduct.desc, description: favProduct.desc,
variants: [singleVariant],
} }
return product return product
}) })
+44 -17
View File
@@ -9,23 +9,38 @@ import MinusIcon from "@/components/icons/MinusIcon";
import { useCart } from "@/app/[name]/(Dialogs)/cart/hook/useCart"; import { useCart } from "@/app/[name]/(Dialogs)/cart/hook/useCart";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import type { Product, ProductImage } from "@/app/[name]/(Main)/types/Types"; import type { Product, ProductImage } from "@/app/[name]/(Main)/types/Types";
import {
hasVariants,
getPrimaryVariantId,
getProductEffectivePrice,
} from "@/app/[name]/(Main)/types/Types";
interface MenuItemProps { interface MenuItemProps {
product: Product; product: Product;
/** در صفحه سبد هر خط با variantId مشخص می‌شود */
variantId?: string | null;
} }
const MenuItem = ({ product }: MenuItemProps) => { const MenuItem = ({ product, variantId: variantIdProp }: MenuItemProps) => {
const { items, addToCart, removeFromCart } = useCart(); const { items, addToCart, removeFromCart } = useCart();
const params = useParams<{ name: string }>(); const params = useParams<{ name: string }>();
const name = params?.name || ''; const name = params?.name || "";
const variantId = variantIdProp ?? getPrimaryVariantId(product);
const selectedVariant = variantId
? product.variants?.find((v) => v.id === variantId)
: undefined;
const withVariants = hasVariants(product);
/** در لیست منو اگر تنوع داشت دکمه جزئیات؛ در سبد همیشه افزودن/کم کردن */
const showDetailsInsteadOfAdd = withVariants && variantIdProp == null;
const quantity = useMemo(() => { const quantity = useMemo(() => {
const item = items?.[product.id]; if (!variantId) return 0;
if (item && typeof item === 'object' && 'quantity' in item) { const item = items?.[variantId];
if (item && typeof item === "object" && "quantity" in item) {
return item.quantity || 0; return item.quantity || 0;
} }
return 0; return 0;
}, [items, product.id]); }, [items, variantId]);
const fallbackImage = "/assets/images/no-image.png"; const fallbackImage = "/assets/images/no-image.png";
const resolvedImage = useMemo(() => { const resolvedImage = useMemo(() => {
@@ -40,8 +55,8 @@ const MenuItem = ({ product }: MenuItemProps) => {
}, [product.image, product.images]); }, [product.image, product.images]);
const productName = useMemo( const productName = useMemo(
() => product.name || product.title || product.foodName || product.productName || 'بدون نام', () => product.name || product.title || 'بدون نام',
[product.name, product.title, product.foodName, product.productName] [product.name, product.title]
); );
const productContent = useMemo(() => { const productContent = useMemo(() => {
@@ -61,23 +76,27 @@ const MenuItem = ({ product }: MenuItemProps) => {
return desc.replace(/,/g, '،'); return desc.replace(/,/g, '،');
}, [product.description, product.desc]); }, [product.description, product.desc]);
const effectivePrice =
selectedVariant != null
? selectedVariant.price
: getProductEffectivePrice(product);
const finalPrice = useMemo(() => { const finalPrice = useMemo(() => {
const basePrice = product.price || 0;
const discount = product.discount || 0; const discount = product.discount || 0;
if (discount > 0) { if (discount > 0) {
return Math.max(0, basePrice - discount); return Math.max(0, effectivePrice - discount);
} }
return basePrice; return effectivePrice;
}, [product.price, product.discount]); }, [effectivePrice, product.discount]);
const formattedPrice = useMemo( const formattedPrice = useMemo(
() => (finalPrice ? finalPrice.toLocaleString('fa-IR') : '0'), () => (finalPrice ? finalPrice.toLocaleString("fa-IR") : "0"),
[finalPrice] [finalPrice]
); );
const formattedOriginalPrice = useMemo( const formattedOriginalPrice = useMemo(
() => (product.price ? product.price.toLocaleString('fa-IR') : '0'), () =>
[product.price] effectivePrice ? effectivePrice.toLocaleString("fa-IR") : "0",
[effectivePrice]
); );
const hasDiscount = useMemo( const hasDiscount = useMemo(
@@ -86,11 +105,11 @@ const MenuItem = ({ product }: MenuItemProps) => {
); );
const handleAddToCart = () => { const handleAddToCart = () => {
addToCart(product.id); if (variantId) addToCart(variantId);
}; };
const handleRemoveFromCart = () => { const handleRemoveFromCart = () => {
removeFromCart(product.id); if (variantId) removeFromCart(variantId);
}; };
const productDetailUrl = `/${name}/${product.id}?category=${product.category.id}`; const productDetailUrl = `/${name}/${product.id}?category=${product.category.id}`;
@@ -136,6 +155,14 @@ const MenuItem = ({ product }: MenuItemProps) => {
</span> </span>
)} )}
</div> </div>
{showDetailsInsteadOfAdd ? (
<Link
href={productDetailUrl}
className="bg-background active:drop-shadow-xs max-w-[115px] rounded-md w-full h-8 inline-flex justify-center items-center gap-2 px-2 text-sm2 font-normal text-foreground"
>
جزئیات
</Link>
) : (
<motion.div <motion.div
whileTap={{ scale: 1.05 }} whileTap={{ scale: 1.05 }}
className="bg-background active:drop-shadow-xs max-w-[115px] rounded-md w-full h-8 inline-flex p-1 justify-between items-center gap-2 relative overflow-hidden" className="bg-background active:drop-shadow-xs max-w-[115px] rounded-md w-full h-8 inline-flex p-1 justify-between items-center gap-2 relative overflow-hidden"
@@ -144,7 +171,6 @@ const MenuItem = ({ product }: MenuItemProps) => {
<button <button
onClick={handleAddToCart} onClick={handleAddToCart}
className="inline-flex w-full justify-center items-center gap-2" className="inline-flex w-full justify-center items-center gap-2"
> >
<PlusIcon /> <PlusIcon />
<div className="text-sm2 pt-0.5 font-normal text-foreground"> <div className="text-sm2 pt-0.5 font-normal text-foreground">
@@ -177,6 +203,7 @@ const MenuItem = ({ product }: MenuItemProps) => {
</> </>
)} )}
</motion.div> </motion.div>
)}
</div> </div>
</div> </div>
</div> </div>
+3 -3
View File
@@ -52,15 +52,15 @@ const StepOtp = ({ phone, slug }: Props) => {
setRefreshToken(data?.data?.tokens?.refreshToken?.token as string) setRefreshToken(data?.data?.tokens?.refreshToken?.token as string)
if (Object.keys(items).length > 0) { if (Object.keys(items).length > 0) {
const bulkCartItems = Object.entries(items).map(([key, value]) => { const bulkCartItems = Object.entries(items).map(([variantId, value]) => {
if (value.quantity > 0) { if (value.quantity > 0) {
return { return {
productId: key, variantId,
quantity: value.quantity quantity: value.quantity
} }
} }
return undefined return undefined
}).filter((item): item is { productId: string; quantity: number } => item !== undefined) }).filter((item): item is { variantId: string; quantity: number } => item !== undefined)
if (!!bulkCartItems.length) { if (!!bulkCartItems.length) {
mutateBulkCart({ items: bulkCartItems }, { mutateBulkCart({ items: bulkCartItems }, {
onSuccess: () => { onSuccess: () => {