This commit is contained in:
@@ -9,12 +9,16 @@ import {
|
||||
useAddOrderItem,
|
||||
useUpdateOrderItemQuantity,
|
||||
useRemoveOrderItem,
|
||||
useSendPleaseComeSms,
|
||||
} from './hooks/useOrderData'
|
||||
import { useParams } from 'react-router-dom'
|
||||
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'
|
||||
@@ -25,6 +29,7 @@ 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'
|
||||
@@ -32,6 +37,7 @@ 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()
|
||||
@@ -41,8 +47,10 @@ const OrderDetails: FC = () => {
|
||||
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)
|
||||
@@ -98,6 +106,7 @@ const OrderDetails: FC = () => {
|
||||
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 ||
|
||||
@@ -306,12 +315,33 @@ const OrderDetails: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
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'>
|
||||
@@ -356,13 +386,16 @@ const OrderDetails: FC = () => {
|
||||
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>
|
||||
@@ -416,6 +449,15 @@ const OrderDetails: FC = () => {
|
||||
isloading={isRemovingItem}
|
||||
label='آیا از حذف این آیتم مطمئن هستید؟'
|
||||
/>
|
||||
|
||||
<SendPleaseComeSmsModal
|
||||
isOpen={showPleaseComeSmsModal}
|
||||
onClose={() => setShowPleaseComeSmsModal(false)}
|
||||
isLoading={isSendingPleaseComeSms}
|
||||
customerName={order.user?.firstName || ''}
|
||||
restaurantName={order.restaurant?.name || ''}
|
||||
onConfirm={handleConfirmPleaseComeSms}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC } from 'react'
|
||||
import { type FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Add } from 'iconsax-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
@@ -6,12 +6,15 @@ import Table from '@/components/Table'
|
||||
import Filters from '@/components/Filters'
|
||||
import Button from '@/components/Button'
|
||||
import { Pages } from '@/config/Pages'
|
||||
import { useGetOrders } from './hooks/useOrderData'
|
||||
import { useGetOrders, useSendPleaseComeSms } from './hooks/useOrderData'
|
||||
import { useOrderFilters } from './hooks/useOrderFilters'
|
||||
import { getOrderTableColumns } from './components/OrderTableColumns'
|
||||
import { useOrderFiltersFields } from './components/OrderFiltersFields'
|
||||
import SendPleaseComeSmsModal from './components/SendPleaseComeSmsModal'
|
||||
import type { Order } from './types/Types'
|
||||
import type { RowDataType } from '@/components/types/TableTypes'
|
||||
import { toast } from 'react-toastify'
|
||||
import { extractErrorMessage } from '@/config/func'
|
||||
|
||||
const OrdersList: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
@@ -24,13 +27,32 @@ const OrdersList: FC = () => {
|
||||
} = useOrderFilters()
|
||||
|
||||
const { data: ordersData, isLoading } = useGetOrders(apiParams)
|
||||
const { mutate: sendPleaseComeSms, isPending: isSendingSms } = useSendPleaseComeSms()
|
||||
const [smsOrder, setSmsOrder] = useState<Order | null>(null)
|
||||
|
||||
const orders = ordersData?.data || []
|
||||
const columns = getOrderTableColumns({ t })
|
||||
const columns = getOrderTableColumns({
|
||||
t,
|
||||
onSendPleaseComeSms: (order) => setSmsOrder(order),
|
||||
})
|
||||
const filterFields = useOrderFiltersFields()
|
||||
|
||||
const totalPages = ordersData?.meta?.totalPages || 1
|
||||
|
||||
const handleConfirmSendSms = () => {
|
||||
if (!smsOrder) return
|
||||
|
||||
sendPleaseComeSms(smsOrder.id, {
|
||||
onSuccess: (response) => {
|
||||
toast.success(response.message || 'پیامک در صف ارسال قرار گرفت')
|
||||
setSmsOrder(null)
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(extractErrorMessage(error))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-5'>
|
||||
<div className='flex justify-between items-center'>
|
||||
@@ -64,8 +86,17 @@ const OrdersList: FC = () => {
|
||||
onPageChange: handlePageChange,
|
||||
}}
|
||||
/>
|
||||
|
||||
<SendPleaseComeSmsModal
|
||||
isOpen={!!smsOrder}
|
||||
onClose={() => setSmsOrder(null)}
|
||||
isLoading={isSendingSms}
|
||||
customerName={smsOrder?.user?.firstName || ''}
|
||||
restaurantName={smsOrder?.restaurant?.name || ''}
|
||||
onConfirm={handleConfirmSendSms}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrdersList
|
||||
export default OrdersList
|
||||
|
||||
@@ -1,144 +1,162 @@
|
||||
import Button from '@/components/Button'
|
||||
import { type FC, useState, useEffect } from 'react'
|
||||
import Button from "@/components/Button";
|
||||
import { type FC, useState, useEffect } from "react";
|
||||
|
||||
interface OrderActionsProps {
|
||||
onPrint: () => void
|
||||
showSendToKitchenButton: boolean
|
||||
showCancelButton: boolean
|
||||
isPreparing: boolean
|
||||
isCourierDelivery: boolean
|
||||
isDineInOrCarDelivery: boolean
|
||||
isCustomerPickup: boolean
|
||||
isCanceled: boolean
|
||||
canCompleteOrder: boolean
|
||||
isChangingStatus: boolean
|
||||
onSendToKitchen: () => void
|
||||
onDeliverToCourier: () => void
|
||||
onDeliverToWaiter: () => void
|
||||
onDeliverToReceptionist: () => void
|
||||
onCompleteOrder: () => void
|
||||
onCancelOrder: () => void
|
||||
onPrint: () => void;
|
||||
showSendToKitchenButton: boolean;
|
||||
showCancelButton: boolean;
|
||||
isPreparing: boolean;
|
||||
isCourierDelivery: boolean;
|
||||
isDineInOrCarDelivery: boolean;
|
||||
isCustomerPickup: boolean;
|
||||
isCanceled: boolean;
|
||||
canCompleteOrder: boolean;
|
||||
canSendPleaseComeSms: boolean;
|
||||
isChangingStatus: boolean;
|
||||
isSendingPleaseComeSms?: boolean;
|
||||
onSendToKitchen: () => void;
|
||||
onDeliverToCourier: () => void;
|
||||
onDeliverToWaiter: () => void;
|
||||
onDeliverToReceptionist: () => void;
|
||||
onCompleteOrder: () => void;
|
||||
onCancelOrder: () => void;
|
||||
onSendPleaseComeSms: () => void;
|
||||
}
|
||||
|
||||
const OrderActions: FC<OrderActionsProps> = ({
|
||||
onPrint,
|
||||
showSendToKitchenButton,
|
||||
showCancelButton,
|
||||
isPreparing,
|
||||
isCourierDelivery,
|
||||
isDineInOrCarDelivery,
|
||||
isCustomerPickup,
|
||||
isCanceled,
|
||||
canCompleteOrder,
|
||||
isChangingStatus,
|
||||
onSendToKitchen,
|
||||
onDeliverToCourier,
|
||||
onDeliverToWaiter,
|
||||
onDeliverToReceptionist,
|
||||
onCompleteOrder,
|
||||
onCancelOrder,
|
||||
onPrint,
|
||||
showSendToKitchenButton,
|
||||
showCancelButton,
|
||||
isPreparing,
|
||||
isCourierDelivery,
|
||||
isDineInOrCarDelivery,
|
||||
isCustomerPickup,
|
||||
isCanceled,
|
||||
canCompleteOrder,
|
||||
canSendPleaseComeSms,
|
||||
isChangingStatus,
|
||||
isSendingPleaseComeSms,
|
||||
onSendToKitchen,
|
||||
onDeliverToCourier,
|
||||
onDeliverToWaiter,
|
||||
onDeliverToReceptionist,
|
||||
onCompleteOrder,
|
||||
onCancelOrder,
|
||||
onSendPleaseComeSms,
|
||||
}) => {
|
||||
const [loadingAction, setLoadingAction] = useState<string | null>(null)
|
||||
const [loadingAction, setLoadingAction] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isChangingStatus) {
|
||||
setLoadingAction(null)
|
||||
}
|
||||
}, [isChangingStatus])
|
||||
|
||||
const handleSendToKitchen = () => {
|
||||
setLoadingAction('sendToKitchen')
|
||||
onSendToKitchen()
|
||||
useEffect(() => {
|
||||
if (!isChangingStatus) {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
}, [isChangingStatus]);
|
||||
|
||||
const handleDeliverToCourier = () => {
|
||||
setLoadingAction('deliverToCourier')
|
||||
onDeliverToCourier()
|
||||
}
|
||||
const handleSendToKitchen = () => {
|
||||
setLoadingAction("sendToKitchen");
|
||||
onSendToKitchen();
|
||||
};
|
||||
|
||||
const handleDeliverToWaiter = () => {
|
||||
setLoadingAction('deliverToWaiter')
|
||||
onDeliverToWaiter()
|
||||
}
|
||||
const handleDeliverToCourier = () => {
|
||||
setLoadingAction("deliverToCourier");
|
||||
onDeliverToCourier();
|
||||
};
|
||||
|
||||
const handleDeliverToReceptionist = () => {
|
||||
setLoadingAction('deliverToReceptionist')
|
||||
onDeliverToReceptionist()
|
||||
}
|
||||
const handleDeliverToWaiter = () => {
|
||||
setLoadingAction("deliverToWaiter");
|
||||
onDeliverToWaiter();
|
||||
};
|
||||
|
||||
const handleCancelOrder = () => {
|
||||
setLoadingAction('cancelOrder')
|
||||
onCancelOrder()
|
||||
}
|
||||
const handleDeliverToReceptionist = () => {
|
||||
setLoadingAction("deliverToReceptionist");
|
||||
onDeliverToReceptionist();
|
||||
};
|
||||
|
||||
const handleCompleteOrder = () => {
|
||||
setLoadingAction('completeOrder')
|
||||
onCompleteOrder()
|
||||
}
|
||||
const handleCancelOrder = () => {
|
||||
setLoadingAction("cancelOrder");
|
||||
onCancelOrder();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='mt-5 sm:mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-3'>
|
||||
<Button
|
||||
label='چاپ'
|
||||
onClick={onPrint}
|
||||
className='bg-slate-700 hover:bg-slate-800'
|
||||
/>
|
||||
const handleCompleteOrder = () => {
|
||||
setLoadingAction("completeOrder");
|
||||
onCompleteOrder();
|
||||
};
|
||||
|
||||
{!isCanceled && showSendToKitchenButton && (
|
||||
<Button
|
||||
label='ارسال به آشپزخانه'
|
||||
onClick={handleSendToKitchen}
|
||||
isloading={isChangingStatus && loadingAction === 'sendToKitchen'}
|
||||
className='bg-orange-600 hover:bg-orange-700'
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<div className="mt-5 sm:mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-3">
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
label="چاپ"
|
||||
onClick={onPrint}
|
||||
className="border bg-white border-slate-700 text-slate-700 hover:bg-slate-100"
|
||||
/>
|
||||
|
||||
{!isCanceled && isPreparing && isCourierDelivery && (
|
||||
<Button
|
||||
label='تحویل به پیک'
|
||||
onClick={handleDeliverToCourier}
|
||||
isloading={isChangingStatus && loadingAction === 'deliverToCourier'}
|
||||
className='bg-blue-600 hover:bg-blue-700'
|
||||
/>
|
||||
)}
|
||||
{canSendPleaseComeSms && (
|
||||
<Button
|
||||
label="ارسال پیام"
|
||||
onClick={onSendPleaseComeSms}
|
||||
isloading={isSendingPleaseComeSms}
|
||||
className="border bg-white border-teal-600 text-teal-600 hover:bg-teal-100"
|
||||
/>
|
||||
)}
|
||||
{!isCanceled && showCancelButton && (
|
||||
<Button
|
||||
label="لغو سفارش"
|
||||
onClick={handleCancelOrder}
|
||||
isloading={isChangingStatus && loadingAction === "cancelOrder"}
|
||||
className="bg-transparent border border-red-500 text-red-500 hover:bg-red-100 "
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isCanceled && isPreparing && isDineInOrCarDelivery && (
|
||||
<Button
|
||||
label='تحویل به گارسون'
|
||||
onClick={handleDeliverToWaiter}
|
||||
isloading={isChangingStatus && loadingAction === 'deliverToWaiter'}
|
||||
className='bg-purple-600 hover:bg-purple-700'
|
||||
/>
|
||||
)}
|
||||
{!isCanceled && showSendToKitchenButton && (
|
||||
<Button
|
||||
label="ارسال به آشپزخانه"
|
||||
onClick={handleSendToKitchen}
|
||||
isloading={isChangingStatus && loadingAction === "sendToKitchen"}
|
||||
className="bg-orange-600 hover:bg-orange-700"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isCanceled && isPreparing && isCustomerPickup && (
|
||||
<Button
|
||||
label='تحویل به رسپشنیست'
|
||||
onClick={handleDeliverToReceptionist}
|
||||
isloading={isChangingStatus && loadingAction === 'deliverToReceptionist'}
|
||||
className='bg-indigo-600 hover:bg-indigo-700'
|
||||
/>
|
||||
)}
|
||||
{!isCanceled && isPreparing && isCourierDelivery && (
|
||||
<Button
|
||||
label="تحویل به پیک"
|
||||
onClick={handleDeliverToCourier}
|
||||
isloading={isChangingStatus && loadingAction === "deliverToCourier"}
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isCanceled && canCompleteOrder && (
|
||||
<Button
|
||||
label='اتمام سفارش'
|
||||
onClick={handleCompleteOrder}
|
||||
isloading={isChangingStatus && loadingAction === 'completeOrder'}
|
||||
className='bg-green-600 hover:bg-green-700'
|
||||
/>
|
||||
)}
|
||||
{!isCanceled && isPreparing && isDineInOrCarDelivery && (
|
||||
<Button
|
||||
label="تحویل به گارسون"
|
||||
onClick={handleDeliverToWaiter}
|
||||
isloading={isChangingStatus && loadingAction === "deliverToWaiter"}
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isCanceled && showCancelButton && (
|
||||
<Button
|
||||
label='لغو سفارش'
|
||||
onClick={handleCancelOrder}
|
||||
isloading={isChangingStatus && loadingAction === 'cancelOrder'}
|
||||
className='bg-transparent border border-red-500 text-red-500 hover:bg-red-50 sm:col-span-2 lg:col-span-1'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{!isCanceled && isPreparing && isCustomerPickup && (
|
||||
<Button
|
||||
label="تحویل به رسپشنیست"
|
||||
onClick={handleDeliverToReceptionist}
|
||||
isloading={
|
||||
isChangingStatus && loadingAction === "deliverToReceptionist"
|
||||
}
|
||||
className="bg-indigo-600 hover:bg-indigo-700"
|
||||
/>
|
||||
)}
|
||||
|
||||
export default OrderActions
|
||||
{!isCanceled && canCompleteOrder && (
|
||||
<Button
|
||||
label="اتمام سفارش"
|
||||
onClick={handleCompleteOrder}
|
||||
isloading={isChangingStatus && loadingAction === "completeOrder"}
|
||||
className="bg-green-600 hover:bg-green-700"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderActions;
|
||||
|
||||
@@ -1,146 +1,189 @@
|
||||
import { Eye } from 'iconsax-react'
|
||||
import type { ColumnType } from '@/components/types/TableTypes'
|
||||
import type { Order } from '../types/Types'
|
||||
import { formatPrice, formatOptionalDate } from '@/helpers/func'
|
||||
import Status from '@/components/Status'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '@/config/Pages'
|
||||
import { formatFaNumber } from '@/helpers/func'
|
||||
import type { TFunction } from 'i18next'
|
||||
import { Eye, MessageText } from "iconsax-react";
|
||||
import type { ColumnType } from "@/components/types/TableTypes";
|
||||
import type { Order } from "../types/Types";
|
||||
import { formatPrice, formatOptionalDate } from "@/helpers/func";
|
||||
import Status from "@/components/Status";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Pages } from "@/config/Pages";
|
||||
import { formatFaNumber } from "@/helpers/func";
|
||||
import type { TFunction } from "i18next";
|
||||
|
||||
interface GetOrderTableColumnsParams {
|
||||
onDelete?: (id: string) => void
|
||||
isDeleting?: boolean
|
||||
t: TFunction
|
||||
onDelete?: (id: string) => void;
|
||||
isDeleting?: boolean;
|
||||
onSendPleaseComeSms?: (order: Order) => void;
|
||||
t: TFunction;
|
||||
}
|
||||
|
||||
const getStatusVariant = (status: string): 'success' | 'error' | 'warning' | 'info' | 'pending' => {
|
||||
const variantMap: Record<string, 'success' | 'error' | 'warning' | 'info' | 'pending'> = {
|
||||
'new': 'pending',
|
||||
'preparing': 'warning',
|
||||
'shipped': 'info',
|
||||
'completed': 'success',
|
||||
'canceled': 'error',
|
||||
}
|
||||
return variantMap[status] || 'pending'
|
||||
}
|
||||
const getStatusVariant = (
|
||||
status: string,
|
||||
): "success" | "error" | "warning" | "info" | "pending" => {
|
||||
const variantMap: Record<
|
||||
string,
|
||||
"success" | "error" | "warning" | "info" | "pending"
|
||||
> = {
|
||||
new: "pending",
|
||||
preparing: "warning",
|
||||
shipped: "info",
|
||||
completed: "success",
|
||||
canceled: "error",
|
||||
};
|
||||
return variantMap[status] || "pending";
|
||||
};
|
||||
|
||||
const getShiftAdminName = (item: Order): string => {
|
||||
const admin = item.cashShift?.admin
|
||||
if (!admin) return '-'
|
||||
const name = [admin.firstName, admin.lastName].filter(Boolean).join(' ')
|
||||
return name || admin.phone || '-'
|
||||
}
|
||||
const admin = item.cashShift?.admin;
|
||||
if (!admin) return "-";
|
||||
const name = [admin.firstName, admin.lastName].filter(Boolean).join(" ");
|
||||
return name || admin.phone || "-";
|
||||
};
|
||||
|
||||
export const getOrderTableColumns = ({ t }: GetOrderTableColumnsParams): ColumnType<Order>[] => {
|
||||
return [
|
||||
{
|
||||
key: 'orderNumber',
|
||||
title: 'شماره',
|
||||
render: (item: Order) => {
|
||||
return formatFaNumber(item.orderNumber)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'customerName',
|
||||
title: 'نام مشتری',
|
||||
render: (item: Order) => {
|
||||
const name = [item.user?.firstName, item.user?.lastName].filter(Boolean).join(' ')
|
||||
return name || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'shiftAdmin',
|
||||
title: 'شیفت',
|
||||
render: (item: Order) => getShiftAdminName(item),
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
title: 'تاریخ',
|
||||
render: (item: Order) => {
|
||||
if (!item.createdAt) return '-'
|
||||
return (
|
||||
<div className="flex flex-col whitespace-nowrap">
|
||||
<span className="text-[11px] text-description">
|
||||
{formatOptionalDate(item.createdAt, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
<span className="text-[15px] font-medium">
|
||||
{formatOptionalDate(item.createdAt, {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'methods',
|
||||
title: 'تحویل / پرداخت',
|
||||
render: (item: Order) => {
|
||||
const rows = [
|
||||
{ label: 'نوع تحویل', value: item.deliveryMethod?.description || '-' },
|
||||
{ label: 'نوع پرداخت', value: item.paymentMethod?.description || '-' },
|
||||
]
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
|
||||
{rows.map((row) => (
|
||||
<div key={row.label} className="flex items-center justify-between gap-3">
|
||||
<span className="text-description">{row.label}</span>
|
||||
<span>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'amounts',
|
||||
title: 'مبالغ',
|
||||
render: (item: Order) => {
|
||||
const rows = [
|
||||
{ label: 'مبلغ کل', value: formatPrice(item.total) },
|
||||
{ label: 'پرداخت شده', value: formatPrice(Number(item.paidAmount ?? 0)) },
|
||||
{ label: 'مانده', value: formatPrice(Number(item.balance ?? 0)) },
|
||||
]
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
|
||||
{rows.map((row) => (
|
||||
<div key={row.label} className="flex items-center justify-between gap-3">
|
||||
<span className="text-description">{row.label}</span>
|
||||
<span>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: 'وضعیت',
|
||||
render: (item: Order) => {
|
||||
return (
|
||||
<Status
|
||||
variant={getStatusVariant(item.status)}
|
||||
label={t(`order_status.${item.status}`)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'details',
|
||||
title: '',
|
||||
render: (item: Order) => {
|
||||
return (
|
||||
<Link to={Pages.orders.detail + item.id}>
|
||||
<Eye size={20} color="#8C90A3" className="cursor-pointer" />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
export const getOrderTableColumns = ({
|
||||
t,
|
||||
onSendPleaseComeSms,
|
||||
}: GetOrderTableColumnsParams): ColumnType<Order>[] => {
|
||||
return [
|
||||
{
|
||||
key: "orderNumber",
|
||||
title: "شماره",
|
||||
render: (item: Order) => {
|
||||
return formatFaNumber(item.orderNumber);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "customerName",
|
||||
title: "نام مشتری",
|
||||
render: (item: Order) => {
|
||||
const name = [item.user?.firstName, item.user?.lastName]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
return name || "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "shiftAdmin",
|
||||
title: "شیفت",
|
||||
render: (item: Order) => getShiftAdminName(item),
|
||||
},
|
||||
{
|
||||
key: "createdAt",
|
||||
title: "تاریخ",
|
||||
render: (item: Order) => {
|
||||
if (!item.createdAt) return "-";
|
||||
return (
|
||||
<div className="flex flex-col whitespace-nowrap">
|
||||
<span className="text-[11px] text-description">
|
||||
{formatOptionalDate(item.createdAt, {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
})}
|
||||
</span>
|
||||
<span className="text-[15px] font-medium">
|
||||
{formatOptionalDate(item.createdAt, {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "methods",
|
||||
title: "تحویل / پرداخت",
|
||||
render: (item: Order) => {
|
||||
const rows = [
|
||||
{
|
||||
label: "نوع تحویل",
|
||||
value: item.deliveryMethod?.description || "-",
|
||||
},
|
||||
{
|
||||
label: "نوع پرداخت",
|
||||
value: item.paymentMethod?.description || "-",
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
|
||||
{rows.map((row) => (
|
||||
<div
|
||||
key={row.label}
|
||||
className="flex items-center justify-between gap-3"
|
||||
>
|
||||
<span className="text-description">{row.label}</span>
|
||||
<span>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "amounts",
|
||||
title: "مبالغ",
|
||||
render: (item: Order) => {
|
||||
const rows = [
|
||||
{ label: "مبلغ کل", value: formatPrice(item.total) },
|
||||
{
|
||||
label: "پرداخت شده",
|
||||
value: formatPrice(Number(item.paidAmount ?? 0)),
|
||||
},
|
||||
{ label: "مانده", value: formatPrice(Number(item.balance ?? 0)) },
|
||||
];
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
|
||||
{rows.map((row) => (
|
||||
<div
|
||||
key={row.label}
|
||||
className="flex items-center justify-between gap-3"
|
||||
>
|
||||
<span className="text-description">{row.label}</span>
|
||||
<span>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
title: "وضعیت",
|
||||
render: (item: Order) => {
|
||||
return (
|
||||
<Status
|
||||
variant={getStatusVariant(item.status)}
|
||||
label={t(`order_status.${item.status}`)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
title: "",
|
||||
render: (item: Order) => {
|
||||
const canSendSms = Boolean(item.user?.phone);
|
||||
return (
|
||||
<div className="flex items-center gap-3 whitespace-nowrap">
|
||||
{canSendSms && onSendPleaseComeSms && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSendPleaseComeSms(item)}
|
||||
className="cursor-pointer text-[12px] text-teal-700 hover:text-teal-800 font-medium"
|
||||
title="ارسال پیام به مشتری"
|
||||
>
|
||||
<MessageText
|
||||
size={20}
|
||||
color="#8C90A3"
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<Link to={Pages.orders.detail + item.id} title="جزئیات">
|
||||
<Eye size={20} color="#8C90A3" className="cursor-pointer" />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { type FC } from 'react'
|
||||
import DefaulModal from '@/components/DefaulModal'
|
||||
import Button from '@/components/Button'
|
||||
|
||||
interface SendPleaseComeSmsModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
isLoading?: boolean
|
||||
customerName: string
|
||||
restaurantName: string
|
||||
onConfirm: () => void
|
||||
}
|
||||
|
||||
const SendPleaseComeSmsModal: FC<SendPleaseComeSmsModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
isLoading,
|
||||
customerName,
|
||||
restaurantName,
|
||||
onConfirm,
|
||||
}) => {
|
||||
const previewMessage = `${customerName || '#NAME#'} عزیز لطفا به پیشخوان ${restaurantName || '#REST#'} مراجعه کنید.`
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={isOpen}
|
||||
close={onClose}
|
||||
isHeader
|
||||
title_header='ارسال پیام'
|
||||
width={420}
|
||||
>
|
||||
<div className='mt-6'>
|
||||
<p className='text-sm text-description text-center mb-4'>
|
||||
متن پیامک:
|
||||
</p>
|
||||
<div className='rounded-xl border border-gray-200 bg-gray-50 px-4 py-5 text-center text-[15px] leading-8'>
|
||||
{previewMessage}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-4 justify-center mt-10'>
|
||||
<Button
|
||||
label='ارسال'
|
||||
onClick={onConfirm}
|
||||
isloading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
label='انصراف'
|
||||
className='bg-transparent text-black border border-primary'
|
||||
onClick={onClose}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default SendPleaseComeSmsModal
|
||||
@@ -155,3 +155,9 @@ export const useRemoveOrderItem = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useSendPleaseComeSms = () => {
|
||||
return useMutation({
|
||||
mutationFn: (orderId: string) => api.sendPleaseComeSms(orderId),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -191,3 +191,10 @@ export const getDailyOrderReport = async (
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const sendPleaseComeSms = async (orderId: string): Promise<{ message: string }> => {
|
||||
const { data } = await axios.post<{ message: string }>(
|
||||
`/admin/orders/${orderId}/please-come`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user