From bc3cb0714b9c96ea22d4eeea5e9b182719e8b342 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 22 Jul 2026 15:55:50 +0330 Subject: [PATCH] fix tailwind error --- src/app/[name]/(Main)/[id]/page.tsx | 676 +++++++++++++-------------- src/components/listview/MenuItem.tsx | 10 +- 2 files changed, 316 insertions(+), 370 deletions(-) diff --git a/src/app/[name]/(Main)/[id]/page.tsx b/src/app/[name]/(Main)/[id]/page.tsx index 6fed081..2e18644 100644 --- a/src/app/[name]/(Main)/[id]/page.tsx +++ b/src/app/[name]/(Main)/[id]/page.tsx @@ -1,400 +1,348 @@ -'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) { +function ProductPage({}: Props) { + const { id, name } = useParams(); + const router = useRouter(); + const { isSuccess } = useGetProfile(); + const { data: product, isLoading } = useGetProduct(id as string); + const { data: about } = useGetAbout(); + const { items, addToCart, removeFromCart, setCartQuantity, isCartMutating } = useCart(); + const [isFavorite, setIsFavorite] = useState(false); + const [selectedVariantId, setSelectedVariantId] = useState(null); + const [selectedWeight, setSelectedWeight] = useState(1); + const isUserInteractingRef = useRef(false); + const cartSyncTimerRef = useRef | null>(null); + const pendingCartWeightRef = useRef(null); + const { mutate: toggleFavorite } = useToggleFavorite(); - const { id, name } = useParams(); - const router = useRouter(); - const { isSuccess } = useGetProfile(); - const { data: product, isLoading } = useGetProduct(id as string); - const { data: about } = useGetAbout(); - const { items, addToCart, removeFromCart, setCartQuantity, isCartMutating } = useCart(); - const [isFavorite, setIsFavorite] = useState(false); - const [selectedVariantId, setSelectedVariantId] = useState(null); - const [selectedWeight, setSelectedWeight] = useState(1); - const isUserInteractingRef = useRef(false); - const cartSyncTimerRef = useRef | null>(null); - const pendingCartWeightRef = useRef(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 (!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, cartId]); - 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 (!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, cartId]); + const handleAddToCart = () => { + if (cartId) { + addToCart(cartId); + } + }; - const handleAddToCart = () => { - if (cartId) { - addToCart(cartId); - } - }; + const handleAddVariableToCart = () => { + if (!cartId) return; + if (cartSyncTimerRef.current) { + clearTimeout(cartSyncTimerRef.current); + cartSyncTimerRef.current = null; + } + pendingCartWeightRef.current = null; + isUserInteractingRef.current = false; + setCartQuantity(cartId, selectedWeight); + }; - const handleAddVariableToCart = () => { - if (!cartId) return; - if (cartSyncTimerRef.current) { - clearTimeout(cartSyncTimerRef.current); - cartSyncTimerRef.current = null; - } - pendingCartWeightRef.current = null; + const clearCartSyncTimer = useCallback(() => { + if (cartSyncTimerRef.current) { + clearTimeout(cartSyncTimerRef.current); + cartSyncTimerRef.current = null; + } + }, []); + + const handleVariableWeightChange = useCallback( + (weight: number) => { + isUserInteractingRef.current = true; + setSelectedWeight(weight); + clearCartSyncTimer(); + pendingCartWeightRef.current = null; + }, + [clearCartSyncTimer], + ); + + const handleVariableWeightCommit = useCallback( + (weight: number) => { + if (!isSuccess || !cartId || quantity <= 0) { isUserInteractingRef.current = false; - setCartQuantity(cartId, selectedWeight); - }; + return; + } - const clearCartSyncTimer = useCallback(() => { - if (cartSyncTimerRef.current) { - clearTimeout(cartSyncTimerRef.current); - cartSyncTimerRef.current = null; - } - }, []); + pendingCartWeightRef.current = weight; + clearCartSyncTimer(); - const handleVariableWeightChange = useCallback((weight: number) => { - isUserInteractingRef.current = true; - setSelectedWeight(weight); - clearCartSyncTimer(); + cartSyncTimerRef.current = setTimeout(() => { + cartSyncTimerRef.current = null; + const pendingWeight = pendingCartWeightRef.current; pendingCartWeightRef.current = null; - }, [clearCartSyncTimer]); - const handleVariableWeightCommit = useCallback((weight: number) => { - if (!isSuccess || !cartId || quantity <= 0) { - isUserInteractingRef.current = false; - return; + if (pendingWeight == null || !cartId) { + isUserInteractingRef.current = false; + return; } - pendingCartWeightRef.current = weight; - clearCartSyncTimer(); + const currentItem = items?.[cartId] ?? items?.[String(cartId)]; + const currentQty = currentItem && typeof currentItem === "object" && "quantity" in currentItem ? currentItem.quantity : 0; - cartSyncTimerRef.current = setTimeout(() => { - cartSyncTimerRef.current = null; - const pendingWeight = pendingCartWeightRef.current; - pendingCartWeightRef.current = null; - - if (pendingWeight == null || !cartId) { - isUserInteractingRef.current = false; - return; - } - - const currentItem = items?.[cartId] ?? items?.[String(cartId)]; - 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]); - - const handleRemoveFromCart = () => { - if (cartId) { - removeFromCart(cartId); + if (Math.abs(pendingWeight - currentQty) > 0.0001) { + setCartQuantity(cartId, pendingWeight); } - }; + isUserInteractingRef.current = false; + }, 1000); + }, + [isSuccess, cartId, quantity, clearCartSyncTimer, items, setCartQuantity], + ); - const handleToggleFavorite = async () => { - if (!productId) return; + const handleRemoveFromCart = () => { + if (cartId) { + removeFromCart(cartId); + } + }; - const token = await getToken(); - if (!token || !isSuccess) { - toast('ابتدا لاگین کنید', 'error'); - return; - } + const handleToggleFavorite = async () => { + if (!productId) return; - // به‌روزرسانی خوش‌بینانه: تغییر state قبل از ارسال درخواست - const previousFavorite = isFavorite; - setIsFavorite(!isFavorite); - - toggleFavorite(productId, { - onSuccess: () => { - // تایید تغییر در صورت موفقیت - }, - onError: () => { - // بازگرداندن به حالت قبلی در صورت خطا - setIsFavorite(previousFavorite); - toast('خطا در تغییر وضعیت علاقه‌مندی', 'error'); - }, - }); - }; - - useEffect(() => { - const typed = product as unknown as { data?: { isFavorite?: boolean } }; - const isFav = Boolean(typed?.data?.isFavorite); - setIsFavorite(isFav); - }, [product]); - - useEffect(() => { - if (!product?.data) return; - const variants = product.data.variants ?? []; - if (!variants.length) { - setSelectedVariantId(product.data.id); - return; - } - setSelectedVariantId((prev) => prev ?? variants[0].id); - }, [product?.data]); - - useEffect(() => { - if (!product?.data || !isVariablePricing(product.data) || !cartId) return; - if (isUserInteractingRef.current) return; - - const { min } = getVariableQuantityRange(product.data); - const targetWeight = quantity > 0 ? quantity : min; - setSelectedWeight((prev) => ( - Math.abs(prev - targetWeight) < 0.0001 ? prev : targetWeight - )); - }, [product?.data, cartId, quantity]); - - useEffect(() => () => { - clearCartSyncTimer(); - }, [clearCartSyncTimer]); - - if (isLoading) { - return ( -
-

