This commit is contained in:
+2
-1
@@ -855,7 +855,8 @@
|
|||||||
"canceled": "لغو شده",
|
"canceled": "لغو شده",
|
||||||
"deliveredToWaiter": "تحویل به گارسون",
|
"deliveredToWaiter": "تحویل به گارسون",
|
||||||
"deliveredToReceptionist": "تحویل به پیشخوان",
|
"deliveredToReceptionist": "تحویل به پیشخوان",
|
||||||
"deliveredToRecepient": "تحویل به گیرنده"
|
"deliveredToRecepient": "تحویل به گیرنده",
|
||||||
|
"needConfirmation": "در انتظار تایید فروشگاه"
|
||||||
},
|
},
|
||||||
"reviews": {
|
"reviews": {
|
||||||
"reviews": "نظرات",
|
"reviews": "نظرات",
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ const OrderDetails: FC = () => {
|
|||||||
|
|
||||||
// منطق نمایش دکمهها
|
// منطق نمایش دکمهها
|
||||||
const isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
|
const isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
|
||||||
|
const isNeedConfirmation = order.status === OrderStatus.NEED_CONFIRMATION
|
||||||
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
|
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
|
||||||
const isOrderPaid = order.status === OrderStatus.PAID
|
const isOrderPaid = order.status === OrderStatus.PAID
|
||||||
const isPreparing = order.status === OrderStatus.PREPARING
|
const isPreparing = order.status === OrderStatus.PREPARING
|
||||||
@@ -159,6 +160,17 @@ const OrderDetails: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleConfirmQuantities = () => {
|
||||||
|
changeOrderStatus({
|
||||||
|
orderId: order.id,
|
||||||
|
status: OrderStatus.PENDING_PAYMENT
|
||||||
|
}, {
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleCancelOrder = (description?: string) => {
|
const handleCancelOrder = (description?: string) => {
|
||||||
changeOrderStatus({
|
changeOrderStatus({
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
@@ -205,7 +217,7 @@ const OrderDetails: FC = () => {
|
|||||||
<div className='flex gap-7 mt-9'>
|
<div className='flex gap-7 mt-9'>
|
||||||
<div className='flex-1 space-y-8'>
|
<div className='flex-1 space-y-8'>
|
||||||
<CustomerInfo order={order} />
|
<CustomerInfo order={order} />
|
||||||
<OrderItemsDetails order={order} getStatusColor={getStatusColor} />
|
<OrderItemsDetails order={order} getStatusColor={getStatusColor} isNeedConfirmation={isNeedConfirmation} iconColor='#7c3aed' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -223,6 +235,7 @@ const OrderDetails: FC = () => {
|
|||||||
isCourierDelivery={isCourierDelivery}
|
isCourierDelivery={isCourierDelivery}
|
||||||
isCustomerPickup={isCustomerPickup}
|
isCustomerPickup={isCustomerPickup}
|
||||||
isCanceled={isCanceled}
|
isCanceled={isCanceled}
|
||||||
|
isNeedConfirmation={isNeedConfirmation}
|
||||||
isVerifyingPayment={isVerifyingPayment}
|
isVerifyingPayment={isVerifyingPayment}
|
||||||
isChangingStatus={isChangingStatus}
|
isChangingStatus={isChangingStatus}
|
||||||
onVerifyCashPayment={handleVerifyCashPayment}
|
onVerifyCashPayment={handleVerifyCashPayment}
|
||||||
@@ -230,6 +243,7 @@ const OrderDetails: FC = () => {
|
|||||||
onDeliverToCourier={handleDeliverToCourier}
|
onDeliverToCourier={handleDeliverToCourier}
|
||||||
onDeliverToReceptionist={handleDeliverToReceptionist}
|
onDeliverToReceptionist={handleDeliverToReceptionist}
|
||||||
onCancelOrder={() => setShowCancelModal(true)}
|
onCancelOrder={() => setShowCancelModal(true)}
|
||||||
|
onConfirmQuantities={handleConfirmQuantities}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface OrderActionsProps {
|
|||||||
isCourierDelivery: boolean
|
isCourierDelivery: boolean
|
||||||
isCustomerPickup: boolean
|
isCustomerPickup: boolean
|
||||||
isCanceled: boolean
|
isCanceled: boolean
|
||||||
|
isNeedConfirmation: boolean
|
||||||
isVerifyingPayment: boolean
|
isVerifyingPayment: boolean
|
||||||
isChangingStatus: boolean
|
isChangingStatus: boolean
|
||||||
onVerifyCashPayment: () => void
|
onVerifyCashPayment: () => void
|
||||||
@@ -16,6 +17,7 @@ interface OrderActionsProps {
|
|||||||
onDeliverToCourier: () => void
|
onDeliverToCourier: () => void
|
||||||
onDeliverToReceptionist: () => void
|
onDeliverToReceptionist: () => void
|
||||||
onCancelOrder: () => void
|
onCancelOrder: () => void
|
||||||
|
onConfirmQuantities: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderActions: FC<OrderActionsProps> = ({
|
const OrderActions: FC<OrderActionsProps> = ({
|
||||||
@@ -26,13 +28,15 @@ const OrderActions: FC<OrderActionsProps> = ({
|
|||||||
isCourierDelivery,
|
isCourierDelivery,
|
||||||
isCustomerPickup,
|
isCustomerPickup,
|
||||||
isCanceled,
|
isCanceled,
|
||||||
|
isNeedConfirmation,
|
||||||
isVerifyingPayment,
|
isVerifyingPayment,
|
||||||
isChangingStatus,
|
isChangingStatus,
|
||||||
onVerifyCashPayment,
|
onVerifyCashPayment,
|
||||||
onSendToKitchen,
|
onSendToKitchen,
|
||||||
onDeliverToCourier,
|
onDeliverToCourier,
|
||||||
onDeliverToReceptionist,
|
onDeliverToReceptionist,
|
||||||
onCancelOrder
|
onCancelOrder,
|
||||||
|
onConfirmQuantities
|
||||||
}) => {
|
}) => {
|
||||||
const [loadingAction, setLoadingAction] = useState<string | null>(null)
|
const [loadingAction, setLoadingAction] = useState<string | null>(null)
|
||||||
|
|
||||||
@@ -64,8 +68,22 @@ const OrderActions: FC<OrderActionsProps> = ({
|
|||||||
|
|
||||||
if (isCanceled) return null
|
if (isCanceled) return null
|
||||||
|
|
||||||
|
const handleConfirmQuantities = () => {
|
||||||
|
setLoadingAction('confirmQuantities')
|
||||||
|
onConfirmQuantities()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-6 flex flex-col gap-3'>
|
<div className='mt-6 flex flex-col gap-3'>
|
||||||
|
{isNeedConfirmation && (
|
||||||
|
<Button
|
||||||
|
label='تایید مقدار'
|
||||||
|
onClick={handleConfirmQuantities}
|
||||||
|
isloading={isChangingStatus && loadingAction === 'confirmQuantities'}
|
||||||
|
className='bg-purple-600 hover:bg-purple-700'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{showVerifyCashPaymentButton && (
|
{showVerifyCashPaymentButton && (
|
||||||
<Button
|
<Button
|
||||||
label='پرداخت شده'
|
label='پرداخت شده'
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import Table from '@/components/Table'
|
import Table from '@/components/Table'
|
||||||
import { ShoppingCart, Calendar, Clock, Tag } from 'iconsax-react'
|
import { ShoppingCart, Calendar, Clock, Tag, Edit2 } from 'iconsax-react'
|
||||||
import type { FC } from 'react'
|
import { type FC, useState } from 'react'
|
||||||
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
|
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
|
||||||
import type { Order, OrderItem as OrderItemType } from '../types/Types'
|
import type { Order, OrderItem as OrderItemType } from '../types/Types'
|
||||||
import { formatOptionalDate, formatPrice, formatFaNumber } from '@/helpers/func'
|
import { formatOptionalDate, formatPrice, formatFaNumber } from '@/helpers/func'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import OrderSummary from './OrderSummary'
|
import OrderSummary from './OrderSummary'
|
||||||
|
import { useUpdateOrderItemQuantity } from '../hooks/useOrderData'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { extractErrorMessage } from '@/config/func'
|
||||||
|
import DefaulModal from '@/components/DefaulModal'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
|
||||||
interface OrderItemTable extends RowDataType {
|
interface OrderItemTable extends RowDataType {
|
||||||
image: string
|
image: string
|
||||||
@@ -19,9 +25,97 @@ interface OrderItemTable extends RowDataType {
|
|||||||
interface OrderItemsDetailsProps {
|
interface OrderItemsDetailsProps {
|
||||||
order: Order
|
order: Order
|
||||||
getStatusColor: (status: string) => string
|
getStatusColor: (status: string) => string
|
||||||
|
isNeedConfirmation?: boolean
|
||||||
|
iconColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderItemsDetails: FC<OrderItemsDetailsProps> = ({ order, getStatusColor }) => {
|
interface EditQuantityModalProps {
|
||||||
|
item: OrderItemType
|
||||||
|
orderId: string
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditQuantityModal: FC<EditQuantityModalProps> = ({ item, orderId, isOpen, onClose }) => {
|
||||||
|
const [value, setValue] = useState(String(item.quantity))
|
||||||
|
const { mutate: updateQuantity, isPending } = useUpdateOrderItemQuantity()
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const parsed = parseFloat(value)
|
||||||
|
if (isNaN(parsed) || parsed <= 0) return
|
||||||
|
updateQuantity(
|
||||||
|
{ orderId, orderItemId: item.id, quantity: parsed },
|
||||||
|
{
|
||||||
|
onSuccess: onClose,
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaulModal open={isOpen} close={onClose} isHeader title_header='ویرایش تعداد'>
|
||||||
|
<div className='mt-6 space-y-6'>
|
||||||
|
<Input
|
||||||
|
label='تعداد'
|
||||||
|
allowDecimal
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
placeholder='مقدار جدید را وارد کنید'
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<div className='flex gap-3 justify-end'>
|
||||||
|
<Button
|
||||||
|
label='ذخیره'
|
||||||
|
onClick={handleSave}
|
||||||
|
isloading={isPending}
|
||||||
|
className='min-w-[90px]'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label='انصراف'
|
||||||
|
onClick={onClose}
|
||||||
|
className='bg-transparent text-black border border-primary min-w-[90px]'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DefaulModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QuantityWithEditProps {
|
||||||
|
item: OrderItemType
|
||||||
|
orderId: string
|
||||||
|
iconColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuantityWithEdit: FC<QuantityWithEditProps> = ({ item, orderId, iconColor = '#6d28d9' }) => {
|
||||||
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='flex items-center justify-center gap-2'>
|
||||||
|
<span>{formatFaNumber(item.quantity)}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setModalOpen(true)}
|
||||||
|
className='flex items-center justify-center hover:opacity-70 transition-opacity'
|
||||||
|
>
|
||||||
|
<Edit2 size={15} color={iconColor} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{modalOpen && (
|
||||||
|
<EditQuantityModal
|
||||||
|
item={item}
|
||||||
|
orderId={orderId}
|
||||||
|
isOpen={modalOpen}
|
||||||
|
onClose={() => setModalOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrderItemsDetails: FC<OrderItemsDetailsProps> = ({ order, getStatusColor, isNeedConfirmation, iconColor }) => {
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
|
||||||
const orderItems: OrderItemTable[] = order.items.map((item: OrderItemType) => ({
|
const orderItems: OrderItemTable[] = order.items.map((item: OrderItemType) => ({
|
||||||
@@ -63,7 +157,15 @@ const OrderItemsDetails: FC<OrderItemsDetailsProps> = ({ order, getStatusColor }
|
|||||||
title: 'تعداد',
|
title: 'تعداد',
|
||||||
key: 'quantity',
|
key: 'quantity',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (item) => formatFaNumber(item.quantity)
|
render: (item) => {
|
||||||
|
if (isNeedConfirmation) {
|
||||||
|
const originalItem = order.items.find(i => i.id === item.id)
|
||||||
|
if (originalItem) {
|
||||||
|
return <QuantityWithEdit item={originalItem} orderId={order.id} iconColor={iconColor} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatFaNumber(item.quantity)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'قیمت واحد',
|
title: 'قیمت واحد',
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum OrderStatus {
|
export enum OrderStatus {
|
||||||
|
NEED_CONFIRMATION = "needConfirmation",
|
||||||
PENDING_PAYMENT = "pendingPayment",
|
PENDING_PAYMENT = "pendingPayment",
|
||||||
PAID = "paid",
|
PAID = "paid",
|
||||||
PREPARING = "preparing",
|
PREPARING = "preparing",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import * as api from "../service/OrderService";
|
import * as api from "../service/OrderService";
|
||||||
import type { GetOrdersParams, ChangeOrderStatusParams, RefundPaymentParams } from "../service/OrderService";
|
import type { GetOrdersParams, ChangeOrderStatusParams, RefundPaymentParams } from "../service/OrderService";
|
||||||
|
|
||||||
|
|
||||||
export const useGetOrders = (params?: GetOrdersParams) => {
|
export const useGetOrders = (params?: GetOrdersParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["orders", params],
|
queryKey: ["orders", params],
|
||||||
@@ -62,3 +63,15 @@ export const useRefundPayment = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateOrderItemQuantity = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ orderId, orderItemId, quantity }: { orderId: string; orderItemId: string; quantity: number }) =>
|
||||||
|
api.updateOrderItemQuantity(orderId, orderItemId, quantity),
|
||||||
|
onSuccess: (_, { orderId }) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["order", orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["orders"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -82,3 +82,11 @@ export const refundPayment = async (
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
await axios.post(`/admin/orders/${orderId}/refund`, params);
|
await axios.post(`/admin/orders/${orderId}/refund`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateOrderItemQuantity = async (
|
||||||
|
orderId: string,
|
||||||
|
orderItemId: string,
|
||||||
|
quantity: number
|
||||||
|
): Promise<void> => {
|
||||||
|
await axios.patch(`/admin/orders/${orderId}/items/${orderItemId}/quantity`, { quantity });
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user