empty data

This commit is contained in:
hamid zarghami
2025-12-24 14:03:29 +03:30
parent 299839fd96
commit 41800bbee8
7 changed files with 177 additions and 139 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

@@ -60,51 +60,47 @@ const CartSummary = () => {
return (
<>
<div className='mt-2'>
<div className='relative'>
<h3 className='text-sm'>{t('InputDescription.Label')}</h3>
<textarea
className='w-full px-4 py-2.5 mt-4 text-sm2 leading-6 outline-0 border border-border rounded-normal resize-none focus:ring-0'
placeholder={t('InputDescription.Placeholder')}
id='description'
name='description'
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
<span className='absolute top-4 right-2 px-2 bg-background text-foreground text-xs'></span>
</div>
</div>
<div
className={`fixed bottom-0 left-0 right-0 z-50 bg-white border-t border-border p-4 w-full ${isCartEmpty ? 'pointer-events-none opacity-60' : ''}`}
>
<div className='flex justify-between items-center gap-4'>
<div className='flex flex-col'>
<div className='text-xs text-gray-400'>{t('PayableAmountLabel')}</div>
<div className='text-sm mt-2 font-semibold'>{formatPrice(totalPrice)} تومان</div>
{!isCartEmpty && (
<>
<div className='mt-2'>
<div className='relative'>
<h3 className='text-sm'>{t('InputDescription.Label')}</h3>
<textarea
className='w-full px-4 py-2.5 mt-4 text-sm2 leading-6 outline-0 border border-border rounded-normal resize-none focus:ring-0'
placeholder={t('InputDescription.Placeholder')}
id='description'
name='description'
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
<span className='absolute top-4 right-2 px-2 bg-background text-foreground text-xs'></span>
</div>
</div>
<Link
href={'order/checkout/0'}
onClick={(event) => {
if (isCartEmpty) {
event.preventDefault();
return;
}
if (!isSuccess) {
event.preventDefault();
const redirectUrl = encodeURIComponent(pathname);
router.push(`/${name}/auth?redirect=${redirectUrl}`);
return;
}
}}
>
<Button className='w-fit! px-10' disabled={isCartEmpty}>{t('ButtonCheckout')}</Button>
</Link>
<div className='fixed bottom-0 left-0 right-0 z-50 bg-white border-t border-border p-4 w-full'>
<div className='flex justify-between items-center gap-4'>
<div className='flex flex-col'>
<div className='text-xs text-gray-400'>{t('PayableAmountLabel')}</div>
<div className='text-sm mt-2 font-semibold'>{formatPrice(totalPrice)} تومان</div>
</div>
</div>
</div>
<Link
href={'order/checkout/0'}
onClick={(event) => {
if (!isSuccess) {
event.preventDefault();
const redirectUrl = encodeURIComponent(pathname);
router.push(`/${name}/auth?redirect=${redirectUrl}`);
return;
}
}}
>
<Button className='w-fit! px-10'>{t('ButtonCheckout')}</Button>
</Link>
</div>
</div>
</>
)}
{/* {!isCartEmpty && (
<div className='flex items-center justify-between rounded-xl border border-border px-4 py-3 text-xs text-muted-foreground'>
+64 -14
View File
@@ -4,22 +4,37 @@ 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 CartItemsList from './components/CartItemsList';
import CartSummary from './components/CartSummary';
import Prompt from '@/components/utils/Prompt';
import { useGetFoods } from '@/app/[name]/(Main)/hooks/useMenuData';
import { CartSummarySkeleton } from './components/CartSkeleton';
import VerticalScrollView from '@/components/listview/VerticalScrollView';
import MenuItem from '@/components/listview/MenuItem';
import MenuItemRenderer from '@/components/listview/MenuItemRenderer';
import type { Food } from '@/app/[name]/(Main)/types/Types';
const CartIndex = () => {
const { t } = useTranslation('parallels', { keyPrefix: 'Cart' });
const router = useRouter();
const { clearCart } = useCart();
const { clearCart, items } = useCart();
const [showClearConfirm, setShowClearConfirm] = React.useState(false);
const { data: foodsResponse, isFetching } = useGetFoods();
const foods = React.useMemo(() => foodsResponse?.data ?? [], [foodsResponse?.data]);
const isLoading = isFetching && !foods.length;
const cartFoods = React.useMemo(() => {
if (!foods.length) return [];
return Object.entries(items)
.filter(([, detail]) => detail?.quantity && detail.quantity > 0)
.map(([id]) =>
foods.find((foodItem) => String(foodItem.id) === String(id))
)
.filter((food): food is Food => Boolean(food));
}, [foods, items]);
const isCartEmpty = cartFoods.length === 0;
const handleClearCart = (e: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
@@ -34,14 +49,18 @@ const CartIndex = () => {
};
return (
<div className='bg-inherit flex flex-col h-full'>
<div className='grid grid-cols-3 items-center py-4'>
<Trash
className='cursor-pointer place-self-start'
size='24'
color='currentColor'
onClick={() => setShowClearConfirm(true)}
/>
<div className='overflow-y-auto h-full noscrollbar flex flex-col'>
<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'
@@ -51,9 +70,40 @@ const CartIndex = () => {
/>
</div>
<div className='flex overflow-y-auto noscrollbar flex-col h-full pt-4 gap-4 pb-24' dir='rtl'>
<CartItemsList />
{isLoading ? <CartSummarySkeleton /> : <CartSummary />}
<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>
) : 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>
) : (
<VerticalScrollView className="overflow-y-auto h-full pb-24">
{cartFoods.map((food) => (
<MenuItemRenderer key={food.id}>
<MenuItem food={food} />
</MenuItemRenderer>
))}
<CartSummary />
</VerticalScrollView>
)}
</div>
<div className={showClearConfirm ? 'fixed inset-0 z-1001' : 'pointer-events-none fixed inset-0 z-1001'}>
+60 -78
View File
@@ -4,6 +4,7 @@ import { ef } from '@/lib/helpers/utfNumbers';
import { ArrowLeft, Trash } from 'iconsax-react';
import { useRouter } from 'next/navigation';
import React from 'react'
import Image from 'next/image';
import { useTranslation } from 'react-i18next';
import { useGetNotifications, useDeleteNotification } from './hooks/useNotificationData';
import type { Notification } from './types/Types';
@@ -30,7 +31,7 @@ const formatTime = (dateString: string): string => {
export default function NotificationsIndex({ }: Props) {
const { t } = useTranslation('notifications')
const router = useRouter();
const { data, isLoading, isError } = useGetNotifications();
const { data, isLoading } = useGetNotifications();
const { mutate: deleteNotification } = useDeleteNotification();
const notifications = React.useMemo(() => {
@@ -41,50 +42,9 @@ export default function NotificationsIndex({ }: Props) {
deleteNotification(id);
};
if (isLoading) {
return (
<div className='h-full bg-inherit relative flex flex-col lg:gap-4'>
<div className='grid grid-cols-3 items-center py-4 '>
<span></span>
<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='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>
</div>
);
}
if (isError || !notifications.length) {
return (
<div className='h-full bg-inherit relative flex flex-col lg:gap-4'>
<div className='grid grid-cols-3 items-center py-4 '>
<span></span>
<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='flex flex-col items-center justify-center gap-2 py-12 text-sm2 text-muted-foreground'>
<p>اعلانی وجود ندارد</p>
</div>
</div>
);
}
return (
<div className='h-full bg-inherit relative flex flex-col lg:gap-4'>
<div className='grid grid-cols-3 items-center py-4 '>
<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'>{t("Heading")}</h1>
<ArrowLeft
@@ -95,42 +55,64 @@ export default function NotificationsIndex({ }: Props) {
/>
</div>
<ul className='flex flex-col gap-4 pb-20'>
{notifications.map((notification: Notification) => {
const isNew = !notification.sentAt;
const formattedDate = formatDate(notification.createdAt);
const formattedTime = formatTime(notification.createdAt);
<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>
) : notifications.length === 0 ? (
<div className='flex flex-col items-center justify-center gap-4 h-full'>
<Image
src='/assets/images/notification.png'
alt='notification'
width={120}
height={120}
className='object-contain'
/>
<div className='flex flex-col items-center gap-2 text-sm2 text-muted-foreground'>
<p>اعلانی وجود ندارد</p>
</div>
</div>
) : (
<ul className='flex flex-col gap-4 pb-20'>
{notifications.map((notification: Notification) => {
const isNew = !notification.sentAt;
const formattedDate = formatDate(notification.createdAt);
const formattedTime = formatTime(notification.createdAt);
return (
<li key={notification.id} className='bg-container py-5 px-4 rounded-container shadow-lg'>
<h5 className='font-medium text-sm'>{notification.title}</h5>
<p className='text-xs mt-2 leading-5'>{notification.content}</p>
return (
<li key={notification.id} className='bg-container py-5 px-4 rounded-container shadow-lg'>
<h5 className='font-medium text-sm'>{notification.title}</h5>
<p className='text-xs mt-2 leading-5'>{notification.content}</p>
<div className='flex items-center justify-between mt-5 '>
<div className="flex items-center justify-start gap-2">
{isNew && (
<span className='px-4 pt-1 pb-0.5 text-xs bg-primary text-white rounded-md'>
{t('Tags.New')}
</span>
)}
<span className='text-disabled2 text-xs pt-0.5'>
{ef(formattedDate)}
</span>
<span className='size-1.5 bg-disabled rounded-full'></span>
<span className='text-disabled2 text-xs pt-0.5'>
{ef(formattedTime)}
</span>
</div>
<Trash
size={24}
className='stroke-primary dark:stroke-foreground cursor-pointer'
onClick={() => handleDelete(notification.id)}
/>
</div>
</li>
);
})}
</ul>
<div className='flex items-center justify-between mt-5 '>
<div className="flex items-center justify-start gap-2">
{isNew && (
<span className='px-4 pt-1 pb-0.5 text-xs bg-primary text-white rounded-md'>
{t('Tags.New')}
</span>
)}
<span className='text-disabled2 text-xs pt-0.5'>
{ef(formattedDate)}
</span>
<span className='size-1.5 bg-disabled rounded-full'></span>
<span className='text-disabled2 text-xs pt-0.5'>
{ef(formattedTime)}
</span>
</div>
<Trash
size={24}
className='stroke-primary dark:stroke-foreground cursor-pointer'
onClick={() => handleDelete(notification.id)}
/>
</div>
</li>
);
})}
</ul>
)}
</div>
</div>
)
}
+15 -5
View File
@@ -2,6 +2,7 @@
import React, { useMemo } from 'react'
import { useRouter } from 'next/navigation'
import { ArrowLeft } from 'iconsax-react'
import Image from 'next/image'
import { useGetFavorites } from './hooks/useFavoriteData'
import MenuItem from '@/components/listview/MenuItem'
import MenuItemRenderer from '@/components/listview/MenuItemRenderer'
@@ -74,11 +75,20 @@ function FavoritePage() {
<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 className='flex flex-col items-center justify-center gap-4 h-full'>
<Image
src='/assets/images/favorite.png'
alt='favorite'
width={120}
height={120}
className='object-contain'
/>
<div className='flex flex-col items-center gap-2 text-sm2 text-muted-foreground'>
<p>شما هیچ مورد علاقهمندی ثبت نکردهاید</p>
<p className='text-xs'>
برای افزودن غذا به علاقهمندیها، به صفحه جزئیات غذا بروید
</p>
</div>
</div>
) : (
<VerticalScrollView className="overflow-y-auto h-full">