compelete return
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
import { type FC, useState } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import { useGetReturnDetails, useApproveReturn, useRejectReturn } from './hooks/useOrderData'
|
||||
import { useGetReturnDetails, useApproveReturn, useRejectReturn, useCompleteReturn } from './hooks/useOrderData'
|
||||
import PageTitle from '@/components/PageTitle'
|
||||
import PageLoading from '@/components/PageLoading'
|
||||
import { formatPrice } from '@/helpers/func'
|
||||
import StatusWithText from '@/components/StatusWithText'
|
||||
import { Calendar, Money3, Box, Location, CloseCircle, TickCircle, Shop } from 'iconsax-react'
|
||||
import { Calendar, Money3, Box, Location, CloseCircle, TickCircle, Shop, DocumentUpload } from 'iconsax-react'
|
||||
import Button from '@/components/Button'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import ModalConfrim from '@/components/ModalConfrim'
|
||||
import { toast } from 'react-toastify'
|
||||
import { extractErrorMessage } from '@/helpers/utils'
|
||||
import DefaulModal from '@/components/DefaulModal'
|
||||
import Textarea from '@/components/Textarea'
|
||||
import DatePicker from '@/components/DatePicker'
|
||||
import UploadBox from '@/components/UploadBox'
|
||||
import { useUploadSingle } from '@/pages/uploader/hooks/useUploaderData'
|
||||
|
||||
const ReturnDetails: FC = () => {
|
||||
const { id } = useParams()
|
||||
@@ -19,9 +24,16 @@ const ReturnDetails: FC = () => {
|
||||
const { data, isLoading, error } = useGetReturnDetails(id!)
|
||||
const approveReturn = useApproveReturn()
|
||||
const rejectReturn = useRejectReturn()
|
||||
const completeReturn = useCompleteReturn()
|
||||
const uploadSingle = useUploadSingle()
|
||||
|
||||
const [showApproveModal, setShowApproveModal] = useState(false)
|
||||
const [showRejectModal, setShowRejectModal] = useState(false)
|
||||
const [showCompleteModal, setShowCompleteModal] = useState(false)
|
||||
const [refundDate, setRefundDate] = useState('')
|
||||
const [refundComment, setRefundComment] = useState('')
|
||||
const [refundReceiptFile, setRefundReceiptFile] = useState<File[]>([])
|
||||
const [isUploading, setIsUploading] = useState(false)
|
||||
|
||||
const handleApprove = async () => {
|
||||
try {
|
||||
@@ -49,6 +61,42 @@ const ReturnDetails: FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleCompleteReturn = async () => {
|
||||
try {
|
||||
setIsUploading(true)
|
||||
let receiptUrl = ''
|
||||
|
||||
// آپلود فایل رسید (اگر انتخاب شده باشد)
|
||||
if (refundReceiptFile.length > 0) {
|
||||
const uploadResponse = await uploadSingle.mutateAsync(refundReceiptFile[0])
|
||||
receiptUrl = uploadResponse?.results?.url?.url || ''
|
||||
}
|
||||
|
||||
// ارسال درخواست تکمیل مرجوعی
|
||||
await completeReturn.mutateAsync({
|
||||
id: id!,
|
||||
params: {
|
||||
refundDate,
|
||||
refundComment,
|
||||
refundReceipt: receiptUrl
|
||||
}
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ['return-details', id] })
|
||||
queryClient.invalidateQueries({ queryKey: ['order-returns'] })
|
||||
setShowCompleteModal(false)
|
||||
setRefundDate('')
|
||||
setRefundComment('')
|
||||
setRefundReceiptFile([])
|
||||
toast.success('مرجوعی با موفقیت تکمیل شد')
|
||||
navigate('/orders/returns')
|
||||
} catch (error) {
|
||||
toast.error(extractErrorMessage(error))
|
||||
} finally {
|
||||
setIsUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoading />
|
||||
}
|
||||
@@ -80,6 +128,8 @@ const ReturnDetails: FC = () => {
|
||||
switch (status) {
|
||||
case 'Approved':
|
||||
return 'success'
|
||||
case 'Completed':
|
||||
return 'success'
|
||||
case 'Rejected':
|
||||
return 'error'
|
||||
case 'Pending':
|
||||
@@ -93,6 +143,8 @@ const ReturnDetails: FC = () => {
|
||||
switch (status) {
|
||||
case 'Approved':
|
||||
return 'تایید شده'
|
||||
case 'Completed':
|
||||
return 'تکمیل شده'
|
||||
case 'Rejected':
|
||||
return 'رد شده'
|
||||
case 'Pending':
|
||||
@@ -144,6 +196,21 @@ const ReturnDetails: FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* دکمه تکمیل مرجوعی */}
|
||||
{data?.results?.returnOrder?.status === 'Approved' && (
|
||||
<div className='flex gap-3 justify-end'>
|
||||
<Button
|
||||
onClick={() => setShowCompleteModal(true)}
|
||||
className='bg-blue-600 hover:bg-blue-700 text-white w-fit'
|
||||
>
|
||||
<div className='flex items-center gap-2'>
|
||||
<DocumentUpload color='#fff' size={20} />
|
||||
<div>تکمیل مرجوعی</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* کارتهای اطلاعاتی */}
|
||||
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6'>
|
||||
{/* اطلاعات فروشگاه */}
|
||||
@@ -324,6 +391,63 @@ const ReturnDetails: FC = () => {
|
||||
label="آیا از رد این مرجوعی اطمینان دارید؟"
|
||||
isLoading={rejectReturn.isPending}
|
||||
/>
|
||||
|
||||
{/* مودال تکمیل مرجوعی */}
|
||||
<DefaulModal
|
||||
open={showCompleteModal}
|
||||
close={() => {
|
||||
setShowCompleteModal(false)
|
||||
setRefundDate('')
|
||||
setRefundComment('')
|
||||
setRefundReceiptFile([])
|
||||
}}
|
||||
isHeader={true}
|
||||
title_header="تکمیل مرجوعی"
|
||||
>
|
||||
<div className="space-y-4 mt-4 w-[400px]">
|
||||
<DatePicker
|
||||
label="تاریخ بازگشت وجه"
|
||||
placeholder="تاریخ را انتخاب کنید"
|
||||
onChange={(date) => setRefundDate(date)}
|
||||
defaulValue={refundDate}
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
label="توضیحات بازگشت وجه"
|
||||
placeholder="توضیحات را وارد کنید"
|
||||
value={refundComment}
|
||||
onChange={(e) => setRefundComment(e.target.value)}
|
||||
rows={4}
|
||||
/>
|
||||
|
||||
<UploadBox
|
||||
label="رسید بازگشت وجه"
|
||||
onChange={(files) => setRefundReceiptFile(files)}
|
||||
isReset={!showCompleteModal}
|
||||
/>
|
||||
|
||||
<div className="flex gap-3 justify-end pt-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowCompleteModal(false)
|
||||
setRefundDate('')
|
||||
setRefundComment('')
|
||||
setRefundReceiptFile([])
|
||||
}}
|
||||
className="bg-gray-200 hover:bg-gray-300 text-gray-700 w-fit"
|
||||
>
|
||||
انصراف
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCompleteReturn}
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white w-fit"
|
||||
disabled={!refundDate || !refundComment || isUploading || completeReturn.isPending}
|
||||
>
|
||||
{isUploading || completeReturn.isPending ? 'در حال انجام...' : 'تکمیل مرجوعی'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ const Returns: FC = () => {
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case 'Approved': return 'success'
|
||||
case 'Completed': return 'success'
|
||||
case 'Rejected': return 'error'
|
||||
case 'Pending':
|
||||
default: return 'warning'
|
||||
@@ -50,6 +51,7 @@ const Returns: FC = () => {
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'Approved': return 'تایید شده'
|
||||
case 'Completed': return 'تکمیل شده'
|
||||
case 'Rejected': return 'رد شده'
|
||||
case 'Pending': return 'در انتظار'
|
||||
default: return status
|
||||
@@ -76,32 +78,38 @@ const Returns: FC = () => {
|
||||
<tbody>
|
||||
{returnsOrders.length === 0 ? (
|
||||
<tr className='tr'>
|
||||
<td colSpan={6} className='text-center py-8 text-muted-foreground'>
|
||||
<td colSpan={7} className='text-center py-8 text-muted-foreground'>
|
||||
مرجوعی یافت نشد
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
returnsOrders.map((ret: ReturnOrderType) => (
|
||||
<tr key={ret._id} className='tr'>
|
||||
<Td text={`#${ret?.order?._id ?? '-'}`} />
|
||||
<Td text={ret?.shopName ?? '-'} />
|
||||
<Td text=''>
|
||||
<StatusWithText
|
||||
variant={getStatusVariant(ret?.status)}
|
||||
text={getStatusText(ret?.status)}
|
||||
/>
|
||||
</Td>
|
||||
<Td text={`${formatPrice(Number(ret?.total_price || 0))} تومان`} />
|
||||
<Td text={formatDateShort(ret?.createdAt)} />
|
||||
<Td text={String(ret?.returnOrderItems?.quantity ?? 0)} />
|
||||
<Td text="">
|
||||
returnsOrders.map((ret: ReturnOrderType) => {
|
||||
const totalQuantity = Array.isArray(ret?.returnOrderItems)
|
||||
? ret.returnOrderItems.reduce((sum, item) => sum + (item?.quantity || 0), 0)
|
||||
: 0
|
||||
|
||||
<Link to={`${Pages.orders.returnDetails}${ret._id}`}>
|
||||
<Eye size={16} color="#4B5563" />
|
||||
</Link>
|
||||
</Td>
|
||||
</tr>
|
||||
))
|
||||
return (
|
||||
<tr key={ret._id} className='tr'>
|
||||
<Td text={`#${ret?.order?._id ?? '-'}`} />
|
||||
<Td text={ret?.shopName ?? '-'} />
|
||||
<Td text=''>
|
||||
<StatusWithText
|
||||
variant={getStatusVariant(ret?.status)}
|
||||
text={getStatusText(ret?.status)}
|
||||
/>
|
||||
</Td>
|
||||
<Td text={`${formatPrice(Number(ret?.total_price || 0))} تومان`} />
|
||||
<Td text={formatDateShort(ret?.createdAt)} />
|
||||
<Td text={String(totalQuantity)} />
|
||||
<Td text="">
|
||||
|
||||
<Link to={`${Pages.orders.returnDetails}${ret._id}`}>
|
||||
<Eye size={16} color="#4B5563" />
|
||||
</Link>
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -14,6 +14,7 @@ type Props = {
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case 'Approved': return 'success'
|
||||
case 'Completed': return 'success'
|
||||
case 'Rejected': return 'error'
|
||||
case 'Pending':
|
||||
default: return 'warning'
|
||||
@@ -23,6 +24,7 @@ const getStatusVariant = (status: string) => {
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'Approved': return 'تایید شده'
|
||||
case 'Completed': return 'تکمیل شده'
|
||||
case 'Rejected': return 'رد شده'
|
||||
case 'Pending': return 'در انتظار'
|
||||
default: return status
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import * as api from "../service/OrderService";
|
||||
import { useSharedStore } from "@/shared/store/sharedStore";
|
||||
import type { CompleteReturnType } from "../types/ReturnTypes";
|
||||
|
||||
export const useGetNativeOrders = (
|
||||
page: number = 1,
|
||||
@@ -104,3 +105,10 @@ export const useRejectReturn = () => {
|
||||
mutationFn: api.rejectReturn,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCompleteReturn = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ id, params }: { id: string; params: CompleteReturnType }) =>
|
||||
api.compeleteReturn(id, params),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import axios from "../../../config/axios";
|
||||
import type { AddTrackCodeType } from "../types/ReturnTypes";
|
||||
import type {
|
||||
AddTrackCodeType,
|
||||
CompleteReturnType,
|
||||
} from "../types/ReturnTypes";
|
||||
import type {
|
||||
CancelReasonsResponseType,
|
||||
CreateCancelReasonType,
|
||||
@@ -104,3 +107,14 @@ export const rejectReturn = async (id: string) => {
|
||||
const { data } = await axios.post(`/admin/orders/returns/${id}/reject`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const compeleteReturn = async (
|
||||
id: string,
|
||||
params: CompleteReturnType
|
||||
) => {
|
||||
const { data } = await axios.post(
|
||||
`/admin/orders/returns/${id}/complete`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -51,3 +51,9 @@ export type AddTrackCodeType = {
|
||||
postingReceipt: string;
|
||||
orderId: string;
|
||||
};
|
||||
|
||||
export type CompleteReturnType = {
|
||||
refundDate: string;
|
||||
refundComment: string;
|
||||
refundReceipt: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user