favorite list
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { getFavorites } from "../service/Service";
|
||||||
|
|
||||||
|
export const useGetFavorites = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["favorites"],
|
||||||
|
queryFn: getFavorites,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { ArrowLeft } from 'iconsax-react'
|
||||||
|
import { useGetFavorites } from './hooks/useFavoriteData'
|
||||||
|
import MenuItem from '@/components/listview/MenuItem'
|
||||||
|
import MenuItemRenderer from '@/components/listview/MenuItemRenderer'
|
||||||
|
import VerticalScrollView from '@/components/listview/VerticalScrollView'
|
||||||
|
import type { Favorite } from './types/Types'
|
||||||
|
import type { Food } from '@/app/[name]/(Main)/types/Types'
|
||||||
|
|
||||||
|
function FavoritePage() {
|
||||||
|
const router = useRouter()
|
||||||
|
const { data: favoritesData, isLoading } = useGetFavorites()
|
||||||
|
|
||||||
|
const favorites = useMemo(() => {
|
||||||
|
return favoritesData?.data || []
|
||||||
|
}, [favoritesData?.data])
|
||||||
|
|
||||||
|
const foodItems = useMemo(() => {
|
||||||
|
return favorites.map((favorite: Favorite) => {
|
||||||
|
const favoriteFood = favorite.food
|
||||||
|
// تبدیل FavoriteFood به Food برای استفاده در MenuItem
|
||||||
|
const food: Food = {
|
||||||
|
id: favoriteFood.id,
|
||||||
|
createdAt: favoriteFood.createdAt,
|
||||||
|
updatedAt: favoriteFood.updatedAt,
|
||||||
|
deletedAt: favoriteFood.deletedAt,
|
||||||
|
restaurant: favoriteFood.restaurant as unknown as Food['restaurant'],
|
||||||
|
category: favoriteFood.category as unknown as Food['category'],
|
||||||
|
inventory: {} as Food['inventory'],
|
||||||
|
title: favoriteFood.title,
|
||||||
|
desc: favoriteFood.desc,
|
||||||
|
content: favoriteFood.content,
|
||||||
|
price: favoriteFood.price,
|
||||||
|
order: favoriteFood.order,
|
||||||
|
prepareTime: favoriteFood.prepareTime,
|
||||||
|
weekDays: favoriteFood.weekDays,
|
||||||
|
mealTypes: favoriteFood.mealTypes,
|
||||||
|
isActive: favoriteFood.isActive,
|
||||||
|
images: favoriteFood.images,
|
||||||
|
inPlaceServe: favoriteFood.inPlaceServe,
|
||||||
|
pickupServe: favoriteFood.pickupServe,
|
||||||
|
score: favoriteFood.score,
|
||||||
|
discount: favoriteFood.discount,
|
||||||
|
isSpecialOffer: favoriteFood.isSpecialOffer,
|
||||||
|
reviews: [],
|
||||||
|
favorites: [],
|
||||||
|
isFavorite: true,
|
||||||
|
name: favoriteFood.title,
|
||||||
|
description: favoriteFood.desc,
|
||||||
|
}
|
||||||
|
return food
|
||||||
|
})
|
||||||
|
}, [favorites])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='overflow-y-auto h-full noscrollbar flex flex-col'>
|
||||||
|
<div className='grid grid-cols-3 items-center'>
|
||||||
|
<span></span>
|
||||||
|
<h1 className='text-sm2 place-self-center font-medium'>علاقهمندیها</h1>
|
||||||
|
<ArrowLeft
|
||||||
|
className='cursor-pointer place-self-end'
|
||||||
|
size='24'
|
||||||
|
color='currentColor'
|
||||||
|
onClick={() => { router.back() }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 flex-1 h-full">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className='flex flex-col items-center justify-center gap-2 py-12 text-sm2 text-muted-foreground'>
|
||||||
|
<span className='h-4 w-4 animate-spin rounded-full border border-dashed border-foreground border-t-transparent'></span>
|
||||||
|
<p>در حال دریافت علاقهمندیها...</p>
|
||||||
|
</div>
|
||||||
|
) : foodItems.length === 0 ? (
|
||||||
|
<div className='flex flex-col items-center justify-center gap-2 py-12 text-sm2 text-muted-foreground'>
|
||||||
|
<p>شما هیچ مورد علاقهمندی ثبت نکردهاید</p>
|
||||||
|
<p className='text-xs'>
|
||||||
|
برای افزودن غذا به علاقهمندیها، به صفحه جزئیات غذا بروید
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<VerticalScrollView className="overflow-y-auto h-full">
|
||||||
|
{foodItems.map((food) => (
|
||||||
|
<MenuItemRenderer key={food.id}>
|
||||||
|
<MenuItem food={food} />
|
||||||
|
</MenuItemRenderer>
|
||||||
|
))}
|
||||||
|
</VerticalScrollView>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FavoritePage
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { api } from "@/config/axios";
|
||||||
|
import { FavoritesResponse } from "../types/Types";
|
||||||
|
|
||||||
|
export const getFavorites = async (): Promise<FavoritesResponse> => {
|
||||||
|
const { data } = await api.get<FavoritesResponse>("/public/foods/favorite");
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { BaseResponse } from "@/app/[name]/(Main)/types/Types";
|
||||||
|
|
||||||
|
export type MealType = "breakfast" | "lunch" | "dinner" | "snack";
|
||||||
|
|
||||||
|
export interface FavoriteFood {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
restaurant: string;
|
||||||
|
category: string;
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
content: string[];
|
||||||
|
price: number;
|
||||||
|
order: number | null;
|
||||||
|
prepareTime: number | null;
|
||||||
|
weekDays: number[];
|
||||||
|
mealTypes: MealType[];
|
||||||
|
isActive: boolean;
|
||||||
|
images: string[];
|
||||||
|
inPlaceServe: boolean;
|
||||||
|
pickupServe: boolean;
|
||||||
|
score: number;
|
||||||
|
discount: number;
|
||||||
|
isSpecialOffer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Favorite {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
user: string;
|
||||||
|
food: FavoriteFood;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FavoritesResponse = BaseResponse<Favorite[]>;
|
||||||
|
|
||||||
@@ -7,18 +7,20 @@ import BottomNavHighlightLink from './BottomNavLinkBig'
|
|||||||
import PagerIcon from '../icons/PagerIcon'
|
import PagerIcon from '../icons/PagerIcon'
|
||||||
import BagIcon from '../icons/BagIcon'
|
import BagIcon from '../icons/BagIcon'
|
||||||
import HeartIcon from '../icons/HeartIcon'
|
import HeartIcon from '../icons/HeartIcon'
|
||||||
import { useParams, usePathname } from 'next/navigation'
|
import { useParams, usePathname, useRouter } from 'next/navigation'
|
||||||
import HomeIcon from '../icons/HomeIcon'
|
import HomeIcon from '../icons/HomeIcon'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart';
|
import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart';
|
||||||
import { Building } from 'iconsax-react';
|
import { Building } from 'iconsax-react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
||||||
|
|
||||||
type BottomNavBarProps = {
|
type BottomNavBarProps = {
|
||||||
onPagerClick?: () => void;
|
onPagerClick?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function BottomNavBar({ onPagerClick }: BottomNavBarProps) {
|
function BottomNavBar({ onPagerClick }: BottomNavBarProps) {
|
||||||
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { name } = params;
|
const { name } = params;
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -27,7 +29,8 @@ function BottomNavBar({ onPagerClick }: BottomNavBarProps) {
|
|||||||
const buildingVariant = React.useMemo(() => {
|
const buildingVariant = React.useMemo(() => {
|
||||||
return (isAboutRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
|
return (isAboutRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
|
||||||
}, [isAboutRoute]);
|
}, [isAboutRoute]);
|
||||||
|
const { isSuccess } = useGetProfile()
|
||||||
|
const router = useRouter()
|
||||||
const { t } = useTranslation('common', {
|
const { t } = useTranslation('common', {
|
||||||
keyPrefix: 'BottomNavbar'
|
keyPrefix: 'BottomNavbar'
|
||||||
});
|
});
|
||||||
@@ -39,6 +42,14 @@ function BottomNavBar({ onPagerClick }: BottomNavBarProps) {
|
|||||||
}, 0);
|
}, 0);
|
||||||
}, [items]);
|
}, [items]);
|
||||||
|
|
||||||
|
const handleFavoritesClick = () => {
|
||||||
|
if (isSuccess) {
|
||||||
|
router.push('/favorites');
|
||||||
|
} else {
|
||||||
|
router.push(`/${name}/auth`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-0 left-0 w-full bg-transparent pointer-events-none">
|
<div className="fixed bottom-0 left-0 w-full bg-transparent pointer-events-none">
|
||||||
<div className="max-w-md mx-auto w-full aspect-436/104 relative z-30">
|
<div className="max-w-md mx-auto w-full aspect-436/104 relative z-30">
|
||||||
@@ -111,7 +122,7 @@ function BottomNavBar({ onPagerClick }: BottomNavBarProps) {
|
|||||||
{t('about')}
|
{t('about')}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
<BottomNavLink href={'/auth'} icon={<HeartIcon width={20} height={20} />} value={t('Favorites')} />
|
<BottomNavLink href="#" onClick={handleFavoritesClick} icon={<HeartIcon width={20} height={20} />} value={t('Favorites')} />
|
||||||
</nav>
|
</nav>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user