273 lines
10 KiB
TypeScript
273 lines
10 KiB
TypeScript
"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;
|