fast navigate + cache
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-06-03 15:08:09 +03:30
parent b9608cd486
commit 194533ec5f
27 changed files with 218 additions and 194 deletions
+173
View File
@@ -0,0 +1,173 @@
'use client';
import React from 'react';
import { useRouter } from 'next/navigation';
import { useTranslation } from 'react-i18next';
import { ArrowLeft, Trash } from 'iconsax-react';
import Image from 'next/image';
import { useCart } from './hook/useCart';
import CartSummary from './components/CartSummary';
import Prompt from '@/components/utils/Prompt';
import { useGetFoods } from '@/app/[name]/(Main)/hooks/useMenuData';
import MenuItem from '@/components/listview/MenuItem';
import MenuItemRenderer from '@/components/listview/MenuItemRenderer';
import { useGetAbout } from '../about/hooks/useAboutData';
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
import { useGetCartItems } from './hooks/useCartData';
import PagerModal from '@/components/pager/PagerModal';
import Button from '@/components/button/PrimaryButton';
import NotificationBellIcon from '@/components/icons/NotificationBellIcon';
import CartSkeleton from './components/CartSkeleton';
import {
cartItemToFood,
guestCartItemsToFoods,
hasCartEntries,
} from './lib/cartUtils';
const CartPage = () => {
const { t } = useTranslation('parallels', { keyPrefix: 'Cart' });
const router = useRouter();
const { isSuccess } = useGetProfile();
const { clearCart, items } = useCart();
const { data: cartData, isPending: cartPending } = useGetCartItems(isSuccess);
const { data: foodsResponse } = useGetFoods();
const { data: aboutData } = useGetAbout();
const [showClearConfirm, setShowClearConfirm] = React.useState(false);
const [showPagerModal, setShowPagerModal] = React.useState(false);
const isPremium = aboutData?.data?.plan === 'premium';
const cartFoods = React.useMemo(() => {
if (isSuccess) {
const apiItems =
cartData?.data?.items?.filter((item) => item.quantity > 0) ?? [];
return apiItems.map(cartItemToFood);
}
return guestCartItemsToFoods(items, foodsResponse?.data ?? []);
}, [isSuccess, cartData?.data?.items, items, foodsResponse?.data]);
const hasItems = React.useMemo(() => {
if (isSuccess) {
return (cartData?.data?.totalItems ?? 0) > 0 || hasCartEntries(items);
}
return hasCartEntries(items);
}, [isSuccess, cartData?.data?.totalItems, items]);
const guestAwaitingMenu =
!isSuccess && hasItems && cartFoods.length === 0 && !foodsResponse?.data;
const showInitialCartLoad = isSuccess && cartPending && !cartData && hasItems;
const isCartEmpty = !hasItems;
const handleClearCart = (e: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
clearCart();
setShowClearConfirm(false);
};
const toggleClearConfirm = (e: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
setShowClearConfirm(false);
};
return (
<div className='overflow-y-auto h-full noscrollbar flex flex-col bg-background'>
<div className='grid grid-cols-3 items-center'>
{isCartEmpty ? (
<span></span>
) : (
<Trash
className='cursor-pointer place-self-start'
size='24'
color='currentColor'
onClick={() => setShowClearConfirm(true)}
/>
)}
<h1 className='text-sm2 place-self-center font-medium'>{t('Heading')}</h1>
<ArrowLeft
className='cursor-pointer place-self-end'
size='24'
color='currentColor'
onClick={() => router.back()}
/>
</div>
<div className="mt-8 flex-1 h-full">
{showInitialCartLoad || guestAwaitingMenu ? (
<CartSkeleton />
) : isCartEmpty ? (
<div className='flex flex-col items-center justify-center gap-4 h-full'>
<Image
src='/assets/images/cart.png'
alt='cart'
width={120}
height={120}
className='object-contain'
/>
<div className='flex flex-col items-center gap-2 text-sm2 text-muted-foreground'>
<p>{t('EmptyStateTitle', { defaultValue: 'سبد خرید شما خالی است' })}</p>
<p className='text-xs'>
{t('EmptyStateDescription', {
defaultValue: 'برای افزودن غذا، به منوی رستوران برگردید.',
})}
</p>
</div>
</div>
) : (
<div className="flex flex-col gap-4 pb-24">
{!isPremium && (
<div className='bg-container rounded-container p-4 border border-border shadow-sm'>
<div className='flex items-start gap-3 mb-4'>
<div className='shrink-0 mt-0.5'>
<NotificationBellIcon width={24} height={24} className="currentColor" />
</div>
<div className='flex-1'>
<p className='text-sm font-medium text-foreground mb-1'>
ثبت سفارش
</p>
<p className='text-xs text-muted-foreground leading-5'>
برای ثبت سفارش، لطفاً گارسون را صدا کنید
</p>
</div>
</div>
<Button
onClick={() => setShowPagerModal(true)}
className='w-full dark:bg-white! dark:text-black! flex items-center justify-center gap-2'
>
<NotificationBellIcon width={18} height={18} className="text-white dark:text-black!" />
<span>صدا کردن گارسون</span>
</Button>
</div>
)}
{cartFoods.map((food) => (
<MenuItemRenderer key={food.id}>
<MenuItem food={food} />
</MenuItemRenderer>
))}
<CartSummary isPremium={isPremium} hasItems={hasItems} />
</div>
)}
</div>
<div className={showClearConfirm ? 'fixed inset-0 z-1001' : 'pointer-events-none fixed inset-0 z-1001'}>
<Prompt
title='پاک کردن سبد خرید'
description='آیا از پاک کردن تمامی آیتم‌های سبد خرید اطمینان دارید؟'
textConfirm='بله'
textCancel='خیر'
onConfirm={handleClearCart}
onCancel={toggleClearConfirm}
visible={showClearConfirm}
onClick={toggleClearConfirm}
/>
</div>
<PagerModal visible={showPagerModal} onClose={() => setShowPagerModal(false)} />
</div>
);
};
export default CartPage;