variable item and confrim + ...
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-06-29 09:39:15 +03:30
parent 96e0c4c3eb
commit 2a5905b35f
7 changed files with 164 additions and 7 deletions
+2 -1
View File
@@ -855,7 +855,8 @@
"canceled": "لغو شده",
"deliveredToWaiter": "تحویل به گارسون",
"deliveredToReceptionist": "تحویل به پیشخوان",
"deliveredToRecepient": "تحویل به گیرنده"
"deliveredToRecepient": "تحویل به گیرنده",
"needConfirmation": "در انتظار تایید فروشگاه"
},
"reviews": {
"reviews": "نظرات",
+15 -1
View File
@@ -88,6 +88,7 @@ const OrderDetails: FC = () => {
// منطق نمایش دکمه‌ها
const isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
const isNeedConfirmation = order.status === OrderStatus.NEED_CONFIRMATION
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
const isOrderPaid = order.status === OrderStatus.PAID
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) => {
changeOrderStatus({
orderId: order.id,
@@ -205,7 +217,7 @@ const OrderDetails: FC = () => {
<div className='flex gap-7 mt-9'>
<div className='flex-1 space-y-8'>
<CustomerInfo order={order} />
<OrderItemsDetails order={order} getStatusColor={getStatusColor} />
<OrderItemsDetails order={order} getStatusColor={getStatusColor} isNeedConfirmation={isNeedConfirmation} iconColor='#7c3aed' />
</div>
<div>
@@ -223,6 +235,7 @@ const OrderDetails: FC = () => {
isCourierDelivery={isCourierDelivery}
isCustomerPickup={isCustomerPickup}
isCanceled={isCanceled}
isNeedConfirmation={isNeedConfirmation}
isVerifyingPayment={isVerifyingPayment}
isChangingStatus={isChangingStatus}
onVerifyCashPayment={handleVerifyCashPayment}
@@ -230,6 +243,7 @@ const OrderDetails: FC = () => {
onDeliverToCourier={handleDeliverToCourier}
onDeliverToReceptionist={handleDeliverToReceptionist}
onCancelOrder={() => setShowCancelModal(true)}
onConfirmQuantities={handleConfirmQuantities}
/>
</div>
</div>
+19 -1
View File
@@ -9,6 +9,7 @@ interface OrderActionsProps {
isCourierDelivery: boolean
isCustomerPickup: boolean
isCanceled: boolean
isNeedConfirmation: boolean
isVerifyingPayment: boolean
isChangingStatus: boolean
onVerifyCashPayment: () => void
@@ -16,6 +17,7 @@ interface OrderActionsProps {
onDeliverToCourier: () => void
onDeliverToReceptionist: () => void
onCancelOrder: () => void
onConfirmQuantities: () => void
}
const OrderActions: FC<OrderActionsProps> = ({
@@ -26,13 +28,15 @@ const OrderActions: FC<OrderActionsProps> = ({
isCourierDelivery,
isCustomerPickup,
isCanceled,
isNeedConfirmation,
isVerifyingPayment,
isChangingStatus,
onVerifyCashPayment,
onSendToKitchen,
onDeliverToCourier,
onDeliverToReceptionist,
onCancelOrder
onCancelOrder,
onConfirmQuantities
}) => {
const [loadingAction, setLoadingAction] = useState<string | null>(null)
@@ -64,8 +68,22 @@ const OrderActions: FC<OrderActionsProps> = ({
if (isCanceled) return null
const handleConfirmQuantities = () => {
setLoadingAction('confirmQuantities')
onConfirmQuantities()
}
return (
<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 && (
<Button
label='پرداخت شده'
@@ -1,11 +1,17 @@
import Table from '@/components/Table'
import { ShoppingCart, Calendar, Clock, Tag } from 'iconsax-react'
import type { FC } from 'react'
import { ShoppingCart, Calendar, Clock, Tag, Edit2 } from 'iconsax-react'
import { type FC, useState } from 'react'
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
import type { Order, OrderItem as OrderItemType } from '../types/Types'
import { formatOptionalDate, formatPrice, formatFaNumber } from '@/helpers/func'
import { useTranslation } from 'react-i18next'
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 {
image: string
@@ -19,9 +25,97 @@ interface OrderItemTable extends RowDataType {
interface OrderItemsDetailsProps {
order: Order
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 orderItems: OrderItemTable[] = order.items.map((item: OrderItemType) => ({
@@ -63,7 +157,15 @@ const OrderItemsDetails: FC<OrderItemsDetailsProps> = ({ order, getStatusColor }
title: 'تعداد',
key: 'quantity',
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: 'قیمت واحد',
+1
View File
@@ -1,4 +1,5 @@
export enum OrderStatus {
NEED_CONFIRMATION = "needConfirmation",
PENDING_PAYMENT = "pendingPayment",
PAID = "paid",
PREPARING = "preparing",
+13
View File
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/OrderService";
import type { GetOrdersParams, ChangeOrderStatusParams, RefundPaymentParams } from "../service/OrderService";
export const useGetOrders = (params?: GetOrdersParams) => {
return useQuery({
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"] });
},
});
};
+8
View File
@@ -82,3 +82,11 @@ export const refundPayment = async (
): Promise<void> => {
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 });
};