order detail
This commit is contained in:
@@ -0,0 +1,417 @@
|
||||
'use client'
|
||||
import { FC, useState } from 'react'
|
||||
import { ArrowRight2, ShieldTick, Truck, MessageText, CloseCircle } from 'iconsax-react'
|
||||
import { useRouter, useParams } from 'next/navigation'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
import * as yup from 'yup'
|
||||
import PageLoading from '@/components/PageLoading'
|
||||
import TrackCode from './components/TrackCode'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import Modal from '@/components/Modal'
|
||||
import Select from '@/components/Select'
|
||||
import Input from '@/components/Input'
|
||||
import withLayout from '@/hoc/withLayout'
|
||||
import Image from 'next/image'
|
||||
|
||||
// Mock data
|
||||
const mockOrderDetail = {
|
||||
payment: {
|
||||
transaction_id: 'TRX123456789'
|
||||
},
|
||||
order: {
|
||||
_id: 'ORD001',
|
||||
user: {
|
||||
fullName: 'احمد محمدی'
|
||||
},
|
||||
shipmentAddress: {
|
||||
postalCode: '1234567890',
|
||||
phone: '09123456789',
|
||||
address: 'تهران، خیابان ولیعصر، پلاک 123'
|
||||
},
|
||||
createdAt: '1402/12/15',
|
||||
orderItems: {
|
||||
_id: 'ITEM001',
|
||||
itemsCount: 3,
|
||||
status: 'processing',
|
||||
totalShipmentCost: '25000 تومان',
|
||||
totalPaymentPrice: '450000',
|
||||
shipper: {
|
||||
name: 'پست ایران',
|
||||
deliveryTime: 3
|
||||
},
|
||||
shipmentItems: [
|
||||
{
|
||||
_id: 'SHIP001',
|
||||
product: {
|
||||
_id: 'PROD001',
|
||||
title_fa: 'لپتاپ اپل مکبوک پرو 13 اینچی',
|
||||
imagesUrl: {
|
||||
cover: 'https://via.placeholder.com/80x80'
|
||||
}
|
||||
},
|
||||
quantity: 1,
|
||||
cancelled_quantity: 0,
|
||||
returned_quantity: 0,
|
||||
selling_price: 450000,
|
||||
variant: {
|
||||
color: {
|
||||
name: 'نقرهای'
|
||||
},
|
||||
size: {
|
||||
name: '13 اینچ'
|
||||
},
|
||||
meterages: {
|
||||
name: '256GB'
|
||||
},
|
||||
warranty: {
|
||||
duration: '2 سال',
|
||||
name: 'گارانتی'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mockCancelReasons = [
|
||||
{ _id: 'REASON1', title: 'تغییر نظر در خرید' },
|
||||
{ _id: 'REASON2', title: 'یافتن قیمت بهتر' },
|
||||
{ _id: 'REASON3', title: 'مشکل در محصول' },
|
||||
{ _id: 'REASON4', title: 'تأخیر در ارسال' }
|
||||
]
|
||||
|
||||
// Form validation schema
|
||||
const cancelOrderSchema = yup.object({
|
||||
reason: yup.string().required('لطفا دلیل لغو سفارش را انتخاب کنید'),
|
||||
description: yup.string().required('لطفا توضیحات لغو سفارش را وارد کنید'),
|
||||
count: yup.string().required('تعداد لغو سفارش را وارد کنید')
|
||||
})
|
||||
|
||||
type CancelOrderFormData = yup.InferType<typeof cancelOrderSchema>
|
||||
|
||||
const OrderDetail: FC = () => {
|
||||
const { id } = useParams()
|
||||
const [showModalCancel, setShowModalCancel] = useState<boolean>(false)
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||
const router = useRouter()
|
||||
|
||||
// Form with react-hook-form
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
reset
|
||||
} = useForm<CancelOrderFormData>({
|
||||
resolver: yupResolver(cancelOrderSchema)
|
||||
})
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'processing': 'در حال پردازش',
|
||||
'shipped': 'ارسال شده',
|
||||
'delivered': 'تحویل داده شده',
|
||||
'cancelled': 'لغو شده'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
setIsLoading(true)
|
||||
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
setIsLoading(false)
|
||||
setShowModalCancel(false)
|
||||
reset() // Reset form
|
||||
alert('درخواست لغو سفارش با موفقیت ثبت شد')
|
||||
router.push('/profile/orders')
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full lg:px-10 px-4 mt-20'>
|
||||
<div className='border p-6 rounded-2xl text-sm'>
|
||||
<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>
|
||||
|
||||
{isLoading ? (
|
||||
<PageLoading />
|
||||
) : (
|
||||
<div className='mt-6 lg:flex-row flex-col flex gap-6 items-start'>
|
||||
{/* Sidebar */}
|
||||
<div className='lg:w-[280px] w-full p-5 rounded-2xl shadow-lg'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>شناسه تراکنش :</div>
|
||||
<div className="font-mono">
|
||||
{mockOrderDetail.payment.transaction_id}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2 mt-4'>
|
||||
<div className='text-[#7F7F7F]'>گیرنده :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.user.fullName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2 mt-4'>
|
||||
<div className='text-[#7F7F7F]'>کدپستی :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.shipmentAddress.postalCode}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2 mt-4'>
|
||||
<div className='text-[#7F7F7F] whitespace-nowrap'>تاریخ ثبت سفارش :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.createdAt}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2 mt-4'>
|
||||
<div className='text-[#7F7F7F]'>شماره موبایل :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.shipmentAddress.phone}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-start gap-2 mt-4'>
|
||||
<div className='text-[#7F7F7F] whitespace-nowrap'>آدرس :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.shipmentAddress.address}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TrackCode orderId={Number(id) || 0} />
|
||||
|
||||
<Button
|
||||
className='mt-3 w-full'
|
||||
variant="outline"
|
||||
>
|
||||
مشاهده فاکتور
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className='flex-1 w-full bg-white shadow-lg p-6 rounded-2xl'>
|
||||
<div className='flex md:flex-row flex-col gap-4 justify-between md:items-center'>
|
||||
<div className='flex md:flex-row flex-col gap-4'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>تعداد مرسوله :</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>تعداد اقلام :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.orderItems.itemsCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='text-primary font-semibold'>
|
||||
{getStatusText(mockOrderDetail.order.orderItems.status)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2 mt-6'>
|
||||
<div className='text-[#7F7F7F]'>حداکثر مهلت ارسال :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.orderItems.shipper.deliveryTime} روز
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex md:flex-row flex-col gap-3 justify-between mt-6'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>هزینه ارسال :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.orderItems.totalShipmentCost}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>مبلغ مرسوله :</div>
|
||||
<div>
|
||||
{mockOrderDetail.order.orderItems.totalPaymentPrice} تومان
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-[#7F7F7F]'>شناسه سفارش :</div>
|
||||
<div className="font-mono">
|
||||
{mockOrderDetail.order._id}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product Items */}
|
||||
{mockOrderDetail.order.orderItems.shipmentItems.map((item) => (
|
||||
<div key={item.product._id} className='mt-8 py-7 border-t items-start flex gap-6 sm:flex-row flex-col'>
|
||||
<Image
|
||||
src={item.product.imagesUrl.cover}
|
||||
width={80}
|
||||
height={80}
|
||||
className='w-20 h-20 object-cover rounded-lg border'
|
||||
alt={item.product.title_fa}
|
||||
/>
|
||||
|
||||
<div className='flex-1 w-full'>
|
||||
<div className='truncate max-w-[90%] font-medium'>
|
||||
{item.product.title_fa}
|
||||
</div>
|
||||
|
||||
<div className='mt-4 text-[#999999] space-y-3'>
|
||||
<div className='flex gap-2'>
|
||||
<div>تعداد اقلام :</div>
|
||||
<div>{item.quantity}</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div>تعداد کنسلی :</div>
|
||||
<div>{item.cancelled_quantity}</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div>تعداد مرجوعی :</div>
|
||||
<div>{item.returned_quantity}</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-3 items-center'>
|
||||
<div className='size-3 bg-black rounded-full'></div>
|
||||
<div>
|
||||
{item.variant.color?.name ||
|
||||
item.variant.size?.name ||
|
||||
item.variant.meterages?.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-3 items-center'>
|
||||
<MessageText size={20} className="text-gray-400" />
|
||||
<div className="font-mono">
|
||||
SKP_{item.product._id}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-3 items-center'>
|
||||
<ShieldTick size={20} className="text-green-500" />
|
||||
<div>
|
||||
گارانتی {item.variant.warranty.duration} {item.variant.warranty.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-3 items-center'>
|
||||
<Truck size={20} className="text-blue-500" />
|
||||
<div>
|
||||
{mockOrderDetail.order.orderItems.shipper.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 flex justify-between items-center'>
|
||||
<div>
|
||||
<div className='text-lg font-semibold'>
|
||||
{Intl.NumberFormat('fa-IR').format(item.selling_price)}
|
||||
</div>
|
||||
<div className='text-xs mt-1 text-gray-500'>تومان</div>
|
||||
</div>
|
||||
|
||||
{item.quantity !== item.cancelled_quantity && (
|
||||
<button
|
||||
onClick={() => setShowModalCancel(true)}
|
||||
className='flex cursor-pointer gap-2 text-[#B3261E] hover:bg-red-50 p-2 rounded-lg transition-colors'
|
||||
>
|
||||
<div>لغو سفارش</div>
|
||||
<CloseCircle size={16} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Cancel Order Modal */}
|
||||
<Modal
|
||||
open={showModalCancel}
|
||||
close={() => setShowModalCancel(false)}
|
||||
isHeader
|
||||
title='درخواست لغو سفارش'
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className='mt-6'>
|
||||
<Select
|
||||
placeholder='دلیل لغو سفارش'
|
||||
items={mockCancelReasons.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}
|
||||
/>
|
||||
|
||||
<Input
|
||||
variant='floating_outlined'
|
||||
label='تعداد'
|
||||
className='mt-6'
|
||||
{...register('count')}
|
||||
error_text={errors.count?.message}
|
||||
/>
|
||||
|
||||
<div className='mt-6'>
|
||||
<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 className='flex gap-2 text-xs text-[#7F7F7F] mt-4 p-3 bg-blue-50 rounded-lg'>
|
||||
<div className="size-5 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-blue-600 text-xs">!</span>
|
||||
</div>
|
||||
<span>
|
||||
توجه داشته باشید که بعد از ثبت لغو سفارش طبق
|
||||
</span>
|
||||
<span className='text-primary font-medium'>
|
||||
جدول جرائم
|
||||
</span>
|
||||
<span>
|
||||
جریمه میشود
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Button
|
||||
type="submit"
|
||||
className='w-full'
|
||||
disabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
لغو سفارش
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default withLayout(OrderDetail)
|
||||
Reference in New Issue
Block a user