Files
dmenu-plus-front/src/app/[name]/(Main)/[id]/page.tsx
T
hamid zarghami 94e41da139
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
fix warning
2026-06-06 11:58:02 +03:30

273 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useCart } from "@/app/[name]/(Main)/cart/hook/useCart";
import MinusIcon from "@/components/icons/MinusIcon";
import PlusIcon from "@/components/icons/PlusIcon";
import { toast } from "@/components/Toast";
import { getToken } from "@/lib/api/func";
import { ef } from "@/lib/helpers/utfNumbers";
import { motion } from "framer-motion";
import { ArrowLeft, Clock, Cup, Heart, TruckTick } from "iconsax-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useGetProfile } from "../../(Profile)/profile/hooks/userProfileData";
import { useGetAbout } from "../about/hooks/useAboutData";
import { useGetFood, useToggleFavorite } from "./hooks/useFoodData";
type Props = object;
function FoodPage({}: Props) {
const { id, name } = useParams();
const router = useRouter();
const { isSuccess } = useGetProfile();
const { data: food, isLoading } = useGetFood(id as string);
const { data: about } = useGetAbout();
const { items, addToCart, removeFromCart } = useCart();
const [isFavorite, setIsFavorite] = useState<boolean>(false);
const { mutate: toggleFavorite } = useToggleFavorite();
const foodId = food?.data?.id || (id as string);
const quantity = useMemo(() => {
const item = items?.[foodId];
if (item && typeof item === "object" && "quantity" in item) {
return item.quantity || 0;
}
return 0;
}, [items, foodId]);
const handleAddToCart = () => {
if (foodId) {
addToCart(foodId);
}
};
const handleRemoveFromCart = () => {
if (foodId) {
removeFromCart(foodId);
}
};
const handleToggleFavorite = async () => {
if (!foodId) return;
const token = await getToken();
if (!token || !isSuccess) {
toast("ابتدا لاگین کنید", "error");
return;
}
// به‌روزرسانی خوش‌بینانه: تغییر state قبل از ارسال درخواست
const previousFavorite = isFavorite;
setIsFavorite(!isFavorite);
toggleFavorite(foodId, {
onSuccess: () => {
// تایید تغییر در صورت موفقیت
},
onError: () => {
// بازگرداندن به حالت قبلی در صورت خطا
setIsFavorite(previousFavorite);
toast("خطا در تغییر وضعیت علاقه‌مندی", "error");
},
});
};
useEffect(() => {
if (food?.data?.isFavorite) {
setIsFavorite(true);
} else {
setIsFavorite(false);
}
}, [food?.data?.isFavorite]);
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<p className="text-disabled-text">در حال بارگذاری...</p>
</div>
);
}
if (!food?.data) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<p className="text-disabled-text">غذا یافت نشد</p>
</div>
);
}
const foodData = food.data;
const foodName = foodData.title || foodData.name || foodData.foodName || "";
const foodImage =
typeof foodData.image === "string"
? foodData.image
: Array.isArray(foodData.images) && foodData.images.length > 0
? typeof foodData.images[0] === "string"
? foodData.images[0]
: typeof foodData.images[0] === "object" &&
foodData.images[0] !== null &&
"url" in foodData.images[0]
? (foodData.images[0] as { url: string }).url
: "/assets/images/no-image.png"
: "/assets/images/no-image.png";
const categoryName = foodData.category?.title;
const prepareTime = foodData.prepareTime || 0;
const price = foodData.price || 0;
const content = Array.isArray(foodData.content)
? foodData.content.join("، ")
: foodData.content || foodData.desc || "";
const handleBackToMenu = () => {
const urlParams = new URLSearchParams(window.location.search);
const categoryParam = urlParams.get("category") || foodData.category?.id;
if (categoryParam) {
router.push(`/${name}?category=${categoryParam}`);
} else {
router.push(`/${name}`);
}
};
return (
<div className="not-lg:max-w-lg not-lg:place-self-center not-lg:w-full not-lg:mb-10 pt-6 not-lg:flex not-lg:flex-col lg:fixed lg:z-10 lg:top-20 xl:top-22 lg:bottom-24 lg:left-5 lg:right-4 xl:right-[285px] lg:grid lg:grid-cols-2 lg:min-h-0 lg:overflow-hidden lg:rounded-2xl">
<div className="relative w-full lg:h-full min-h-0 not-lg:bg-container rounded-2xl overflow-hidden lg:rounded-r-none lg:order-1">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
className="lg:object-cover lg:h-full object-cover bg-[#F2F2F9] w-full"
src={foodImage}
alt={foodName}
onError={(event) => {
event.currentTarget.src = "/assets/images/no-image.png";
}}
/>
<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"
/>
</button>
</div>
</div>
<div className="relative px-6 pt-6 pb-7.5 lg:flex lg:flex-col lg:h-full lg:min-h-0 bg-container w-full rounded-3xl overflow-hidden not-lg:-translate-y-6 lg:rounded-l-none lg:overflow-y-auto">
<div className="w-full inline-flex justify-between items-center shrink-0">
<h5 className="text-base font-bold">{foodName}</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>
<div className="mt-4 lg:mt-0 lg:flex-1 lg:flex lg:flex-col lg:justify-center lg:min-h-0 lg:overflow-y-auto">
<div className="text-xs">
<div className="flex items-center gap-1">
<Clock size={14} className="stroke-disabled-text" />
<span className="text-disabled-text">
زمان پخت و آماده سازی:{" "}
{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>۰</div>
<span>امتیاز برای هر بار خرید</span>
</>
) : about?.data?.score?.purchaseScore &&
about?.data?.score?.purchaseAmount ? (
<>
<div>
{ef(
Math.round(
foodData?.price *
(Number(about.data.score.purchaseScore) /
Number(about.data.score.purchaseAmount)),
).toLocaleString("en-US"),
)}
</div>
<span>امتیاز برای هر بار خرید</span>
</>
) : (
<span>-</span>
)}
</span>
</div>
<div className="flex items-center gap-2 mt-2">
<TruckTick size={14} className="stroke-disabled-text" />
<span className="text-disabled-text">
{foodData.pickupServe ? "ارسال با پیک" : "-"}
</span>
</div>
</div>
<div className="mt-7 text-xs">
<p className="font-bold">محتویات:</p>
<p className="mt-2 leading-6">{content || "-"}</p>
</div>
</div>
<div className="mt-12 lg:mt-0 shrink-0 flex justify-between items-center">
<span dir="ltr">{ef(price.toLocaleString("en-US"))} T</span>
<motion.div
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"
>
{quantity <= 0 ? (
<button
onClick={handleAddToCart}
className="inline-flex w-full justify-center items-center gap-2"
>
<PlusIcon />
<div className="text-sm2 pt-0.5 font-normal text-foreground">
افزودن
</div>
</button>
) : (
<>
<button
onClick={handleAddToCart}
className="bg-container hover:bg-container/60 active:bg-container/30 rounded-sm p-2 transition-colors"
>
<PlusIcon className="text-foreground" />
</button>
<motion.div
key={quantity}
initial={{ scale: 1 }}
animate={{ scale: [1.2, 0.95, 1] }}
transition={{ duration: 0.3 }}
className="text-sm2 pt-0.5 font-semibold"
>
{quantity}
</motion.div>
<button
onClick={handleRemoveFromCart}
className="bg-container hover:bg-container/60 active:bg-container/30 rounded-sm p-2 transition-colors"
>
<MinusIcon className="text-foreground" />
</button>
</>
)}
</motion.div>
</div>
</div>
</div>
);
}
export default FoodPage;