return order + success page return order + ...

This commit is contained in:
hamid zarghami
2025-10-08 09:08:01 +03:30
parent 1b7e9eb168
commit 700e757128
17 changed files with 1295 additions and 24 deletions
+17 -9
View File
@@ -1,6 +1,6 @@
'use client'
import { FC, useState } from 'react'
import { ArrowRight2 } from 'iconsax-react'
import { ArrowLeft, TaskSquare } from 'iconsax-react'
import { useRouter, useParams } from 'next/navigation'
import PageLoading from '@/components/PageLoading'
import Layout from '@/hoc/Layout'
@@ -10,6 +10,7 @@ import OrderMainContent from './components/OrderMainContent'
import CancelOrderModal from './components/CancelOrderModal'
import { useCancelOrder, useGetOrderDetail } from '../hooks/useOrdersData'
import { toast } from '@/components/Toast'
import { PRIMARY_COLOR } from '@/config/const'
const OrderDetail: FC = () => {
@@ -66,14 +67,21 @@ const OrderDetail: FC = () => {
<Menu pageActive='orders' />
<div className='border p-6 rounded-2xl text-sm mt-7'>
<div className='flex gap-2 items-center border-b pb-6'>
<button
onClick={() => router.push('/profile/orders')}
className='p-2 hover:bg-gray-100 rounded-lg transition-colors'
>
<ArrowRight2 size={20} className="rotate-180" />
</button>
<div className="text-lg font-semibold">جزئیات سفارش</div>
<div className='flex justify-between items-center border-b pb-6'>
<div className='flex gap-2 items-center'>
<button
onClick={() => router.push('/profile/orders')}
className='p-2 hover:bg-gray-100 rounded-lg transition-colors'
>
<ArrowLeft color='black' size={20} className="rotate-180" />
</button>
<div className="text-lg font-semibold">جزئیات سفارش</div>
</div>
<div className='flex gap-2 items-center cursor-pointer' onClick={() => router.push(`/profile/orders/${id}/return`)}>
<TaskSquare color={PRIMARY_COLOR} size={20} />
<div className='text-primary'>فرم درخواست مرجوعی</div>
</div>
</div>
{cancelOrderMutation.isPending ? (
@@ -0,0 +1,96 @@
'use client'
import React from 'react'
import Image from 'next/image'
import { Checkbox } from '@/components/ui/checkbox'
import { ShipmentItemDetail } from '../../../types/Types'
interface ProductSelectionProps {
items: ShipmentItemDetail[]
selectedItems: string[]
onItemSelect: (itemId: string, checked: boolean) => void
}
const ProductSelection: React.FC<ProductSelectionProps> = ({
items,
selectedItems,
onItemSelect
}) => {
return (
<div className='space-y-0'>
{items.length === 0 && (
<div className='text-center py-8 text-gray-500'>
هیچ آیتمی برای نمایش وجود ندارد
</div>
)}
{items.map((item, index) => {
const availableQuantity = item.quantity - item.cancelled_quantity - item.returned_quantity
const canReturn = availableQuantity > 0
return (
<div key={item._id}>
<div className={`p-4 ${!canReturn ? 'opacity-60' : ''}`}>
<div className='flex items-start gap-4'>
<div className='flex items-center justify-center'>
<Checkbox
id={item._id}
checked={selectedItems.includes(item._id)}
onCheckedChange={(checked) => onItemSelect(item._id, checked as boolean)}
disabled={!canReturn}
className={`w-5 h-5 ${!canReturn ? 'opacity-70' : ''}`}
/>
</div>
<Image
src={item.product.imagesUrl.cover}
width={80}
height={80}
className='w-20 h-20 object-cover rounded-lg border flex-shrink-0'
alt={item.product.title_fa}
/>
<div className='flex-1 min-w-0'>
<div className='font-medium text-sm leading-relaxed mb-2'>
{item.product.title_fa}
</div>
<div className='flex items-center gap-2 mb-2'>
<div className='w-3 h-3 bg-gray-800 rounded-full'></div>
<span className='text-xs text-gray-600'>مشکی</span>
</div>
<div className='text-xs text-gray-500 mb-1'>
کد کالا: {item.product._id || 'نامشخص'}
</div>
<div className='text-xs text-gray-500 mb-1'>
فروشنده: سندس کالا
</div>
<div className='text-xs text-gray-500 mb-2'>
وضعیت: آونگ
</div>
<div className='text-sm font-bold text-primary'>
{Intl.NumberFormat('fa-IR').format(item.selling_price)} تومان
</div>
{!canReturn && (
<div className='text-xs text-red-500 mt-2'>
این آیتم قابل مرجوع کردن نیست
</div>
)}
</div>
</div>
</div>
{index < items.length - 1 && (
<div className='border-t border-gray-200'></div>
)}
</div>
)
})}
</div>
)
}
export default ProductSelection
@@ -0,0 +1,134 @@
'use client'
import React from 'react'
import Image from 'next/image'
import { Button } from '@/components/ui/button'
import { ShipmentItemDetail } from '../../../types/Types'
interface ReturnConfirmationProps {
selectedItems: ShipmentItemDetail[]
quantities: { [key: string]: number }
reason: string
description: string
images: File[]
imagePreviewUrls: string[]
returnReasons: Array<{ _id: string; title: string }>
onSubmit: () => void
onBack: () => void
isLoading: boolean
}
const ReturnConfirmation: React.FC<ReturnConfirmationProps> = ({
selectedItems,
quantities,
reason,
description,
// images,
imagePreviewUrls,
returnReasons,
onSubmit,
onBack,
isLoading
}) => {
const selectedReason = returnReasons.find(r => r._id === reason)
return (
<div className='space-y-6'>
<h3 className='text-lg font-semibold'>تایید اطلاعات مرجوعی</h3>
{/* محصولات انتخاب شده */}
<div className='space-y-4'>
<h4 className='font-medium'>محصولات انتخاب شده</h4>
{selectedItems.map((item) => (
<div key={item._id} className='p-4 border rounded-lg'>
<div className='flex items-center gap-4'>
<Image
src={item.product.imagesUrl.cover}
width={60}
height={60}
className='w-15 h-15 object-cover rounded-lg border flex-shrink-0'
alt={item.product.title_fa}
/>
<div className='flex-1'>
<div className='font-medium'>{item.product.title_fa}</div>
<div className='text-sm text-gray-600 mt-1'>
تعداد مرجوعی: {quantities[item._id]}
</div>
<div className='text-sm text-primary font-medium mt-1'>
{Intl.NumberFormat('fa-IR').format(item.selling_price)} تومان
</div>
</div>
</div>
</div>
))}
</div>
{/* اطلاعات مرجوعی */}
<div className='space-y-4'>
<h4 className='font-medium'>اطلاعات مرجوعی</h4>
<div className='p-4 border rounded-lg space-y-3'>
<div>
<span className='font-medium text-sm'>دلیل مرجوعی: </span>
<span className='text-sm'>{selectedReason?.title}</span>
</div>
<div>
<span className='font-medium text-sm'>توضیحات: </span>
<p className='text-sm text-gray-700 mt-1 leading-relaxed'>{description}</p>
</div>
</div>
</div>
{/* تصاویر آپلود شده */}
{imagePreviewUrls.length > 0 && (
<div className='space-y-4'>
<h4 className='font-medium'>تصاویر آپلود شده</h4>
<div className='grid grid-cols-2 md:grid-cols-3 gap-4'>
{imagePreviewUrls.map((url, index) => (
<div key={index} className='relative'>
<Image
src={url}
width={100}
height={100}
className='max-w-[200px] max-h-[200px] object-contain rounded border'
alt={`تصویر ${index + 1}`}
/>
</div>
))}
</div>
</div>
)}
{/* اخطار */}
<div className='flex gap-2 text-xs text-[#7F7F7F] p-3 bg-primary/10 rounded-lg'>
<div className="size-5 bg-primary/20 rounded-full flex items-center justify-center">
<span className="text-primary text-xs">!</span>
</div>
<span>
لطفاً قبل از تایید نهایی، تمامی اطلاعات را بررسی کنید. پس از ثبت درخواست مرجوعی، امکان تغییر اطلاعات وجود ندارد.
</span>
</div>
{/* دکمه‌های عملیات */}
<div className='flex gap-3 justify-center'>
<Button
type="button"
variant="outline"
onClick={onBack}
>
بازگشت
</Button>
<Button
type="button"
onClick={onSubmit}
disabled={isLoading}
isLoading={isLoading}
>
تایید و ثبت مرجوعی
</Button>
</div>
</div>
)
}
export default ReturnConfirmation
@@ -0,0 +1,257 @@
'use client'
import React, { useState, useRef } from 'react'
import { useForm } from 'react-hook-form'
import { yupResolver } from '@hookform/resolvers/yup'
import * as yup from 'yup'
import Select from '@/components/Select'
import { Button } from '@/components/ui/button'
import { ShipmentItemDetail } from '../../../types/Types'
import Image from 'next/image'
import { CloseCircle, DocumentUpload } from 'iconsax-react'
interface ReturnFormData {
quantities: { [key: string]: number }
reason: string
description: string
}
interface ReturnFormProps {
selectedItems: ShipmentItemDetail[]
onSubmit: (data: { quantities: { [key: string]: number }; reason: string; description: string; images: File[] }) => void
onBack: () => void
isLoading: boolean
returnReasons: Array<{ _id: string; title: string }>
}
const returnFormSchema = yup.object({
reason: yup.string().required('دلیل مرجوعی را انتخاب کنید'),
description: yup.string().required('توضیحات را وارد کنید').min(10, 'توضیحات باید حداقل ۱۰ کاراکتر باشد'),
quantities: yup.object().test('quantities', 'تعداد مرجوعی باید معتبر باشد', function (value: Record<string, number>) {
if (!value) return false
const selectedItems = this.parent.selectedItems || []
for (const item of selectedItems) {
const quantity = value[item._id]
const availableQuantity = item.quantity - item.cancelled_quantity - item.returned_quantity
if (!quantity || quantity < 1 || quantity > availableQuantity) {
return false
}
}
return true
})
})
const ReturnForm: React.FC<ReturnFormProps> = ({
selectedItems,
onSubmit,
onBack,
isLoading,
returnReasons
}) => {
const [images, setImages] = useState<File[]>([])
const [imagePreviewUrls, setImagePreviewUrls] = useState<string[]>([])
const fileInputRef = useRef<HTMLInputElement>(null)
const {
register,
handleSubmit,
watch,
setValue,
formState: { errors },
reset
} = useForm<ReturnFormData>({
resolver: yupResolver(returnFormSchema),
defaultValues: {
quantities: selectedItems.reduce((acc, item) => ({
...acc,
[item._id]: 1
}), {})
}
})
const handleFormSubmit = (data: ReturnFormData) => {
onSubmit({
quantities: data.quantities,
reason: data.reason,
description: data.description,
images
})
reset()
}
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || [])
const validFiles = files.filter(file => {
const isValidType = file.type === 'image/jpeg' || file.type === 'image/png'
const isValidSize = file.size <= 10 * 1024 * 1024 // 10MB
return isValidType && isValidSize
})
if (validFiles.length !== files.length) {
alert('فقط فایل‌های JPG و PNG با حداکثر حجم ۱۰ مگابایت مجاز هستند')
}
const newImages = [...images, ...validFiles].slice(0, 5) // حداکثر ۵ عکس
setImages(newImages)
const newPreviewUrls = newImages.map(file => URL.createObjectURL(file))
setImagePreviewUrls(newPreviewUrls)
}
const removeImage = (index: number) => {
const newImages = images.filter((_, i) => i !== index)
const newPreviewUrls = imagePreviewUrls.filter((_, i) => i !== index)
setImages(newImages)
setImagePreviewUrls(newPreviewUrls)
}
return (
<div className='space-y-6'>
<h3 className='text-lg font-semibold'>تکمیل اطلاعات مرجوعی</h3>
{/* انتخاب تعداد مرجوعی برای هر آیتم */}
<div className='space-y-4'>
<h4 className='font-medium'>تعداد مرجوعی</h4>
{selectedItems.map((item) => {
const availableQuantity = item.quantity - item.cancelled_quantity - item.returned_quantity
return (
<div key={item._id} className='p-4 border rounded-lg'>
<div className='flex items-center gap-4 mb-2'>
<Image
src={item.product.imagesUrl.cover}
width={40}
height={40}
className='w-10 h-10 object-cover rounded border'
alt={item.product.title_fa}
/>
<div className='flex-1'>
<div className='font-medium text-sm'>{item.product.title_fa}</div>
<div className='text-xs text-gray-600'>قابل مرجوع: {availableQuantity}</div>
</div>
</div>
<div className='flex items-center gap-2'>
<label className='text-sm'>تعداد:</label>
<input
type='number'
min={1}
max={availableQuantity}
defaultValue={1}
{...register(`quantities.${item._id}`, {
valueAsNumber: true,
min: { value: 1, message: 'حداقل تعداد ۱ است' },
max: { value: availableQuantity, message: `حداکثر تعداد ${availableQuantity} است` }
})}
className='w-20 px-2 py-1 border rounded text-center'
/>
</div>
</div>
)
})}
</div>
<form onSubmit={handleSubmit(handleFormSubmit)} className='space-y-6'>
{/* انتخاب دلیل مرجوعی */}
<Select
placeholder='دلیل مرجوعی'
items={returnReasons.map((item) => ({
label: item.title,
value: item._id
}))}
className='bg-white border'
value={watch('reason')}
name='reason'
onChange={(e) => setValue('reason', e.target.value)}
error_text={errors.reason?.message}
/>
{/* توضیحات */}
<div>
<div className="text-sm font-medium mb-2">توضیحات :</div>
<textarea
{...register('description')}
className='h-[123px] rounded-xl p-3 text-sm border w-full resize-none focus:outline-none focus:ring-2 focus:ring-primary/20'
placeholder='توضیحات خود را بنویسید (حداقل ۱۰ کاراکتر)'
/>
{errors.description && (
<div className='text-xs text-red-500 mt-1'>
{errors.description.message}
</div>
)}
</div>
{/* آپلود عکس */}
<div>
<div className="text-sm font-medium mb-2">آپلود عکس</div>
<div className='text-xs text-gray-600 mb-3'>
اگر ایراد کالا قابل مشاهده است به صورتی عکس بگیرید که مشخص باشد (با فرمت JPG یا PNG و هر عکس حداکثر ۱۰ مگابایت)
</div>
{/* نمایش عکس‌های آپلود شده */}
{imagePreviewUrls.length > 0 && (
<div className='grid grid-cols-2 md:grid-cols-3 gap-4 mb-4'>
{imagePreviewUrls.map((url, index) => (
<div key={index} className='relative'>
<Image
src={url}
width={100}
height={100}
className='max-w-[200px] max-h-[200px] object-contain rounded border'
alt={`تصویر ${index + 1}`}
/>
<button
type='button'
onClick={() => removeImage(index)}
className='absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center'
>
<CloseCircle color='white' size={14} />
</button>
</div>
))}
</div>
)}
{/* دکمه آپلود */}
{images.length < 5 && (
<div>
<input
ref={fileInputRef}
type='file'
accept='image/jpeg,image/png'
multiple
onChange={handleImageUpload}
className='hidden'
/>
<button
type='button'
onClick={() => fileInputRef.current?.click()}
className='flex items-center gap-2 px-4 py-2 border-2 border-dashed border-gray-300 rounded-lg hover:border-primary transition-colors'
>
<DocumentUpload color='gray' size={20} />
<span>انتخاب عکس</span>
</button>
</div>
)}
</div>
{/* دکمه‌های عملیات */}
<div className='flex gap-3 justify-center'>
<Button
type="button"
variant="outline"
onClick={onBack}
>
بازگشت
</Button>
<Button
type="submit"
disabled={isLoading}
isLoading={isLoading}
>
ادامه
</Button>
</div>
</form>
</div>
)
}
export default ReturnForm
+221 -2
View File
@@ -1,8 +1,227 @@
import { NextPage } from 'next'
'use client'
import Layout from '@/hoc/Layout'
import { type NextPage } from 'next'
import { useParams, useRouter } from 'next/navigation'
import { useState } from 'react'
import { useGetOrderDetail, useReturnOrder, useGetReturnReasons } from '../../hooks/useOrdersData'
import ProductSelection from './components/ProductSelection'
import ReturnForm from './components/ReturnForm'
import ReturnConfirmation from './components/ReturnConfirmation'
import { toast } from '@/components/Toast'
import { extractErrorMessage } from '@/helpers/errorUtils'
import { Button } from '@/components/ui/button'
type Step = 1 | 2 | 3
interface FormData {
quantities: { [key: string]: number }
reason: string
description: string
images: File[]
}
const ReturnPage: NextPage = () => {
const { id } = useParams()
const router = useRouter()
const { data: order, isLoading: isOrderLoading } = useGetOrderDetail(id as string)
const returnOrderMutation = useReturnOrder()
const { data: returnReasons } = useGetReturnReasons()
const [currentStep, setCurrentStep] = useState<Step>(1)
const [selectedItems, setSelectedItems] = useState<string[]>([])
const [formData, setFormData] = useState<FormData | null>(null)
const [imagePreviewUrls, setImagePreviewUrls] = useState<string[]>([])
const handleItemSelect = (itemId: string, checked: boolean) => {
if (checked) {
setSelectedItems(prev => [...prev, itemId])
} else {
setSelectedItems(prev => prev.filter(id => id !== itemId))
}
}
const handleFormSubmit = (data: FormData) => {
setFormData(data)
// ایجاد preview URLs برای تصاویر
const previewUrls = data.images.map(file => URL.createObjectURL(file))
setImagePreviewUrls(previewUrls)
setCurrentStep(3)
}
const handleFinalSubmit = async () => {
if (!formData || selectedItems.length === 0) {
toast('اطلاعات ناقص است', 'error')
return
}
// تبدیل تصاویر به base64 یا URL
const imagePromises = formData.images.map(async (file) => {
return new Promise<string>((resolve) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result as string)
reader.readAsDataURL(file)
})
})
const imageUrls = await Promise.all(imagePromises)
const returnItems = selectedItems.map(itemId => {
// Find the order item that contains this shipment item
const orderItem = order?.results.order.orderItems.find(orderItem =>
orderItem.shipmentItems.some(shipmentItem => shipmentItem._id === itemId)
)
const shipmentItem = orderItem?.shipmentItems.find(item => item._id === itemId)
if (!orderItem || !shipmentItem) return null
return {
orderItemId: orderItem._id,
shipmentItemId: shipmentItem._id,
quantity: formData.quantities[itemId],
comment: formData.description,
reason: formData.reason,
images: imageUrls
}
}).filter(Boolean) as { orderItemId: string; shipmentItemId: string; quantity: number; comment: string; reason: string; images: string[] }[]
await returnOrderMutation.mutateAsync({
orderId: Number(id),
items: returnItems
}, {
onSuccess: () => {
toast('درخواست مرجوعی با موفقیت ثبت شد', 'success')
router.push(`/profile/orders/${id}/return/success`)
},
onError: (error) => {
toast(extractErrorMessage(error), 'error')
}
})
}
const handleBackToStep1 = () => {
setCurrentStep(1)
setFormData(null)
setImagePreviewUrls([])
}
const handleBackToStep2 = () => {
setCurrentStep(2)
}
// Flatten all shipment items from all order items
const allShipmentItems = order?.results.order.orderItems
.flatMap(orderItem => orderItem.shipmentItems) || []
// Filter selected shipment items
const selectedShipmentItems = allShipmentItems.filter(item =>
selectedItems.includes(item._id)
)
if (isOrderLoading) {
return (
<Layout>
<div className='mt-14 px-4 sm:px-8 lg:px-20'>
<div className='flex justify-center items-center min-h-[400px]'>
<div className='text-center'>
<div className='animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4'></div>
<p>در حال بارگذاری...</p>
</div>
</div>
</div>
</Layout>
)
}
if (!order) {
return (
<Layout>
<div className='mt-14 px-4 sm:px-8 lg:px-20'>
<div className='text-center min-h-[400px] flex items-center justify-center'>
<p>سفارش یافت نشد</p>
</div>
</div>
</Layout>
)
}
return (
<div>page</div>
<Layout>
<div className='mt-14 px-4 sm:px-8 lg:px-20 mx-auto'>
{/* Header */}
<div className='mb-6'>
<div className='flex items-center justify-between mb-4'>
<h1 className='text-xl font-bold'>انتخاب کالاهای مرجوعی</h1>
<div className='text-sm text-gray-600'>شناسه سفارش: {order.results.order._id}</div>
</div>
<div className='border-t border-gray-200 mb-4'></div>
{/* Warning Message */}
<div className='flex items-start gap-3 p-4 bg-orange-50 border border-orange-200 rounded-lg mb-6'>
<div className='w-5 h-5 bg-orange-500 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5'>
<span className='text-white text-xs font-bold'>!</span>
</div>
<p className='text-sm text-gray-700 leading-relaxed'>
دقت کنید که برچسب سریال کالا (چسبیده روی بسته) را سالم نگه دارید و کالای اصلی را با تمام لوازم در بسته بندی اولیه قرار دهید
</p>
</div>
</div>
<div className='space-y-8'>
{currentStep === 1 && (
<>
<ProductSelection
items={allShipmentItems}
selectedItems={selectedItems}
onItemSelect={handleItemSelect}
/>
<div className='flex gap-3 mt-8 justify-center'>
<Button
onClick={() => router.back()}
variant="outline"
size="default"
>
بازگشت
</Button>
<Button
onClick={() => setCurrentStep(2)}
disabled={selectedItems.length === 0}
size="default"
>
ادامه
</Button>
</div>
</>
)}
{currentStep === 2 && (
<ReturnForm
selectedItems={selectedShipmentItems}
onSubmit={handleFormSubmit}
onBack={handleBackToStep1}
isLoading={false}
returnReasons={returnReasons?.results.reasons || []}
/>
)}
{currentStep === 3 && formData && (
<ReturnConfirmation
selectedItems={selectedShipmentItems}
quantities={formData.quantities}
reason={formData.reason}
description={formData.description}
images={formData.images}
imagePreviewUrls={imagePreviewUrls}
returnReasons={returnReasons?.results.reasons || []}
onSubmit={handleFinalSubmit}
onBack={handleBackToStep2}
isLoading={returnOrderMutation.isPending}
/>
)}
</div>
</div>
</Layout>
)
}
@@ -0,0 +1,39 @@
import { Button } from "@/components/ui/button"
import Layout from "@/hoc/Layout"
import { NextPage } from "next"
import Image from "next/image"
import Link from "next/link"
const SuccesPage: NextPage = () => {
return (
<Layout>
<div className='w-full lg:px-10 px-4 mt-20 '>
<div className="shadow-lg rounded-2xl p-6 max-w-4xl mx-auto flex justify-between items-center">
<div className="flex-1">
<div className="text-[#019907]">
درخواست مرجوعی ثبت شد
</div>
<p className="mt-5 text-[#7F7F7F] font-light text-sm">
درخواست شما درحال بررسی است در صورت تایید یا عدم تایید درخواست، با پیامک به شما اطلاع میدهیم
</p>
<Link href="/profile/orders">
<Button className="mt-20">پیگیری درخواست</Button>
</Link>
</div>
<Image
src="/images/success.png"
alt="return-success"
width={200}
height={130}
className="max-w-[200px] max-h-[130px] h-full object-contain"
/>
</div>
</div>
</Layout>
)
}
export default SuccesPage
@@ -30,3 +30,16 @@ export const useGetCancelReasons = () => {
queryFn: api.getCancelReasons,
});
};
export const useGetReturnReasons = () => {
return useQuery({
queryKey: ["returnReasons"],
queryFn: api.getReturnReasons,
});
};
export const useReturnOrder = () => {
return useMutation({
mutationFn: api.returnOrder,
});
};
+12
View File
@@ -4,6 +4,8 @@ import {
OrderDetailResponse,
CancelOrderType,
CancelReasonsResponse,
ReturnOrderType,
ReturnReasonsResponse,
} from "../types/Types";
export const getOrders = async (activeTab: string): Promise<OrdersResponse> => {
@@ -34,3 +36,13 @@ export const cancelOrder = async (params: CancelOrderType) => {
const { data } = await axios.post(`/cancel/user`, params);
return data;
};
export const getReturnReasons = async (): Promise<ReturnReasonsResponse> => {
const { data } = await axios.get(`/returns/reasons`);
return data;
};
export const returnOrder = async (params: ReturnOrderType) => {
const { data } = await axios.post(`/returns/user`, params);
return data;
};
+28
View File
@@ -313,3 +313,31 @@ export interface CancelOrderType {
orderId: number;
items: CancelOrderItem[];
}
export interface ReturnReason {
_id: string;
title: string;
deleted: boolean;
}
export interface ReturnReasonsResponse {
status: number;
success: boolean;
results: {
reasons: ReturnReason[];
};
}
export interface ReturnOrderItem {
orderItemId: string;
shipmentItemId: string;
quantity: number;
comment: string;
reason: string;
images: string[];
}
export interface ReturnOrderType {
orderId: number;
items: ReturnOrderItem[];
}