342 lines
13 KiB
TypeScript
342 lines
13 KiB
TypeScript
import { type FC, useState } from 'react'
|
||
import {
|
||
useGetOrderById,
|
||
useVerifyPayment,
|
||
useChangeOrderStatus,
|
||
useRefundPayment,
|
||
useUpdateOrderFees,
|
||
} from './hooks/useOrderData'
|
||
import { useParams } from 'react-router-dom'
|
||
import { formatFaNumber } from '@/helpers/func'
|
||
import { OrderStatus, PaymentStatusEnum } from './enum/Enum'
|
||
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
|
||
import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum'
|
||
import ModalConfrim from '@/components/ModalConfrim'
|
||
import CustomerInfo from './components/CustomerInfo'
|
||
import OrderItemsDetails from './components/OrderItemsDetails'
|
||
import PaymentInfo from './components/PaymentInfo'
|
||
import PaymentVerificationBox from './components/PaymentVerificationBox'
|
||
import OrderActions from './components/OrderActions'
|
||
import UpdateOrderFeesModal from './components/UpdateOrderFeesModal'
|
||
import { toast } from 'react-toastify'
|
||
import { extractErrorMessage } from '@/config/func'
|
||
import { printOrderReceipt } from './print/orderReceiptPrint'
|
||
import type { UpdateOrderFeesType } from './types/Types'
|
||
|
||
const OrderDetails: FC = () => {
|
||
const { id } = useParams()
|
||
const { data: orderData, isLoading } = useGetOrderById(id!)
|
||
const { mutate: verifyPayment, isPending: isVerifyingPayment } = useVerifyPayment()
|
||
const { mutate: changeOrderStatus, isPending: isChangingStatus } = useChangeOrderStatus()
|
||
const { mutate: refundPayment, isPending: isRefunding } = useRefundPayment()
|
||
const { mutate: updateOrderFees, isPending: isUpdatingFees } = useUpdateOrderFees()
|
||
|
||
const [showCancelModal, setShowCancelModal] = useState(false)
|
||
const [showRefundModal, setShowRefundModal] = useState(false)
|
||
const [showEditFeesModal, setShowEditFeesModal] = useState(false)
|
||
|
||
const order = orderData?.data
|
||
|
||
const getStatusColor = (status: string): string => {
|
||
const colorMap: Record<string, string> = {
|
||
'pendingPayment': '#FFEECC',
|
||
'paid': '#E3F2FD',
|
||
'preparing': '#FFF3E0',
|
||
'ready': '#E3F2FD',
|
||
'shipped': '#E3F2FD',
|
||
'completed': '#E8F5E9',
|
||
'canceled': '#FFEBEE',
|
||
}
|
||
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
|
||
}
|
||
|
||
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 isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
|
||
const isCreditCardPayment = order.paymentMethod?.method === PaymentMethodEnum.CreditCard
|
||
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
|
||
const isOrderPaid = order.status === OrderStatus.PAID
|
||
const isPreparing = order.status === OrderStatus.PREPARING
|
||
const isCanceled = order.status === OrderStatus.CANCELED
|
||
const isCompleted = order.status === OrderStatus.COMPLETED
|
||
const canEditFees = !isCanceled && !isCompleted
|
||
const canCompleteOrder = [
|
||
OrderStatus.DELIVERED_TO_WAITER,
|
||
OrderStatus.DELIVERED_TO_RECEPTIONIST,
|
||
OrderStatus.SHIPPED,
|
||
].includes(order.status as OrderStatus)
|
||
// بررسی وضعیت پرداخت از payments array یا paymentStatus
|
||
// ابتدا از paymentStatus استفاده میکنیم، اگر نبود از اولین payment در payments array استفاده میکنیم
|
||
const paymentStatus = order.paymentStatus || order.payments?.[0]?.status
|
||
// بررسی وضعیت pending (هم enum و هم string literal)
|
||
const isPaymentPending = paymentStatus === PaymentStatusEnum.Pending || paymentStatus === 'pending'
|
||
const isCourierDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCourier
|
||
const isDineInOrCarDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DineIn || order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCar
|
||
const isCustomerPickup = order.deliveryMethod?.method === DeliveryMethodEnum.CustomerPickup
|
||
|
||
const payment = order.payments?.[0]
|
||
const hasPaymentDescription = Boolean(payment?.description)
|
||
const hasPaymentAttachments = Boolean(payment?.attachments && payment.attachments.length > 0)
|
||
const showPaymentReceiptBox = isCreditCardPayment && (hasPaymentDescription || hasPaymentAttachments)
|
||
const showVerifyPaymentButton = (isCashPayment || isCreditCardPayment) && isPaymentPending && !isCanceled
|
||
|
||
// نمایش دکمه ارسال به آشپزخانه
|
||
// برای pendingPayment با پرداخت نقدی یا برای paid
|
||
const showSendToKitchenButton = (isPendingPayment && isCashPayment) || isOrderPaid
|
||
|
||
// نمایش دکمه کنسل
|
||
// همیشه نمایش داده میشود مگر اینکه سفارش قبلاً کنسل شده باشد
|
||
const showCancelButton = !isCanceled
|
||
|
||
const handleVerifyPayment = () => {
|
||
const paymentId = order.paymentId || order.payments?.[0]?.id
|
||
if (paymentId) {
|
||
verifyPayment(paymentId, {
|
||
onSuccess: () => {
|
||
// Success handled by query invalidation
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
const handleSendToKitchen = () => {
|
||
changeOrderStatus({
|
||
orderId: order.id,
|
||
status: OrderStatus.PREPARING
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleDeliverToCourier = () => {
|
||
changeOrderStatus({
|
||
orderId: order.id,
|
||
status: OrderStatus.SHIPPED
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleDeliverToWaiter = () => {
|
||
changeOrderStatus({
|
||
orderId: order.id,
|
||
status: OrderStatus.DELIVERED_TO_WAITER
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleDeliverToReceptionist = () => {
|
||
changeOrderStatus({
|
||
orderId: order.id,
|
||
status: OrderStatus.DELIVERED_TO_RECEPTIONIST
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleCancelOrder = (description?: string) => {
|
||
changeOrderStatus({
|
||
orderId: order.id,
|
||
status: OrderStatus.CANCELED,
|
||
params: description ? { description } : undefined
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
setShowCancelModal(false)
|
||
}
|
||
|
||
const handleCompleteOrder = () => {
|
||
changeOrderStatus(
|
||
{
|
||
orderId: order.id,
|
||
status: OrderStatus.COMPLETED,
|
||
},
|
||
{
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleRefund = (text?: string) => {
|
||
if (!text) return
|
||
refundPayment({
|
||
orderId: order.id,
|
||
params: { description: text }
|
||
}, {
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
setShowRefundModal(false)
|
||
}
|
||
|
||
const handlePrintOrder = () => {
|
||
printOrderReceipt(order)
|
||
}
|
||
|
||
const handleUpdateOrderFees = (params: UpdateOrderFeesType) => {
|
||
updateOrderFees(
|
||
{ orderId: order.id, params },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('هزینههای سفارش بهروزرسانی شد')
|
||
setShowEditFeesModal(false)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className='mt-5'>
|
||
<div className='flex justify-between items-center'>
|
||
<h1 className='text-lg font-light'>
|
||
سفارش {formatFaNumber(order.orderNumber)}
|
||
</h1>
|
||
{/* <div>
|
||
<Select
|
||
items={statusItems}
|
||
value={order.status}
|
||
placeholder={t(`order_status.${order.status}`)}
|
||
className='w-[200px]'
|
||
style={{ backgroundColor: getStatusColor(order.status) }}
|
||
/>
|
||
</div> */}
|
||
</div>
|
||
|
||
<div className='flex gap-7 mt-9'>
|
||
<div className='flex-1 space-y-8'>
|
||
<CustomerInfo order={order} />
|
||
<OrderItemsDetails order={order} getStatusColor={getStatusColor} />
|
||
</div>
|
||
|
||
<div>
|
||
<PaymentInfo
|
||
order={order}
|
||
getPaymentStatusColor={getPaymentStatusColor}
|
||
getPaymentStatusTextColor={getPaymentStatusTextColor}
|
||
getPaymentStatusText={getPaymentStatusText}
|
||
/>
|
||
{showPaymentReceiptBox && (
|
||
<PaymentVerificationBox
|
||
order={order}
|
||
isVerifyingPayment={isVerifyingPayment}
|
||
onVerifyPayment={handleVerifyPayment}
|
||
showVerifyButton={showVerifyPaymentButton}
|
||
/>
|
||
)}
|
||
<OrderActions
|
||
onPrint={handlePrintOrder}
|
||
showSendToKitchenButton={showSendToKitchenButton}
|
||
showCancelButton={showCancelButton}
|
||
showEditFeesButton={canEditFees}
|
||
isPreparing={isPreparing}
|
||
isCourierDelivery={isCourierDelivery}
|
||
isDineInOrCarDelivery={isDineInOrCarDelivery}
|
||
isCustomerPickup={isCustomerPickup}
|
||
isCanceled={isCanceled}
|
||
canCompleteOrder={canCompleteOrder}
|
||
isChangingStatus={isChangingStatus}
|
||
onSendToKitchen={handleSendToKitchen}
|
||
onDeliverToCourier={handleDeliverToCourier}
|
||
onDeliverToWaiter={handleDeliverToWaiter}
|
||
onDeliverToReceptionist={handleDeliverToReceptionist}
|
||
onCompleteOrder={handleCompleteOrder}
|
||
onCancelOrder={() => setShowCancelModal(true)}
|
||
onEditFees={() => setShowEditFeesModal(true)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<UpdateOrderFeesModal
|
||
isOpen={showEditFeesModal}
|
||
onClose={() => setShowEditFeesModal(false)}
|
||
order={order}
|
||
isLoading={isUpdatingFees}
|
||
onSubmit={handleUpdateOrderFees}
|
||
/>
|
||
|
||
{/* مدال کنسل سفارش */}
|
||
<ModalConfrim
|
||
isOpen={showCancelModal}
|
||
close={() => setShowCancelModal(false)}
|
||
onConfrim={handleCancelOrder}
|
||
isloading={isChangingStatus}
|
||
label='آیا از لغو سفارش مطمئن هستید؟'
|
||
isHasDescription
|
||
/>
|
||
|
||
{/* مدال ریفاند */}
|
||
<ModalConfrim
|
||
isOpen={showRefundModal}
|
||
close={() => setShowRefundModal(false)}
|
||
onConfrim={handleRefund}
|
||
isloading={isRefunding}
|
||
label='آیا از ریفاند این سفارش مطمئن هستید؟'
|
||
isHasDescription
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default OrderDetails |