align center and favorite
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import * as api from "../service/Service";
|
import * as api from "../service/Service";
|
||||||
|
|
||||||
export const useGetFood = (id: string) => {
|
export const useGetFood = (id: string) => {
|
||||||
@@ -7,3 +7,15 @@ export const useGetFood = (id: string) => {
|
|||||||
queryFn: () => api.getFood(id),
|
queryFn: () => api.getFood(id),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useAddToFavorite = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.addToFavorite,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveFromFavorite = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.removeFromFavorite,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import { motion } from 'framer-motion'
|
|||||||
import { ArrowLeft, Clock, Cup, Heart, TruckTick } from 'iconsax-react'
|
import { ArrowLeft, Clock, Cup, Heart, TruckTick } from 'iconsax-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useParams, useRouter } from 'next/navigation'
|
import { useParams, useRouter } from 'next/navigation'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { useGetFood } from './hooks/useFoodData'
|
import { useAddToFavorite, useGetFood, useRemoveFromFavorite } from './hooks/useFoodData'
|
||||||
import { useGetAbout } from '../about/hooks/useAboutData'
|
import { useGetAbout } from '../about/hooks/useAboutData'
|
||||||
|
import { useGetProfile } from '../../(Profile)/profile/hooks/userProfileData'
|
||||||
|
import { toast } from '@/components/Toast'
|
||||||
|
import { getToken } from '@/lib/api/func'
|
||||||
|
|
||||||
type Props = object
|
type Props = object
|
||||||
|
|
||||||
@@ -18,9 +21,13 @@ function FoodPage({ }: Props) {
|
|||||||
|
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { isSuccess } = useGetProfile();
|
||||||
const { data: food, isLoading } = useGetFood(id as string);
|
const { data: food, isLoading } = useGetFood(id as string);
|
||||||
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 { mutate: addToFavorite } = useAddToFavorite();
|
||||||
|
const { mutate: removeFromFavorite } = useRemoveFromFavorite();
|
||||||
|
|
||||||
const foodId = food?.data?.id || id as string;
|
const foodId = food?.data?.id || id as string;
|
||||||
const quantity = useMemo(() => {
|
const quantity = useMemo(() => {
|
||||||
@@ -43,6 +50,52 @@ function FoodPage({ }: Props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleFavorite = async () => {
|
||||||
|
if (!foodId) return;
|
||||||
|
|
||||||
|
const token = await getToken();
|
||||||
|
if (!token || !isSuccess) {
|
||||||
|
toast('ابتدا لاگین کنید', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// بهروزرسانی خوشبینانه: تغییر state قبل از ارسال درخواست
|
||||||
|
const previousFavorite = isFavorite;
|
||||||
|
setIsFavorite(!isFavorite);
|
||||||
|
|
||||||
|
if (previousFavorite) {
|
||||||
|
removeFromFavorite(foodId, {
|
||||||
|
onSuccess: () => {
|
||||||
|
// تایید تغییر در صورت موفقیت
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
// بازگرداندن به حالت قبلی در صورت خطا
|
||||||
|
setIsFavorite(previousFavorite);
|
||||||
|
toast('خطا در حذف از علاقهمندیها', 'error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addToFavorite(foodId, {
|
||||||
|
onSuccess: () => {
|
||||||
|
// تایید تغییر در صورت موفقیت
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
// بازگرداندن به حالت قبلی در صورت خطا
|
||||||
|
setIsFavorite(previousFavorite);
|
||||||
|
toast('خطا در افزودن به علاقهمندیها', 'error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (food?.data?.isFavorite) {
|
||||||
|
setIsFavorite(true);
|
||||||
|
} else {
|
||||||
|
setIsFavorite(false);
|
||||||
|
}
|
||||||
|
}, [food?.data?.isFavorite]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-center min-h-[400px]'>
|
<div className='flex items-center justify-center min-h-[400px]'>
|
||||||
@@ -104,8 +157,15 @@ function FoodPage({ }: Props) {
|
|||||||
<h5 className="text-base font-bold">
|
<h5 className="text-base font-bold">
|
||||||
{foodName}
|
{foodName}
|
||||||
</h5>
|
</h5>
|
||||||
<button className='p-2 bg-[#EAECF0] dark:bg-neutral-600 rounded-lg'>
|
<button
|
||||||
<Heart variant='Bold' size={24} className='fill-primary dark:fill-foreground' />
|
onClick={handleToggleFavorite}
|
||||||
|
className='p-2 bg-[#EAECF0] dark:bg-neutral-600 rounded-lg'
|
||||||
|
>
|
||||||
|
<Heart
|
||||||
|
variant={isFavorite ? 'Bold' : 'Outline'}
|
||||||
|
size={24}
|
||||||
|
className={isFavorite ? 'fill-primary dark:fill-foreground' : 'stroke-primary dark:stroke-foreground'}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,13 @@ export const getFood = async (id: string): Promise<FoodResponse> => {
|
|||||||
const { data } = await api.get<FoodResponse>(`/public/foods/${id}`);
|
const { data } = await api.get<FoodResponse>(`/public/foods/${id}`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addToFavorite = async (foodId: string) => {
|
||||||
|
const { data } = await api.post(`/public/foods/favorite/${foodId}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFromFavorite = async (foodId: string) => {
|
||||||
|
const { data } = await api.delete(`/public/foods/favorite/${foodId}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function TransactionsIndex() {
|
|||||||
کپی کد
|
کپی کد
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-14'>
|
<div className='w-14 h-full flex flex-1 justify-center items-center'>
|
||||||
<p className='text-sm2 transform -rotate-90 text-[#B2B2B2] font-medium' aria-label="کد تخفیف">
|
<p className='text-sm2 transform -rotate-90 text-[#B2B2B2] font-medium' aria-label="کد تخفیف">
|
||||||
{coupon.code}
|
{coupon.code}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -20,14 +20,24 @@ export type CategoriesResponse = BaseResponse<Category[]>;
|
|||||||
|
|
||||||
export type FoodImage = string | { url: string; alt?: string | null };
|
export type FoodImage = string | { url: string; alt?: string | null };
|
||||||
|
|
||||||
|
export interface RestaurantRef {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InventoryRef {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MealType = "breakfast" | "lunch" | "dinner" | "snack";
|
||||||
|
|
||||||
export interface Food {
|
export interface Food {
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
restaurant: string;
|
restaurant: RestaurantRef;
|
||||||
category: Category;
|
category: Category;
|
||||||
inventory: string;
|
inventory: InventoryRef;
|
||||||
title: string;
|
title: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
content: string[];
|
content: string[];
|
||||||
@@ -35,7 +45,7 @@ export interface Food {
|
|||||||
order: number | null;
|
order: number | null;
|
||||||
prepareTime: number | null;
|
prepareTime: number | null;
|
||||||
weekDays: number[];
|
weekDays: number[];
|
||||||
mealTypes: unknown[];
|
mealTypes: MealType[];
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
images: string[];
|
images: string[];
|
||||||
inPlaceServe: boolean;
|
inPlaceServe: boolean;
|
||||||
@@ -43,6 +53,9 @@ export interface Food {
|
|||||||
score: number;
|
score: number;
|
||||||
discount: number;
|
discount: number;
|
||||||
isSpecialOffer: boolean;
|
isSpecialOffer: boolean;
|
||||||
|
reviews: unknown[];
|
||||||
|
favorites: unknown[];
|
||||||
|
isFavorite: boolean;
|
||||||
// فیلدهای قدیمی برای سازگاری با سایر بخشهای کد
|
// فیلدهای قدیمی برای سازگاری با سایر بخشهای کد
|
||||||
name?: string;
|
name?: string;
|
||||||
foodName?: string;
|
foodName?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user