466 lines
17 KiB
TypeScript
466 lines
17 KiB
TypeScript
import { type FC, useState } from 'react'
|
||
import {
|
||
useGetOrderById,
|
||
useVerifyPayment,
|
||
useChangeOrderStatus,
|
||
useRefundPayment,
|
||
useAddPayment,
|
||
useUpdateOrderFees,
|
||
useAddOrderItem,
|
||
useUpdateOrderItemQuantity,
|
||
useRemoveOrderItem,
|
||
useSendPleaseComeSms,
|
||
} from './hooks/useOrderData'
|
||
import { useNavigate, useParams } from 'react-router-dom'
|
||
import { ArrowRight2 } from 'iconsax-react'
|
||
import { formatFaNumber } from '@/helpers/func'
|
||
import { Pages } from '@/config/Pages'
|
||
import { OrderStatus, PaymentStatusEnum } from './enum/Enum'
|
||
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
|
||
import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum'
|
||
import Button from '@/components/Button'
|
||
import ModalConfrim from '@/components/ModalConfrim'
|
||
import CustomerInfo from './components/CustomerInfo'
|
||
import OrderItemsDetails from './components/OrderItemsDetails'
|
||
import PaymentsList from './components/PaymentsList'
|
||
import PaymentInfo from './components/PaymentInfo'
|
||
import OrderActions from './components/OrderActions'
|
||
import UpdateOrderFeesModal from './components/UpdateOrderFeesModal'
|
||
import AddOrderItemModal from './components/AddOrderItemModal'
|
||
import RefundPaymentModal from './components/RefundPaymentModal'
|
||
import AddPaymentModal from './components/AddPaymentModal'
|
||
import SendPleaseComeSmsModal from './components/SendPleaseComeSmsModal'
|
||
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 navigate = useNavigate()
|
||
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: addPayment, isPending: isAddingPayment } = useAddPayment()
|
||
const { mutate: updateOrderFees, isPending: isUpdatingFees } = useUpdateOrderFees()
|
||
const { mutate: addOrderItem, isPending: isAddingItem } = useAddOrderItem()
|
||
const { mutate: updateOrderItemQuantity, isPending: isUpdatingItemQuantity } = useUpdateOrderItemQuantity()
|
||
const { mutate: removeOrderItem, isPending: isRemovingItem } = useRemoveOrderItem()
|
||
const { mutate: sendPleaseComeSms, isPending: isSendingPleaseComeSms } = useSendPleaseComeSms()
|
||
|
||
const [showCancelModal, setShowCancelModal] = useState(false)
|
||
const [showPleaseComeSmsModal, setShowPleaseComeSmsModal] = useState(false)
|
||
const [refundModal, setRefundModal] = useState<{ defaultAmount: number; maxAmount: number } | null>(null)
|
||
const [showAddPaymentModal, setShowAddPaymentModal] = useState(false)
|
||
const [showEditFeesModal, setShowEditFeesModal] = useState(false)
|
||
const [showAddItemModal, setShowAddItemModal] = useState(false)
|
||
const [itemIdToRemove, setItemIdToRemove] = useState<string | null>(null)
|
||
const [verifyingPaymentId, setVerifyingPaymentId] = useState<string | null>(null)
|
||
|
||
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'
|
||
}
|
||
|
||
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 isOrderPaid = Number(order.paidAmount ?? 0) > 0 || order.payments?.some(
|
||
(payment) => payment.status === PaymentStatusEnum.Paid
|
||
)
|
||
const isPreparing = order.status === OrderStatus.PREPARING
|
||
const isCanceled = order.status === OrderStatus.CANCELED
|
||
const isCompleted = order.status === OrderStatus.COMPLETED
|
||
const isPendingPayment = order.status === OrderStatus.NEW
|
||
const canEditFees = !isCanceled && !isCompleted
|
||
const canEditItems = !isCanceled && !isCompleted
|
||
const canAddPayment = !isCanceled && Number(order.balance ?? 0) > 0
|
||
const isUpdatingItem = isAddingItem || isUpdatingItemQuantity || isRemovingItem
|
||
const canCompleteOrder = [
|
||
OrderStatus.DELIVERED_TO_WAITER,
|
||
OrderStatus.DELIVERED_TO_RECEPTIONIST,
|
||
OrderStatus.SHIPPED,
|
||
].includes(order.status as OrderStatus)
|
||
const canSendPleaseComeSms = Boolean(order.user?.phone)
|
||
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 showSendToKitchenButton = (isPendingPayment && isCashPayment) || isOrderPaid
|
||
const showCancelButton = !isCanceled
|
||
|
||
const handleVerifyPayment = (paymentId: string) => {
|
||
setVerifyingPaymentId(paymentId)
|
||
verifyPayment(paymentId, {
|
||
onSuccess: () => {
|
||
setVerifyingPaymentId(null)
|
||
},
|
||
onError: (error) => {
|
||
setVerifyingPaymentId(null)
|
||
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 = (amount: number, description: string) => {
|
||
refundPayment(
|
||
{
|
||
orderId: order.id,
|
||
params: { amount, description },
|
||
},
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('بازگشت وجه با موفقیت انجام شد')
|
||
setRefundModal(null)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleAddPayment = (amount: number, description: string) => {
|
||
addPayment(
|
||
{
|
||
orderId: order.id,
|
||
params: {
|
||
amount,
|
||
...(description ? { description } : {}),
|
||
},
|
||
},
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('پرداخت با موفقیت ثبت شد')
|
||
setShowAddPaymentModal(false)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handlePrintOrder = () => {
|
||
printOrderReceipt(order)
|
||
}
|
||
|
||
const handleUpdateOrderFees = (params: UpdateOrderFeesType) => {
|
||
updateOrderFees(
|
||
{ orderId: order.id, params },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('هزینههای سفارش بهروزرسانی شد')
|
||
setShowEditFeesModal(false)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleAddOrderItem = (foodId: string, quantity: number) => {
|
||
addOrderItem(
|
||
{ orderId: order.id, params: { foodId, quantity } },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('آیتم به سفارش اضافه شد')
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleUpdateItemQuantity = (itemId: string, quantity: number) => {
|
||
if (quantity < 1) return
|
||
updateOrderItemQuantity(
|
||
{ orderId: order.id, itemId, params: { quantity } },
|
||
{
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleConfirmRemoveItem = () => {
|
||
if (!itemIdToRemove) return
|
||
removeOrderItem(
|
||
{ orderId: order.id, itemId: itemIdToRemove },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('آیتم از سفارش حذف شد')
|
||
setItemIdToRemove(null)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
const handleConfirmPleaseComeSms = () => {
|
||
sendPleaseComeSms(order.id, {
|
||
onSuccess: (response) => {
|
||
toast.success(response.message || 'پیامک در صف ارسال قرار گرفت')
|
||
setShowPleaseComeSmsModal(false)
|
||
},
|
||
onError: (error) => {
|
||
toast.error(extractErrorMessage(error))
|
||
},
|
||
})
|
||
}
|
||
|
||
return (
|
||
<div className='mt-5 pb-8'>
|
||
<div className='flex justify-between items-center'>
|
||
<h1 className='text-lg font-light'>
|
||
سفارش {formatFaNumber(order.orderNumber)}
|
||
</h1>
|
||
<Button
|
||
className='w-fit px-6 bg-transparent text-primary border-primary border'
|
||
onClick={() => navigate(Pages.orders.list)}
|
||
>
|
||
<div className='flex gap-2 items-center'>
|
||
<ArrowRight2 color='#000' size={18} />
|
||
<span>بازگشت</span>
|
||
</div>
|
||
</Button>
|
||
</div>
|
||
|
||
<div className='flex flex-col lg:flex-row gap-5 lg:gap-7 mt-6 sm:mt-9'>
|
||
<div className='flex-1 min-w-0 space-y-5 sm:space-y-8 order-2 lg:order-1'>
|
||
<CustomerInfo order={order} />
|
||
<OrderItemsDetails
|
||
order={order}
|
||
getStatusColor={getStatusColor}
|
||
canEditItems={canEditItems}
|
||
isUpdatingItem={isUpdatingItem}
|
||
onAddItem={() => setShowAddItemModal(true)}
|
||
onUpdateQuantity={handleUpdateItemQuantity}
|
||
onRemoveItem={(itemId) => setItemIdToRemove(itemId)}
|
||
/>
|
||
<PaymentsList
|
||
order={order}
|
||
isVerifyingPayment={isVerifyingPayment}
|
||
verifyingPaymentId={verifyingPaymentId}
|
||
onVerifyPayment={handleVerifyPayment}
|
||
canAddPayment={canAddPayment}
|
||
onAddPayment={() => setShowAddPaymentModal(true)}
|
||
/>
|
||
</div>
|
||
|
||
<div className='w-full lg:w-[330px] shrink-0 order-1 lg:order-2'>
|
||
<PaymentInfo
|
||
order={order}
|
||
canEditFees={canEditFees}
|
||
onEditFees={() => setShowEditFeesModal(true)}
|
||
isRefunding={isRefunding}
|
||
onRefund={(defaultAmount, maxAmount) =>
|
||
setRefundModal({ defaultAmount, maxAmount })
|
||
}
|
||
/>
|
||
<OrderActions
|
||
onPrint={handlePrintOrder}
|
||
showSendToKitchenButton={showSendToKitchenButton}
|
||
showCancelButton={showCancelButton}
|
||
isPreparing={isPreparing}
|
||
isCourierDelivery={isCourierDelivery}
|
||
isDineInOrCarDelivery={isDineInOrCarDelivery}
|
||
isCustomerPickup={isCustomerPickup}
|
||
isCanceled={isCanceled}
|
||
canCompleteOrder={canCompleteOrder}
|
||
canSendPleaseComeSms={canSendPleaseComeSms}
|
||
isChangingStatus={isChangingStatus}
|
||
isSendingPleaseComeSms={isSendingPleaseComeSms}
|
||
onSendToKitchen={handleSendToKitchen}
|
||
onDeliverToCourier={handleDeliverToCourier}
|
||
onDeliverToWaiter={handleDeliverToWaiter}
|
||
onDeliverToReceptionist={handleDeliverToReceptionist}
|
||
onCompleteOrder={handleCompleteOrder}
|
||
onCancelOrder={() => setShowCancelModal(true)}
|
||
onSendPleaseComeSms={() => setShowPleaseComeSmsModal(true)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<UpdateOrderFeesModal
|
||
isOpen={showEditFeesModal}
|
||
onClose={() => setShowEditFeesModal(false)}
|
||
order={order}
|
||
isLoading={isUpdatingFees}
|
||
onSubmit={handleUpdateOrderFees}
|
||
/>
|
||
|
||
<AddOrderItemModal
|
||
isOpen={showAddItemModal}
|
||
onClose={() => setShowAddItemModal(false)}
|
||
existingItems={order.items}
|
||
isLoading={isAddingItem}
|
||
onAdd={handleAddOrderItem}
|
||
/>
|
||
|
||
<ModalConfrim
|
||
isOpen={showCancelModal}
|
||
close={() => setShowCancelModal(false)}
|
||
onConfrim={handleCancelOrder}
|
||
isloading={isChangingStatus}
|
||
label='آیا از لغو سفارش مطمئن هستید؟'
|
||
isHasDescription
|
||
/>
|
||
|
||
<RefundPaymentModal
|
||
isOpen={!!refundModal}
|
||
onClose={() => setRefundModal(null)}
|
||
defaultAmount={refundModal?.defaultAmount ?? 0}
|
||
maxAmount={refundModal?.maxAmount ?? 0}
|
||
isLoading={isRefunding}
|
||
onSubmit={handleRefund}
|
||
/>
|
||
|
||
<AddPaymentModal
|
||
isOpen={showAddPaymentModal}
|
||
onClose={() => setShowAddPaymentModal(false)}
|
||
defaultAmount={Number(order.balance ?? 0)}
|
||
isLoading={isAddingPayment}
|
||
onSubmit={handleAddPayment}
|
||
/>
|
||
|
||
<ModalConfrim
|
||
isOpen={!!itemIdToRemove}
|
||
close={() => setItemIdToRemove(null)}
|
||
onConfrim={handleConfirmRemoveItem}
|
||
isloading={isRemovingItem}
|
||
label='آیا از حذف این آیتم مطمئن هستید؟'
|
||
/>
|
||
|
||
<SendPleaseComeSmsModal
|
||
isOpen={showPleaseComeSmsModal}
|
||
onClose={() => setShowPleaseComeSmsModal(false)}
|
||
isLoading={isSendingPleaseComeSms}
|
||
customerName={order.user?.firstName || ''}
|
||
restaurantName={order.restaurant?.name || ''}
|
||
onConfirm={handleConfirmPleaseComeSms}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default OrderDetails
|