detail of order

This commit is contained in:
hamid zarghami
2025-12-07 16:12:54 +03:30
parent 8240a8170f
commit 10e5143d78
5 changed files with 220 additions and 83 deletions
+195 -82
View File
@@ -1,11 +1,16 @@
import Select from '@/components/Select'
import Table from '@/components/Table'
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 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
title: string
quantity: number
@@ -13,27 +18,104 @@ interface OrderItem extends RowDataType {
}
const OrderDetails: FC = () => {
const orderItems: OrderItem[] = [
{
id: 1,
image: AppImage,
title: 'پیتزا پپرونی',
quantity: 1,
amount: '۴۲,۰۰۰ تومان'
}
]
const { id } = useParams()
const { data: orderData, isLoading } = useGetOrderById(id!)
const { t } = useTranslation('global')
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: 'تصویر',
key: 'image',
render: (item) => (
<div className=''>
<img
src={item.image}
alt={item.title}
className='w-12 h-12 rounded-lg'
/>
<div>
{item.image ? (
<img
src={item.image}
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>
)
},
@@ -45,7 +127,8 @@ const OrderDetails: FC = () => {
{
title: 'تعداد',
key: 'quantity',
align: 'center'
align: 'center',
render: (item) => formatFaNumber(item.quantity)
},
{
title: 'مبلغ',
@@ -53,15 +136,39 @@ const OrderDetails: FC = () => {
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 (
<div className='mt-5'>
<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>
<Select
items={[]}
placeholder='درحال آماده سازی'
className='w-[200px] bg-[#FFEECC]'
items={statusItems}
value={order.status}
placeholder={t(`order_status.${order.status}`)}
className='w-[200px]'
style={{ backgroundColor: getStatusColor(order.status) }}
/>
</div>
</div>
@@ -80,40 +187,55 @@ const OrderDetails: FC = () => {
<div className='text-description'>
شماره سفارش:
</div>
<div>۱۲۳۴۵</div>
<div>{formatFaNumber(order.orderNumber)}</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
نام مشتری :
</div>
<div>مهرداد مظفری</div>
<div>{customerName}</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>نوع سفارش :</div>
<div>تحویل در محل</div>
<div>{order.deliveryMethod?.title || '-'}</div>
</div>
<div className='flex gap-2'>
<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 className='mt-8 flex gap-20 text-[13px] font-light'>
<div className='flex gap-2'>
<div className='text-description'>شماره تماس:</div>
<div>۰۸۶۹۱۰۰۹۱۰۰۱</div>
<div>{order.user?.phone || '-'}</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>آدرس::</div>
<div>اراک -خیابان شریعتی-خیابان جنت</div>
<div className='flex gap-1'>
<Location size={17} color='#0047FF' />
<div className='text-[13px] text-blue-600'>نقشه</div>
</div>
<div className='flex gap-2 items-center'>
<div className='text-description'>آدرس:</div>
<div>{address}</div>
{order.address?.latitude && order.address?.longitude && (
<a
href={`https://www.google.com/maps?q=${order.address.latitude},${order.address.longitude}`}
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>
@@ -130,21 +252,23 @@ const OrderDetails: FC = () => {
className='mt-0'
/>
<div className='flex justify-between items-center h-14 text-[13px] mt-5 border-t border-border'>
<div className=''>
۱۰٪ ارزش افزوده
{order.tax > 0 && (
<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 className='flex font-bold justify-between items-center h-14 text-[13px] border-t border-border'>
<div>
جمع کل
</div>
<div>
۴۲۰,۰۰۰ تومان
{formatPrice(order.total)}
</div>
</div>
</div>
@@ -157,69 +281,58 @@ const OrderDetails: FC = () => {
</div>
<div className='flex gap-1.5 items-center'>
<div className='size-2 rounded-full bg-green-500'></div>
<div className='text-[13px] text-green-500'>
پرداخت شده
<div className={`size-2 rounded-full ${getPaymentStatusColor(order.paymentStatus)}`}></div>
<div className={`text-[13px] ${getPaymentStatusTextColor(order.paymentStatus)}`}>
{getPaymentStatusText(order.paymentStatus)}
</div>
</div>
</div>
<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=' text-description'>
شماره تراکنش
<div className='text-description'>
نوع پرداخت
</div>
<div>۱۲۳۴۵۶۷۸۹۰</div>
<div>{order.paymentMethod?.title || '-'}</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>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 className='text-description'>
مبلغ سفارش
</div>
<div>۴۰۰۰۰۰۰ تومان</div>
<div>{formatPrice(order.subTotal)}</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'>
تخفیف
{order.totalDiscount > 0 && (
<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 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>{formatPrice(order.deliveryFee)}</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'>
مالیات
{order.tax > 0 && (
<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 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>{formatPrice(order.total)}</div>
</div>
</div>
</div>
</div>
</div >
</div>
)
}
+6
View File
@@ -15,3 +15,9 @@ export const enum OrderStatus {
Delivered = "delivered",
}
export const enum PaymentStatusEnum {
Pending = "pending",
Paid = "paid",
Failed = "failed",
}
+8
View File
@@ -8,3 +8,11 @@ export const useGetOrders = (params?: GetOrdersParams) => {
queryFn: () => api.getOrders(params),
});
};
export const useGetOrderById = (orderId: string) => {
return useQuery({
queryKey: ["order", orderId],
queryFn: () => api.getOrderById(orderId),
enabled: !!orderId,
});
};
+10 -1
View File
@@ -1,5 +1,5 @@
import axios from "@/config/axios";
import type { GetOrdersResponse } from "../types/Types";
import type { GetOrdersResponse, GetOrderByIdResponse } from "../types/Types";
export interface GetOrdersParams {
page?: number;
@@ -17,3 +17,12 @@ export const getOrders = async (
});
return data;
};
export const getOrderById = async (
orderId: string
): Promise<GetOrderByIdResponse> => {
const { data } = await axios.get<GetOrderByIdResponse>(
`/admin/orders/${orderId}`
);
return data;
};
+1
View File
@@ -169,3 +169,4 @@ export interface Order extends RowDataType {
}
export type GetOrdersResponse = IResponse<Order[]>;
export type GetOrderByIdResponse = IResponse<Order>;