Files
dmenu-admin/src/pages/orders/Details.tsx
T
morteza 7ad840fbda
deploy to danak / build_and_deploy (push) Has been cancelled
send sms to user in order detail
2026-07-26 11:56:21 +03:30

466 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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