detail of order
This commit is contained in:
+195
-82
@@ -1,11 +1,16 @@
|
|||||||
import Select from '@/components/Select'
|
import Select from '@/components/Select'
|
||||||
import Table from '@/components/Table'
|
import Table from '@/components/Table'
|
||||||
import { Location } from 'iconsax-react'
|
import { Location } from 'iconsax-react'
|
||||||
import { type FC } from 'react'
|
import { type FC, useMemo } from 'react'
|
||||||
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
|
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
|
||||||
import AppImage from '@/assets/images/appimage.png'
|
import { useGetOrderById } from './hooks/useGetOrders'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { formatPrice, formatOptionalDate, formatFaNumber } from '@/helpers/func'
|
||||||
|
import { OrderStatus, PaymentStatusEnum } from './enum/Enum'
|
||||||
|
import type { OrderItem as OrderItemType } from './types/Types'
|
||||||
|
|
||||||
interface OrderItem extends RowDataType {
|
interface OrderItemTable extends RowDataType {
|
||||||
image: string
|
image: string
|
||||||
title: string
|
title: string
|
||||||
quantity: number
|
quantity: number
|
||||||
@@ -13,27 +18,104 @@ interface OrderItem extends RowDataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const OrderDetails: FC = () => {
|
const OrderDetails: FC = () => {
|
||||||
const orderItems: OrderItem[] = [
|
const { id } = useParams()
|
||||||
{
|
const { data: orderData, isLoading } = useGetOrderById(id!)
|
||||||
id: 1,
|
const { t } = useTranslation('global')
|
||||||
image: AppImage,
|
|
||||||
title: 'پیتزا پپرونی',
|
|
||||||
quantity: 1,
|
|
||||||
amount: '۴۲,۰۰۰ تومان'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const columns: ColumnType<OrderItem>[] = [
|
const order = orderData?.data
|
||||||
|
|
||||||
|
const statusItems = useMemo(() => {
|
||||||
|
const statuses: string[] = [
|
||||||
|
OrderStatus.Pending,
|
||||||
|
OrderStatus.Confirmed,
|
||||||
|
OrderStatus.Preparing,
|
||||||
|
OrderStatus.RejectedByRestaurant,
|
||||||
|
OrderStatus.CancelledByUser,
|
||||||
|
OrderStatus.CancelledBySystem,
|
||||||
|
OrderStatus.ReadyForCustomerPickup,
|
||||||
|
OrderStatus.ReadyForDineIn,
|
||||||
|
OrderStatus.ReadyForDeliveryCar,
|
||||||
|
OrderStatus.ReadyForDeliveryCourier,
|
||||||
|
OrderStatus.Delivered,
|
||||||
|
]
|
||||||
|
return statuses.map((status) => ({
|
||||||
|
value: status,
|
||||||
|
label: t(`order_status.${status}`)
|
||||||
|
}))
|
||||||
|
}, [t])
|
||||||
|
|
||||||
|
const getStatusColor = (status: string): string => {
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
'pending': '#FFEECC',
|
||||||
|
'confirmed': '#E3F2FD',
|
||||||
|
'preparing': '#FFF3E0',
|
||||||
|
'rejectedByRestaurant': '#FFEBEE',
|
||||||
|
'cancelledByUser': '#FFEBEE',
|
||||||
|
'cancelledBySystem': '#FFEBEE',
|
||||||
|
'readyForCustomerPickup': '#E3F2FD',
|
||||||
|
'readyForDineIn': '#E3F2FD',
|
||||||
|
'readyForDeliveryCar': '#E3F2FD',
|
||||||
|
'readyForDeliveryCourier': '#E3F2FD',
|
||||||
|
'delivered': '#E8F5E9',
|
||||||
|
}
|
||||||
|
return colorMap[status] || '#FFEECC'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPaymentStatusColor = (status: string): string => {
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
[PaymentStatusEnum.Paid]: 'bg-green-500',
|
||||||
|
[PaymentStatusEnum.Pending]: 'bg-yellow-500',
|
||||||
|
[PaymentStatusEnum.Failed]: 'bg-red-500',
|
||||||
|
}
|
||||||
|
return colorMap[status] || 'bg-gray-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPaymentStatusTextColor = (status: string): string => {
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
[PaymentStatusEnum.Paid]: 'text-green-500',
|
||||||
|
[PaymentStatusEnum.Pending]: 'text-yellow-500',
|
||||||
|
[PaymentStatusEnum.Failed]: 'text-red-500',
|
||||||
|
}
|
||||||
|
return colorMap[status] || 'text-gray-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPaymentStatusText = (status: string): string => {
|
||||||
|
const textMap: Record<string, string> = {
|
||||||
|
[PaymentStatusEnum.Paid]: 'پرداخت شده',
|
||||||
|
[PaymentStatusEnum.Pending]: 'در انتظار پرداخت',
|
||||||
|
[PaymentStatusEnum.Failed]: 'پرداخت ناموفق',
|
||||||
|
}
|
||||||
|
return textMap[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderItems: OrderItemTable[] = useMemo(() => {
|
||||||
|
if (!order?.items) return []
|
||||||
|
return order.items.map((item: OrderItemType) => ({
|
||||||
|
id: item.id,
|
||||||
|
image: item.food?.images?.[0] || '',
|
||||||
|
title: item.food?.title || '-',
|
||||||
|
quantity: item.quantity,
|
||||||
|
amount: formatPrice(item.totalPrice)
|
||||||
|
}))
|
||||||
|
}, [order])
|
||||||
|
|
||||||
|
const columns: ColumnType<OrderItemTable>[] = [
|
||||||
{
|
{
|
||||||
title: 'تصویر',
|
title: 'تصویر',
|
||||||
key: 'image',
|
key: 'image',
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<div className=''>
|
<div>
|
||||||
<img
|
{item.image ? (
|
||||||
src={item.image}
|
<img
|
||||||
alt={item.title}
|
src={item.image}
|
||||||
className='w-12 h-12 rounded-lg'
|
alt={item.title}
|
||||||
/>
|
className='w-12 h-12 rounded-lg object-cover'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='w-12 h-12 rounded-lg bg-gray-200 flex items-center justify-center'>
|
||||||
|
<span className='text-xs text-gray-400'>بدون تصویر</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -45,7 +127,8 @@ const OrderDetails: FC = () => {
|
|||||||
{
|
{
|
||||||
title: 'تعداد',
|
title: 'تعداد',
|
||||||
key: 'quantity',
|
key: 'quantity',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (item) => formatFaNumber(item.quantity)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'مبلغ',
|
title: 'مبلغ',
|
||||||
@@ -53,15 +136,39 @@ const OrderDetails: FC = () => {
|
|||||||
align: 'right'
|
align: 'right'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className='mt-5 flex items-center justify-center h-64'>
|
||||||
|
<div className='text-lg font-light'>در حال بارگذاری...</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!order) {
|
||||||
|
return (
|
||||||
|
<div className='mt-5 flex items-center justify-center h-64'>
|
||||||
|
<div className='text-lg font-light'>سفارش یافت نشد</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const customerName = [order.user?.firstName, order.user?.lastName].filter(Boolean).join(' ') || '-'
|
||||||
|
const address = order.address ? `${order.address.city || ''} - ${order.address.address || ''}`.trim() : '-'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
<div className='flex justify-between items-center'>
|
<div className='flex justify-between items-center'>
|
||||||
<h1 className='text-lg font-light'> سفارش ۱۲۳۴۵</h1>
|
<h1 className='text-lg font-light'>
|
||||||
|
سفارش {formatFaNumber(order.orderNumber)}
|
||||||
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<Select
|
||||||
items={[]}
|
items={statusItems}
|
||||||
placeholder='درحال آماده سازی'
|
value={order.status}
|
||||||
className='w-[200px] bg-[#FFEECC]'
|
placeholder={t(`order_status.${order.status}`)}
|
||||||
|
className='w-[200px]'
|
||||||
|
style={{ backgroundColor: getStatusColor(order.status) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,40 +187,55 @@ const OrderDetails: FC = () => {
|
|||||||
<div className='text-description'>
|
<div className='text-description'>
|
||||||
شماره سفارش:
|
شماره سفارش:
|
||||||
</div>
|
</div>
|
||||||
<div>۱۲۳۴۵</div>
|
<div>{formatFaNumber(order.orderNumber)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='text-description'>
|
<div className='text-description'>
|
||||||
نام مشتری :
|
نام مشتری :
|
||||||
</div>
|
</div>
|
||||||
<div>مهرداد مظفری</div>
|
<div>{customerName}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='text-description'>نوع سفارش :</div>
|
<div className='text-description'>نوع سفارش :</div>
|
||||||
<div>تحویل در محل</div>
|
<div>{order.deliveryMethod?.title || '-'}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='text-description'>زمان سفارش : </div>
|
<div className='text-description'>زمان سفارش : </div>
|
||||||
<div>۱۴۰۳/۱۱/۲۵ - ۱۳:۰۰:۰۰</div>
|
<div>
|
||||||
|
{formatOptionalDate(order.createdAt, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 flex gap-20 text-[13px] font-light'>
|
<div className='mt-8 flex gap-20 text-[13px] font-light'>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='text-description'>شماره تماس:</div>
|
<div className='text-description'>شماره تماس:</div>
|
||||||
<div>۰۸۶۹۱۰۰۹۱۰۰۱</div>
|
<div>{order.user?.phone || '-'}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2 items-center'>
|
||||||
<div className='text-description'>آدرس::</div>
|
<div className='text-description'>آدرس:</div>
|
||||||
<div>اراک -خیابان شریعتی-خیابان جنت</div>
|
<div>{address}</div>
|
||||||
<div className='flex gap-1'>
|
{order.address?.latitude && order.address?.longitude && (
|
||||||
<Location size={17} color='#0047FF' />
|
<a
|
||||||
<div className='text-[13px] text-blue-600'>نقشه</div>
|
href={`https://www.google.com/maps?q=${order.address.latitude},${order.address.longitude}`}
|
||||||
</div>
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
className='flex gap-1 items-center cursor-pointer'
|
||||||
|
>
|
||||||
|
<Location size={17} color='#0047FF' />
|
||||||
|
<div className='text-[13px] text-blue-600'>نقشه</div>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,21 +252,23 @@ const OrderDetails: FC = () => {
|
|||||||
className='mt-0'
|
className='mt-0'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex justify-between items-center h-14 text-[13px] mt-5 border-t border-border'>
|
{order.tax > 0 && (
|
||||||
<div className=''>
|
<div className='flex justify-between items-center h-14 text-[13px] mt-5 border-t border-border'>
|
||||||
۱۰٪ ارزش افزوده
|
<div>
|
||||||
|
مالیات
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{formatPrice(order.tax)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
)}
|
||||||
۴۲۰,۰۰۰ تومان
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex font-bold justify-between items-center h-14 text-[13px] border-t border-border'>
|
<div className='flex font-bold justify-between items-center h-14 text-[13px] border-t border-border'>
|
||||||
<div>
|
<div>
|
||||||
جمع کل
|
جمع کل
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
۴۲۰,۰۰۰ تومان
|
{formatPrice(order.total)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,69 +281,58 @@ const OrderDetails: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-1.5 items-center'>
|
<div className='flex gap-1.5 items-center'>
|
||||||
<div className='size-2 rounded-full bg-green-500'></div>
|
<div className={`size-2 rounded-full ${getPaymentStatusColor(order.paymentStatus)}`}></div>
|
||||||
<div className='text-[13px] text-green-500'>
|
<div className={`text-[13px] ${getPaymentStatusTextColor(order.paymentStatus)}`}>
|
||||||
پرداخت شده
|
{getPaymentStatusText(order.paymentStatus)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className='mt-10'>
|
||||||
<div className='flex text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className='flex text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
<div className=' text-description'>
|
<div className='text-description'>
|
||||||
شماره تراکنش
|
نوع پرداخت
|
||||||
</div>
|
</div>
|
||||||
<div>۱۲۳۴۵۶۷۸۹۰</div>
|
<div>{order.paymentMethod?.title || '-'}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
<div className=' text-description'>
|
<div className='text-description'>
|
||||||
شماره پیگیری
|
|
||||||
</div>
|
|
||||||
<div>42343546</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
|
||||||
<div className=' text-description'>
|
|
||||||
نوع تراکنش
|
|
||||||
</div>
|
|
||||||
<div>پرداخت آنلاین</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
|
||||||
<div className=' text-description'>
|
|
||||||
مبلغ سفارش
|
مبلغ سفارش
|
||||||
</div>
|
</div>
|
||||||
<div>۴۰۰۰۰۰۰ تومان</div>
|
<div>{formatPrice(order.subTotal)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
{order.totalDiscount > 0 && (
|
||||||
<div className=' text-description'>
|
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
تخفیف
|
<div className='text-description'>
|
||||||
|
تخفیف
|
||||||
|
</div>
|
||||||
|
<div>{formatPrice(order.totalDiscount)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>۰ تومان</div>
|
)}
|
||||||
</div>
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
<div className=' text-description'>
|
<div className='text-description'>
|
||||||
هزینه ارسال
|
هزینه ارسال
|
||||||
</div>
|
</div>
|
||||||
<div>۲۰۰۰۰۰ تومان</div>
|
<div>{formatPrice(order.deliveryFee)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
{order.tax > 0 && (
|
||||||
<div className=' text-description'>
|
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
مالیات
|
<div className='text-description'>
|
||||||
|
مالیات
|
||||||
|
</div>
|
||||||
|
<div>{formatPrice(order.tax)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>۰ تومان</div>
|
)}
|
||||||
</div>
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
||||||
<div className=' text-description'>
|
<div className='text-description'>
|
||||||
مبلغ کل
|
مبلغ کل
|
||||||
</div>
|
</div>
|
||||||
<div>۴۲۰,۰۰۰ تومان</div>
|
<div>{formatPrice(order.total)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div >
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,9 @@ export const enum OrderStatus {
|
|||||||
|
|
||||||
Delivered = "delivered",
|
Delivered = "delivered",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum PaymentStatusEnum {
|
||||||
|
Pending = "pending",
|
||||||
|
Paid = "paid",
|
||||||
|
Failed = "failed",
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,3 +8,11 @@ export const useGetOrders = (params?: GetOrdersParams) => {
|
|||||||
queryFn: () => api.getOrders(params),
|
queryFn: () => api.getOrders(params),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetOrderById = (orderId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["order", orderId],
|
||||||
|
queryFn: () => api.getOrderById(orderId),
|
||||||
|
enabled: !!orderId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type { GetOrdersResponse } from "../types/Types";
|
import type { GetOrdersResponse, GetOrderByIdResponse } from "../types/Types";
|
||||||
|
|
||||||
export interface GetOrdersParams {
|
export interface GetOrdersParams {
|
||||||
page?: number;
|
page?: number;
|
||||||
@@ -17,3 +17,12 @@ export const getOrders = async (
|
|||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getOrderById = async (
|
||||||
|
orderId: string
|
||||||
|
): Promise<GetOrderByIdResponse> => {
|
||||||
|
const { data } = await axios.get<GetOrderByIdResponse>(
|
||||||
|
`/admin/orders/${orderId}`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -169,3 +169,4 @@ export interface Order extends RowDataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetOrdersResponse = IResponse<Order[]>;
|
export type GetOrdersResponse = IResponse<Order[]>;
|
||||||
|
export type GetOrderByIdResponse = IResponse<Order>;
|
||||||
|
|||||||
Reference in New Issue
Block a user