در حال بارگذاری...

-
- ); + const token = await getToken(); + if (!token || !isSuccess) { + toast("ابتدا لاگین کنید", "error"); + return; } - if (!product?.data) { - return ( -
-

کالا یافت نشد

-
- ); + // به‌روزرسانی خوش‌بینانه: تغییر state قبل از ارسال درخواست + const previousFavorite = isFavorite; + setIsFavorite(!isFavorite); + + toggleFavorite(productId, { + onSuccess: () => { + // تایید تغییر در صورت موفقیت + }, + onError: () => { + // بازگرداندن به حالت قبلی در صورت خطا + setIsFavorite(previousFavorite); + toast("خطا در تغییر وضعیت علاقه‌مندی", "error"); + }, + }); + }; + + useEffect(() => { + const typed = product as unknown as { data?: { isFavorite?: boolean } }; + const isFav = Boolean(typed?.data?.isFavorite); + setIsFavorite(isFav); + }, [product]); + + useEffect(() => { + if (!product?.data) return; + const variants = product.data.variants ?? []; + if (!variants.length) { + setSelectedVariantId(product.data.id); + return; } + setSelectedVariantId((prev) => prev ?? variants[0].id); + }, [product?.data]); - const productData = product.data; - 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' - ? 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'; - 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 isVariable = isVariablePricing(productData); - const weightRange = isVariable ? getVariableQuantityRange(productData) : null; - 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 || ''; + useEffect(() => { + if (!product?.data || !isVariablePricing(product.data) || !cartId) return; + if (isUserInteractingRef.current) return; - const handleBackToMenu = () => { - const urlParams = new URLSearchParams(window.location.search); - const categoryParam = urlParams.get('category') || productData.category?.id; - if (categoryParam) { - router.push(`/${name}?category=${categoryParam}`); - } else { - router.push(`/${name}`); - } - }; + const { min } = getVariableQuantityRange(product.data); + const targetWeight = quantity > 0 ? quantity : min; + setSelectedWeight((prev) => (Math.abs(prev - targetWeight) < 0.0001 ? prev : targetWeight)); + }, [product?.data, cartId, quantity]); + useEffect( + () => () => { + clearCartSyncTimer(); + }, + [clearCartSyncTimer], + ); + + if (isLoading) { return ( -
-
- {productName} -
-
- {categoryName || '-'} -
- -
-
+
+

در حال بارگذاری...

+
+ ); + } -
-
-
- {productName} -
- -
+ if (!product?.data) { + return ( +
+

کالا یافت نشد

+
+ ); + } -
- {/*
+ const productData = product.data; + 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" + ? 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"; + 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 isVariable = isVariablePricing(productData); + const weightRange = isVariable ? getVariableQuantityRange(productData) : null; + 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 handleBackToMenu = () => { + const urlParams = new URLSearchParams(window.location.search); + const categoryParam = urlParams.get("category") || productData.category?.id; + if (categoryParam) { + router.push(`/${name}?category=${categoryParam}`); + } else { + router.push(`/${name}`); + } + }; + + return ( +
+
+ {productName} +
+
{categoryName || "-"}
+ +
+
+ +
+
+
{productName}
+ +
+ +
+ {/*
زمان پخت و آماده سازی: {prepareTime > 0 ? `${ef(String(prepareTime))} دقیقه` : '-'}
*/} -
- - - {about?.data?.plan === 'base' ? ( - <> -
۰
- امتیاز برای هر بار خرید - - ) : about?.data?.score?.purchaseScore && about?.data?.score?.purchaseAmount ? ( - <> -
- {ef( - Math.round( - price * - (Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount)) - ).toLocaleString('en-US') - )} -
- امتیاز برای هر بار خرید - - ) : ( - - - )} -
-
-
- - - {productData.pickupServe ? 'ارسال با پیک' : '-'} - -
-
- -
-

