send sms to user in order detail
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-26 11:56:21 +03:30
parent 6dea561b05
commit 7ad840fbda
7 changed files with 471 additions and 267 deletions
+43 -1
View File
@@ -9,12 +9,16 @@ import {
useAddOrderItem, useAddOrderItem,
useUpdateOrderItemQuantity, useUpdateOrderItemQuantity,
useRemoveOrderItem, useRemoveOrderItem,
useSendPleaseComeSms,
} from './hooks/useOrderData' } 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 { formatFaNumber } from '@/helpers/func'
import { Pages } from '@/config/Pages'
import { OrderStatus, PaymentStatusEnum } from './enum/Enum' import { OrderStatus, PaymentStatusEnum } from './enum/Enum'
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum' import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum' import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum'
import Button from '@/components/Button'
import ModalConfrim from '@/components/ModalConfrim' import ModalConfrim from '@/components/ModalConfrim'
import CustomerInfo from './components/CustomerInfo' import CustomerInfo from './components/CustomerInfo'
import OrderItemsDetails from './components/OrderItemsDetails' import OrderItemsDetails from './components/OrderItemsDetails'
@@ -25,6 +29,7 @@ import UpdateOrderFeesModal from './components/UpdateOrderFeesModal'
import AddOrderItemModal from './components/AddOrderItemModal' import AddOrderItemModal from './components/AddOrderItemModal'
import RefundPaymentModal from './components/RefundPaymentModal' import RefundPaymentModal from './components/RefundPaymentModal'
import AddPaymentModal from './components/AddPaymentModal' import AddPaymentModal from './components/AddPaymentModal'
import SendPleaseComeSmsModal from './components/SendPleaseComeSmsModal'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
import { printOrderReceipt } from './print/orderReceiptPrint' import { printOrderReceipt } from './print/orderReceiptPrint'
@@ -32,6 +37,7 @@ import type { UpdateOrderFeesType } from './types/Types'
const OrderDetails: FC = () => { const OrderDetails: FC = () => {
const { id } = useParams() const { id } = useParams()
const navigate = useNavigate()
const { data: orderData, isLoading } = useGetOrderById(id!) const { data: orderData, isLoading } = useGetOrderById(id!)
const { mutate: verifyPayment, isPending: isVerifyingPayment } = useVerifyPayment() const { mutate: verifyPayment, isPending: isVerifyingPayment } = useVerifyPayment()
const { mutate: changeOrderStatus, isPending: isChangingStatus } = useChangeOrderStatus() const { mutate: changeOrderStatus, isPending: isChangingStatus } = useChangeOrderStatus()
@@ -41,8 +47,10 @@ const OrderDetails: FC = () => {
const { mutate: addOrderItem, isPending: isAddingItem } = useAddOrderItem() const { mutate: addOrderItem, isPending: isAddingItem } = useAddOrderItem()
const { mutate: updateOrderItemQuantity, isPending: isUpdatingItemQuantity } = useUpdateOrderItemQuantity() const { mutate: updateOrderItemQuantity, isPending: isUpdatingItemQuantity } = useUpdateOrderItemQuantity()
const { mutate: removeOrderItem, isPending: isRemovingItem } = useRemoveOrderItem() const { mutate: removeOrderItem, isPending: isRemovingItem } = useRemoveOrderItem()
const { mutate: sendPleaseComeSms, isPending: isSendingPleaseComeSms } = useSendPleaseComeSms()
const [showCancelModal, setShowCancelModal] = useState(false) const [showCancelModal, setShowCancelModal] = useState(false)
const [showPleaseComeSmsModal, setShowPleaseComeSmsModal] = useState(false)
const [refundModal, setRefundModal] = useState<{ defaultAmount: number; maxAmount: number } | null>(null) const [refundModal, setRefundModal] = useState<{ defaultAmount: number; maxAmount: number } | null>(null)
const [showAddPaymentModal, setShowAddPaymentModal] = useState(false) const [showAddPaymentModal, setShowAddPaymentModal] = useState(false)
const [showEditFeesModal, setShowEditFeesModal] = useState(false) const [showEditFeesModal, setShowEditFeesModal] = useState(false)
@@ -98,6 +106,7 @@ const OrderDetails: FC = () => {
OrderStatus.DELIVERED_TO_RECEPTIONIST, OrderStatus.DELIVERED_TO_RECEPTIONIST,
OrderStatus.SHIPPED, OrderStatus.SHIPPED,
].includes(order.status as OrderStatus) ].includes(order.status as OrderStatus)
const canSendPleaseComeSms = Boolean(order.user?.phone)
const isCourierDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCourier const isCourierDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCourier
const isDineInOrCarDelivery = const isDineInOrCarDelivery =
order.deliveryMethod?.method === DeliveryMethodEnum.DineIn || 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 ( return (
<div className='mt-5 pb-8'> <div className='mt-5 pb-8'>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<h1 className='text-lg font-light'> <h1 className='text-lg font-light'>
سفارش {formatFaNumber(order.orderNumber)} سفارش {formatFaNumber(order.orderNumber)}
</h1> </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>
<div className='flex flex-col lg:flex-row gap-5 lg:gap-7 mt-6 sm:mt-9'> <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} isCustomerPickup={isCustomerPickup}
isCanceled={isCanceled} isCanceled={isCanceled}
canCompleteOrder={canCompleteOrder} canCompleteOrder={canCompleteOrder}
canSendPleaseComeSms={canSendPleaseComeSms}
isChangingStatus={isChangingStatus} isChangingStatus={isChangingStatus}
isSendingPleaseComeSms={isSendingPleaseComeSms}
onSendToKitchen={handleSendToKitchen} onSendToKitchen={handleSendToKitchen}
onDeliverToCourier={handleDeliverToCourier} onDeliverToCourier={handleDeliverToCourier}
onDeliverToWaiter={handleDeliverToWaiter} onDeliverToWaiter={handleDeliverToWaiter}
onDeliverToReceptionist={handleDeliverToReceptionist} onDeliverToReceptionist={handleDeliverToReceptionist}
onCompleteOrder={handleCompleteOrder} onCompleteOrder={handleCompleteOrder}
onCancelOrder={() => setShowCancelModal(true)} onCancelOrder={() => setShowCancelModal(true)}
onSendPleaseComeSms={() => setShowPleaseComeSmsModal(true)}
/> />
</div> </div>
</div> </div>
@@ -416,6 +449,15 @@ const OrderDetails: FC = () => {
isloading={isRemovingItem} isloading={isRemovingItem}
label='آیا از حذف این آیتم مطمئن هستید؟' label='آیا از حذف این آیتم مطمئن هستید؟'
/> />
<SendPleaseComeSmsModal
isOpen={showPleaseComeSmsModal}
onClose={() => setShowPleaseComeSmsModal(false)}
isLoading={isSendingPleaseComeSms}
customerName={order.user?.firstName || ''}
restaurantName={order.restaurant?.name || ''}
onConfirm={handleConfirmPleaseComeSms}
/>
</div> </div>
) )
} }
+34 -3
View File
@@ -1,4 +1,4 @@
import { type FC } from 'react' import { type FC, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Add } from 'iconsax-react' import { Add } from 'iconsax-react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -6,12 +6,15 @@ import Table from '@/components/Table'
import Filters from '@/components/Filters' import Filters from '@/components/Filters'
import Button from '@/components/Button' import Button from '@/components/Button'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import { useGetOrders } from './hooks/useOrderData' import { useGetOrders, useSendPleaseComeSms } from './hooks/useOrderData'
import { useOrderFilters } from './hooks/useOrderFilters' import { useOrderFilters } from './hooks/useOrderFilters'
import { getOrderTableColumns } from './components/OrderTableColumns' import { getOrderTableColumns } from './components/OrderTableColumns'
import { useOrderFiltersFields } from './components/OrderFiltersFields' import { useOrderFiltersFields } from './components/OrderFiltersFields'
import SendPleaseComeSmsModal from './components/SendPleaseComeSmsModal'
import type { Order } from './types/Types' import type { Order } from './types/Types'
import type { RowDataType } from '@/components/types/TableTypes' import type { RowDataType } from '@/components/types/TableTypes'
import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/config/func'
const OrdersList: FC = () => { const OrdersList: FC = () => {
const { t } = useTranslation('global') const { t } = useTranslation('global')
@@ -24,13 +27,32 @@ const OrdersList: FC = () => {
} = useOrderFilters() } = useOrderFilters()
const { data: ordersData, isLoading } = useGetOrders(apiParams) const { data: ordersData, isLoading } = useGetOrders(apiParams)
const { mutate: sendPleaseComeSms, isPending: isSendingSms } = useSendPleaseComeSms()
const [smsOrder, setSmsOrder] = useState<Order | null>(null)
const orders = ordersData?.data || [] const orders = ordersData?.data || []
const columns = getOrderTableColumns({ t }) const columns = getOrderTableColumns({
t,
onSendPleaseComeSms: (order) => setSmsOrder(order),
})
const filterFields = useOrderFiltersFields() const filterFields = useOrderFiltersFields()
const totalPages = ordersData?.meta?.totalPages || 1 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 ( return (
<div className='mt-5'> <div className='mt-5'>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
@@ -64,6 +86,15 @@ const OrdersList: FC = () => {
onPageChange: handlePageChange, onPageChange: handlePageChange,
}} }}
/> />
<SendPleaseComeSmsModal
isOpen={!!smsOrder}
onClose={() => setSmsOrder(null)}
isLoading={isSendingSms}
customerName={smsOrder?.user?.firstName || ''}
restaurantName={smsOrder?.restaurant?.name || ''}
onConfirm={handleConfirmSendSms}
/>
</div> </div>
) )
} }
+141 -123
View File
@@ -1,144 +1,162 @@
import Button from '@/components/Button' import Button from "@/components/Button";
import { type FC, useState, useEffect } from 'react' import { type FC, useState, useEffect } from "react";
interface OrderActionsProps { interface OrderActionsProps {
onPrint: () => void onPrint: () => void;
showSendToKitchenButton: boolean showSendToKitchenButton: boolean;
showCancelButton: boolean showCancelButton: boolean;
isPreparing: boolean isPreparing: boolean;
isCourierDelivery: boolean isCourierDelivery: boolean;
isDineInOrCarDelivery: boolean isDineInOrCarDelivery: boolean;
isCustomerPickup: boolean isCustomerPickup: boolean;
isCanceled: boolean isCanceled: boolean;
canCompleteOrder: boolean canCompleteOrder: boolean;
isChangingStatus: boolean canSendPleaseComeSms: boolean;
onSendToKitchen: () => void isChangingStatus: boolean;
onDeliverToCourier: () => void isSendingPleaseComeSms?: boolean;
onDeliverToWaiter: () => void onSendToKitchen: () => void;
onDeliverToReceptionist: () => void onDeliverToCourier: () => void;
onCompleteOrder: () => void onDeliverToWaiter: () => void;
onCancelOrder: () => void onDeliverToReceptionist: () => void;
onCompleteOrder: () => void;
onCancelOrder: () => void;
onSendPleaseComeSms: () => void;
} }
const OrderActions: FC<OrderActionsProps> = ({ const OrderActions: FC<OrderActionsProps> = ({
onPrint, onPrint,
showSendToKitchenButton, showSendToKitchenButton,
showCancelButton, showCancelButton,
isPreparing, isPreparing,
isCourierDelivery, isCourierDelivery,
isDineInOrCarDelivery, isDineInOrCarDelivery,
isCustomerPickup, isCustomerPickup,
isCanceled, isCanceled,
canCompleteOrder, canCompleteOrder,
isChangingStatus, canSendPleaseComeSms,
onSendToKitchen, isChangingStatus,
onDeliverToCourier, isSendingPleaseComeSms,
onDeliverToWaiter, onSendToKitchen,
onDeliverToReceptionist, onDeliverToCourier,
onCompleteOrder, onDeliverToWaiter,
onCancelOrder, onDeliverToReceptionist,
onCompleteOrder,
onCancelOrder,
onSendPleaseComeSms,
}) => { }) => {
const [loadingAction, setLoadingAction] = useState<string | null>(null) const [loadingAction, setLoadingAction] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!isChangingStatus) { if (!isChangingStatus) {
setLoadingAction(null) setLoadingAction(null);
}
}, [isChangingStatus])
const handleSendToKitchen = () => {
setLoadingAction('sendToKitchen')
onSendToKitchen()
} }
}, [isChangingStatus]);
const handleDeliverToCourier = () => { const handleSendToKitchen = () => {
setLoadingAction('deliverToCourier') setLoadingAction("sendToKitchen");
onDeliverToCourier() onSendToKitchen();
} };
const handleDeliverToWaiter = () => { const handleDeliverToCourier = () => {
setLoadingAction('deliverToWaiter') setLoadingAction("deliverToCourier");
onDeliverToWaiter() onDeliverToCourier();
} };
const handleDeliverToReceptionist = () => { const handleDeliverToWaiter = () => {
setLoadingAction('deliverToReceptionist') setLoadingAction("deliverToWaiter");
onDeliverToReceptionist() onDeliverToWaiter();
} };
const handleCancelOrder = () => { const handleDeliverToReceptionist = () => {
setLoadingAction('cancelOrder') setLoadingAction("deliverToReceptionist");
onCancelOrder() onDeliverToReceptionist();
} };
const handleCompleteOrder = () => { const handleCancelOrder = () => {
setLoadingAction('completeOrder') setLoadingAction("cancelOrder");
onCompleteOrder() onCancelOrder();
} };
return ( const handleCompleteOrder = () => {
<div className='mt-5 sm:mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-3'> setLoadingAction("completeOrder");
<Button onCompleteOrder();
label='چاپ' };
onClick={onPrint}
className='bg-slate-700 hover:bg-slate-800'
/>
{!isCanceled && showSendToKitchenButton && ( return (
<Button <div className="mt-5 sm:mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-3">
label='ارسال به آشپزخانه' <div className="flex gap-3">
onClick={handleSendToKitchen} <Button
isloading={isChangingStatus && loadingAction === 'sendToKitchen'} label="چاپ"
className='bg-orange-600 hover:bg-orange-700' onClick={onPrint}
/> className="border bg-white border-slate-700 text-slate-700 hover:bg-slate-100"
)} />
{!isCanceled && isPreparing && isCourierDelivery && ( {canSendPleaseComeSms && (
<Button <Button
label='تحویل به پیک' label="ارسال پیام"
onClick={handleDeliverToCourier} onClick={onSendPleaseComeSms}
isloading={isChangingStatus && loadingAction === 'deliverToCourier'} isloading={isSendingPleaseComeSms}
className='bg-blue-600 hover:bg-blue-700' 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 && ( {!isCanceled && showSendToKitchenButton && (
<Button <Button
label='تحویل به گارسون' label="ارسال به آشپزخانه"
onClick={handleDeliverToWaiter} onClick={handleSendToKitchen}
isloading={isChangingStatus && loadingAction === 'deliverToWaiter'} isloading={isChangingStatus && loadingAction === "sendToKitchen"}
className='bg-purple-600 hover:bg-purple-700' className="bg-orange-600 hover:bg-orange-700"
/> />
)} )}
{!isCanceled && isPreparing && isCustomerPickup && ( {!isCanceled && isPreparing && isCourierDelivery && (
<Button <Button
label='تحویل به رسپشنیست' label="تحویل به پیک"
onClick={handleDeliverToReceptionist} onClick={handleDeliverToCourier}
isloading={isChangingStatus && loadingAction === 'deliverToReceptionist'} isloading={isChangingStatus && loadingAction === "deliverToCourier"}
className='bg-indigo-600 hover:bg-indigo-700' className="bg-blue-600 hover:bg-blue-700"
/> />
)} )}
{!isCanceled && canCompleteOrder && ( {!isCanceled && isPreparing && isDineInOrCarDelivery && (
<Button <Button
label='اتمام سفارش' label="تحویل به گارسون"
onClick={handleCompleteOrder} onClick={handleDeliverToWaiter}
isloading={isChangingStatus && loadingAction === 'completeOrder'} isloading={isChangingStatus && loadingAction === "deliverToWaiter"}
className='bg-green-600 hover:bg-green-700' className="bg-purple-600 hover:bg-purple-700"
/> />
)} )}
{!isCanceled && showCancelButton && ( {!isCanceled && isPreparing && isCustomerPickup && (
<Button <Button
label='لغو سفارش' label="تحویل به رسپشنیست"
onClick={handleCancelOrder} onClick={handleDeliverToReceptionist}
isloading={isChangingStatus && loadingAction === 'cancelOrder'} isloading={
className='bg-transparent border border-red-500 text-red-500 hover:bg-red-50 sm:col-span-2 lg:col-span-1' isChangingStatus && loadingAction === "deliverToReceptionist"
/> }
)} className="bg-indigo-600 hover:bg-indigo-700"
</div> />
) )}
}
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;
+182 -139
View File
@@ -1,146 +1,189 @@
import { Eye } from 'iconsax-react' import { Eye, MessageText } from "iconsax-react";
import type { ColumnType } from '@/components/types/TableTypes' import type { ColumnType } from "@/components/types/TableTypes";
import type { Order } from '../types/Types' import type { Order } from "../types/Types";
import { formatPrice, formatOptionalDate } from '@/helpers/func' import { formatPrice, formatOptionalDate } from "@/helpers/func";
import Status from '@/components/Status' import Status from "@/components/Status";
import { Link } from 'react-router-dom' import { Link } from "react-router-dom";
import { Pages } from '@/config/Pages' import { Pages } from "@/config/Pages";
import { formatFaNumber } from '@/helpers/func' import { formatFaNumber } from "@/helpers/func";
import type { TFunction } from 'i18next' import type { TFunction } from "i18next";
interface GetOrderTableColumnsParams { interface GetOrderTableColumnsParams {
onDelete?: (id: string) => void onDelete?: (id: string) => void;
isDeleting?: boolean isDeleting?: boolean;
t: TFunction onSendPleaseComeSms?: (order: Order) => void;
t: TFunction;
} }
const getStatusVariant = (status: string): 'success' | 'error' | 'warning' | 'info' | 'pending' => { const getStatusVariant = (
const variantMap: Record<string, 'success' | 'error' | 'warning' | 'info' | 'pending'> = { status: string,
'new': 'pending', ): "success" | "error" | "warning" | "info" | "pending" => {
'preparing': 'warning', const variantMap: Record<
'shipped': 'info', string,
'completed': 'success', "success" | "error" | "warning" | "info" | "pending"
'canceled': 'error', > = {
} new: "pending",
return variantMap[status] || 'pending' preparing: "warning",
} shipped: "info",
completed: "success",
canceled: "error",
};
return variantMap[status] || "pending";
};
const getShiftAdminName = (item: Order): string => { const getShiftAdminName = (item: Order): string => {
const admin = item.cashShift?.admin const admin = item.cashShift?.admin;
if (!admin) return '-' if (!admin) return "-";
const name = [admin.firstName, admin.lastName].filter(Boolean).join(' ') const name = [admin.firstName, admin.lastName].filter(Boolean).join(" ");
return name || admin.phone || '-' return name || admin.phone || "-";
} };
export const getOrderTableColumns = ({ t }: GetOrderTableColumnsParams): ColumnType<Order>[] => { export const getOrderTableColumns = ({
return [ t,
{ onSendPleaseComeSms,
key: 'orderNumber', }: GetOrderTableColumnsParams): ColumnType<Order>[] => {
title: 'شماره', return [
render: (item: Order) => { {
return formatFaNumber(item.orderNumber) 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(' ') key: "customerName",
return name || '-' title: "نام مشتری",
} render: (item: Order) => {
}, const name = [item.user?.firstName, item.user?.lastName]
{ .filter(Boolean)
key: 'shiftAdmin', .join(" ");
title: 'شیفت', return name || "-";
render: (item: Order) => getShiftAdminName(item), },
}, },
{ {
key: 'createdAt', key: "shiftAdmin",
title: 'تاریخ', title: "شیفت",
render: (item: Order) => { render: (item: Order) => getShiftAdminName(item),
if (!item.createdAt) return '-' },
return ( {
<div className="flex flex-col whitespace-nowrap"> key: "createdAt",
<span className="text-[11px] text-description"> title: "تاریخ",
{formatOptionalDate(item.createdAt, { render: (item: Order) => {
year: 'numeric', if (!item.createdAt) return "-";
month: '2-digit', return (
day: '2-digit', <div className="flex flex-col whitespace-nowrap">
})} <span className="text-[11px] text-description">
</span> {formatOptionalDate(item.createdAt, {
<span className="text-[15px] font-medium"> year: "numeric",
{formatOptionalDate(item.createdAt, { month: "2-digit",
hour: '2-digit', day: "2-digit",
minute: '2-digit', })}
})} </span>
</span> <span className="text-[15px] font-medium">
</div> {formatOptionalDate(item.createdAt, {
) hour: "2-digit",
} minute: "2-digit",
}, })}
{ </span>
key: 'methods', </div>
title: 'تحویل / پرداخت', );
render: (item: Order) => { },
const rows = [ },
{ label: 'نوع تحویل', value: item.deliveryMethod?.description || '-' }, {
{ label: 'نوع پرداخت', value: item.paymentMethod?.description || '-' }, key: "methods",
] title: "تحویل / پرداخت",
return ( render: (item: Order) => {
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap"> const rows = [
{rows.map((row) => ( {
<div key={row.label} className="flex items-center justify-between gap-3"> label: "نوع تحویل",
<span className="text-description">{row.label}</span> value: item.deliveryMethod?.description || "-",
<span>{row.value}</span> },
</div> {
))} label: "نوع پرداخت",
</div> value: item.paymentMethod?.description || "-",
) },
} ];
}, return (
{ <div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
key: 'amounts', {rows.map((row) => (
title: 'مبالغ', <div
render: (item: Order) => { key={row.label}
const rows = [ className="flex items-center justify-between gap-3"
{ label: 'مبلغ کل', value: formatPrice(item.total) }, >
{ label: 'پرداخت شده', value: formatPrice(Number(item.paidAmount ?? 0)) }, <span className="text-description">{row.label}</span>
{ label: 'مانده', value: formatPrice(Number(item.balance ?? 0)) }, <span>{row.value}</span>
] </div>
return ( ))}
<div className="flex flex-col gap-1 text-[12px] whitespace-nowrap"> </div>
{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> key: "amounts",
))} title: "مبالغ",
</div> render: (item: Order) => {
) const rows = [
} { label: "مبلغ کل", value: formatPrice(item.total) },
}, {
{ label: "پرداخت شده",
key: 'status', value: formatPrice(Number(item.paidAmount ?? 0)),
title: 'وضعیت', },
render: (item: Order) => { { label: "مانده", value: formatPrice(Number(item.balance ?? 0)) },
return ( ];
<Status return (
variant={getStatusVariant(item.status)} <div className="flex flex-col gap-1 text-[12px] whitespace-nowrap">
label={t(`order_status.${item.status}`)} {rows.map((row) => (
/> <div
) key={row.label}
} className="flex items-center justify-between gap-3"
}, >
{ <span className="text-description">{row.label}</span>
key: 'details', <span>{row.value}</span>
title: '', </div>
render: (item: Order) => { ))}
return ( </div>
<Link to={Pages.orders.detail + item.id}> );
<Eye size={20} color="#8C90A3" className="cursor-pointer" /> },
</Link> },
) {
} 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
+6
View File
@@ -155,3 +155,9 @@ export const useRemoveOrderItem = () => {
}, },
}); });
}; };
export const useSendPleaseComeSms = () => {
return useMutation({
mutationFn: (orderId: string) => api.sendPleaseComeSms(orderId),
});
};
+7
View File
@@ -191,3 +191,10 @@ export const getDailyOrderReport = async (
); );
return data; 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;
};