diff --git a/src/app/[name]/(Dialogs)/cart/hook/useCart.ts b/src/app/[name]/(Dialogs)/cart/hook/useCart.ts index 2bd2095..13ceab1 100644 --- a/src/app/[name]/(Dialogs)/cart/hook/useCart.ts +++ b/src/app/[name]/(Dialogs)/cart/hook/useCart.ts @@ -14,8 +14,8 @@ export const useCart = () => { const params = useParams(); const currentSlug = (params.name as string) || null; const { clear, increment, decrement, items, slug, setSlug } = useReceiptStore(); - const { mutate: mutateIncrementCart } = useIncrementCart(); - const { mutate: mutateDecrementCart } = useDecrementCart(); + const { mutate: mutateIncrementCart, isPending: isIncrementPending } = useIncrementCart(); + const { mutate: mutateDecrementCart, isPending: isDecrementPending } = useDecrementCart(); const { mutate: mutateClearCart } = useClearCart(); const { data: cartItems } = useGetCartItems(isSuccess); @@ -53,7 +53,7 @@ export const useCart = () => { return {}; } return cartItems.data.items.reduce((acc, item) => { - acc[item.productId] = { quantity: item.quantity }; + acc[item.variantId] = { quantity: item.quantity }; return acc; }, {} as Record); } else { @@ -61,9 +61,7 @@ export const useCart = () => { } }, [isSuccess, cartItems?.data?.items, items]); - const isItemLoading = () => { - return false; - }; + const isCartMutating = isIncrementPending || isDecrementPending; // برای کاربران لاگین شده، slug از API می‌آید (در cartItems.data.shopId) // برای کاربران لاگین نشده، slug از receiptStore می‌آید @@ -83,6 +81,6 @@ export const useCart = () => { addToCart, removeFromCart, clearCart, - isItemLoading, + isCartMutating, }; }; diff --git a/src/app/[name]/(Dialogs)/cart/hooks/useCartData.ts b/src/app/[name]/(Dialogs)/cart/hooks/useCartData.ts index 71e6e7a..abe0c71 100644 --- a/src/app/[name]/(Dialogs)/cart/hooks/useCartData.ts +++ b/src/app/[name]/(Dialogs)/cart/hooks/useCartData.ts @@ -29,7 +29,7 @@ export const useIncrementCart = () => { if (previousCart?.data && productsData?.data) { const existingItem = previousCart.data.items.find( - (item) => item.productId === String(id) + (item) => item.variantId === String(id) ); let updatedItems: CartItem[]; @@ -37,7 +37,7 @@ export const useIncrementCart = () => { if (existingItem) { updatedItems = previousCart.data.items.map((item) => { - if (item.productId === String(id)) { + if (item.variantId === String(id)) { return { ...item, quantity: item.quantity + 1, @@ -58,7 +58,7 @@ export const useIncrementCart = () => { const price = variant.price ?? effectivePrice; const discount = product.discount ?? 0; const newItem: CartItem = { - productId: variantId, + variantId, productTitle: product.title || product.name || "", quantity: 1, price, @@ -124,7 +124,7 @@ export const useDecrementCart = () => { if (previousCart?.data) { const updatedItems = previousCart.data.items .map((item: CartItem) => { - if (item.productId === String(id)) { + if (item.variantId === String(id)) { const newQuantity = item.quantity - 1; if (newQuantity <= 0) { return null; diff --git a/src/app/[name]/(Dialogs)/cart/service/CartService.ts b/src/app/[name]/(Dialogs)/cart/service/CartService.ts index fabd421..abc52ff 100644 --- a/src/app/[name]/(Dialogs)/cart/service/CartService.ts +++ b/src/app/[name]/(Dialogs)/cart/service/CartService.ts @@ -2,7 +2,7 @@ import { api } from "@/config/axios"; import { BulkCartItem, CartResponse, SaveAllMethod } from "../types/Types"; export const bulkCart = async (params: BulkCartItem) => { - const { data } = await api.post("/public/cart/items/bulk", params); + const { data } = await api.post("/public/cart/variant/bulk", params); return data; }; diff --git a/src/app/[name]/(Dialogs)/cart/types/Types.ts b/src/app/[name]/(Dialogs)/cart/types/Types.ts index 251c6e8..d2256be 100644 --- a/src/app/[name]/(Dialogs)/cart/types/Types.ts +++ b/src/app/[name]/(Dialogs)/cart/types/Types.ts @@ -8,8 +8,7 @@ export type BulkCartItem = { }; export interface CartItem { - /** شناسه واریانت (در API ممکن است هنوز productId نامیده شود) */ - productId: string; + variantId: string; productTitle: string; quantity: number; price: number; diff --git a/src/app/[name]/(Main)/[id]/page.tsx b/src/app/[name]/(Main)/[id]/page.tsx index c7d6abe..8a74d63 100644 --- a/src/app/[name]/(Main)/[id]/page.tsx +++ b/src/app/[name]/(Main)/[id]/page.tsx @@ -1,10 +1,9 @@ 'use client' -import MinusIcon from '@/components/icons/MinusIcon' -import PlusIcon from '@/components/icons/PlusIcon' +import { CartQuantityControl } from '@/components/CartQuantityControl' import { ef } from '@/lib/helpers/utfNumbers' import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart' -import { motion } from 'framer-motion' +import { getPrimaryVariantId } from '@/app/[name]/(Main)/types/Types' import { ArrowLeft, Clock, Cup, Heart, TruckTick } from 'iconsax-react' import Image from 'next/image' import { useParams, useRouter } from 'next/navigation' @@ -24,30 +23,34 @@ function ProductPage({ }: Props) { const { isSuccess } = useGetProfile(); const { data: product, isLoading } = useGetProduct(id as string); const { data: about } = useGetAbout(); - const { items, addToCart, removeFromCart } = useCart(); + const { items, addToCart, removeFromCart, isCartMutating } = useCart(); const [isFavorite, setIsFavorite] = useState(false); const [selectedVariantId, setSelectedVariantId] = useState(null); const { mutate: toggleFavorite } = useToggleFavorite(); const productId = product?.data?.id || id as string; + const cartId = useMemo(() => { + if (!product?.data) return null; + return selectedVariantId ?? getPrimaryVariantId(product.data) ?? productId; + }, [product?.data, selectedVariantId, productId]); const quantity = useMemo(() => { - if (!selectedVariantId) return 0; - const item = items?.[selectedVariantId]; + if (!cartId) return 0; + const item = items?.[cartId] ?? items?.[String(cartId)]; if (item && typeof item === 'object' && 'quantity' in item) { return item.quantity || 0; } return 0; - }, [items, selectedVariantId]); + }, [items, cartId]); const handleAddToCart = () => { - if (selectedVariantId) { - addToCart(selectedVariantId); + if (cartId) { + addToCart(cartId); } }; const handleRemoveFromCart = () => { - if (selectedVariantId) { - removeFromCart(selectedVariantId); + if (cartId) { + removeFromCart(cartId); } }; @@ -86,7 +89,7 @@ function ProductPage({ }: Props) { if (!product?.data) return; const variants = product.data.variants ?? []; if (!variants.length) { - setSelectedVariantId(null); + setSelectedVariantId(product.data.id); return; } setSelectedVariantId((prev) => prev ?? variants[0].id); @@ -252,46 +255,13 @@ function ProductPage({ }: Props) {
{ef(price.toLocaleString('en-US'))} T - - {quantity <= 0 ? ( - - ) : ( - <> - - - {quantity} - - - - )} - +
diff --git a/src/components/CartQuantityControl.tsx b/src/components/CartQuantityControl.tsx new file mode 100644 index 0000000..1380a54 --- /dev/null +++ b/src/components/CartQuantityControl.tsx @@ -0,0 +1,79 @@ +'use client' + +import { motion } from 'framer-motion' +import MinusIcon from '@/components/icons/MinusIcon' +import PlusIcon from '@/components/icons/PlusIcon' + +export interface CartQuantityControlProps { + quantity: number + onAdd: () => void + onRemove: () => void + /** در حال ارسال درخواست به سرور (نمایش اسپینر به‌جای آیکون) */ + isMutating?: boolean + /** غیرفعال کردن دکمه افزودن (مثلاً وقتی variant انتخاب نشده) */ + addDisabled?: boolean + /** متن دکمه وقتی quantity صفر است */ + addLabel?: string + /** متن دکمه در حالت loading */ + addingLabel?: string +} + +const Spinner = () => ( + +) + +export function CartQuantityControl({ + quantity, + onAdd, + onRemove, + isMutating = false, + addDisabled = false, + addLabel = 'افزودن', + addingLabel = 'در حال افزودن...', +}: CartQuantityControlProps) { + return ( + + {quantity <= 0 ? ( + + ) : ( + <> + + + {quantity} + + + + )} + + ) +} diff --git a/src/components/listview/MenuItem.tsx b/src/components/listview/MenuItem.tsx index 7154d67..c385580 100644 --- a/src/components/listview/MenuItem.tsx +++ b/src/components/listview/MenuItem.tsx @@ -4,10 +4,8 @@ import { memo, useMemo } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; -import PlusIcon from "@/components/icons/PlusIcon"; -import MinusIcon from "@/components/icons/MinusIcon"; +import { CartQuantityControl } from "@/components/CartQuantityControl"; import { useCart } from "@/app/[name]/(Dialogs)/cart/hook/useCart"; -import { motion } from "framer-motion"; import type { Product, ProductImage } from "@/app/[name]/(Main)/types/Types"; import { hasVariants, @@ -22,7 +20,7 @@ interface MenuItemProps { } const MenuItem = ({ product, variantId: variantIdProp }: MenuItemProps) => { - const { items, addToCart, removeFromCart } = useCart(); + const { items, addToCart, removeFromCart, isCartMutating } = useCart(); const params = useParams<{ name: string }>(); const name = params?.name || ""; const variantId = variantIdProp ?? getPrimaryVariantId(product); @@ -163,46 +161,12 @@ const MenuItem = ({ product, variantId: variantIdProp }: MenuItemProps) => { جزئیات ) : ( - - {quantity <= 0 ? ( - - ) : ( - <> - - - {quantity} - - - - )} - + )} diff --git a/src/config/const.ts b/src/config/const.ts index 9990790..87073ab 100644 --- a/src/config/const.ts +++ b/src/config/const.ts @@ -1,6 +1,5 @@ // export const API_BASE_URL = "https://dmenuplus-api.dev.danakcorp.com"; -// export const API_BASE_URL = -// "https://dmenuplus-api-production.dev.danakcorp.com"; -export const API_BASE_URL = "http://10.86.60.88:4000"; +// export const API_BASE_URL = "https://dkala-api.danakcorp.com"; +export const API_BASE_URL = "http://192.168.99.209:4000"; export const TOKEN_NAME = "dmenu-t"; export const REFRESH_TOKEN_NAME = "dmenu-rt";