محتویات:

-

{content || '-'}

-
- - {hasVariants && variants.length > 0 && ( -
-

{productData.attribute}

-
- {variants.map((variant) => ( - - ))} -
-
- )} - - {isVariable && weightRange ? ( -
-

- قیمت هر {unitPriceLabel}{' '} - T {ef(price.toLocaleString('en-US'))} -

- - - -
-
- {ef(calculatedPrice.toLocaleString('en-US'))} T -
- -
-
- ) : ( -
- {ef(price.toLocaleString('en-US'))} T - -
- )} -
+
+ + + {about?.data?.plan === "base" ? ( + <> +
۰
+ امتیاز برای هر بار خرید + + ) : about?.data?.score?.purchaseScore && about?.data?.score?.purchaseAmount ? ( + <> +
{ef(Math.round(price * (Number(about.data.score.purchaseScore) / Number(about.data.score.purchaseAmount))).toLocaleString("en-US"))}
+ امتیاز برای هر بار خرید + + ) : ( + - + )} +
+
+
+ + {productData.pickupServe ? "ارسال با پیک" : "-"} +
- ) + +
+

محتویات:

+

{content || "-"}

+
+ + {hasVariants && variants.length > 0 && ( +
+

{productData.attribute}

+
+ {variants.map((variant) => ( + + ))} +
+
+ )} + + {isVariable && weightRange ? ( +
+

+ قیمت هر {unitPriceLabel} T {ef(price.toLocaleString("en-US"))} +

+ + + +
+
+ {ef(calculatedPrice.toLocaleString("en-US"))} T +
+ +
+
+ ) : ( +
+ {ef(price.toLocaleString("en-US"))} T + +
+ )} +
+
+ ); } -export default ProductPage +export default ProductPage; diff --git a/src/components/listview/MenuItem.tsx b/src/components/listview/MenuItem.tsx index 7ac8a63..3136f9c 100644 --- a/src/components/listview/MenuItem.tsx +++ b/src/components/listview/MenuItem.tsx @@ -106,9 +106,7 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu const isSpecialOffer = product.isSpecialOffer === true; const specialOfferBadge = isSpecialOffer ? ( - - پیشنهاد ویژه - + پیشنهاد ویژه ) : null; const quantityLabel = useMemo(() => { @@ -135,7 +133,7 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu return (
{specialOfferBadge &&
{specialOfferBadge}
} - + {productName} @@ -211,14 +209,14 @@ const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValu ) : !readOnly ? (
{showDetailsInsteadOfAdd ? ( -
+
جزئیات
) : ( -
+
)}