detail order

This commit is contained in:
hamid zarghami
2025-09-29 10:38:28 +03:30
parent d5cbee4b82
commit 09d0e0ce90
4 changed files with 286 additions and 14 deletions
+8
View File
@@ -25,3 +25,11 @@ export const formatDateShort = (dateString: string): string => {
return dateString;
}
};
export const formatPrice = (price: number): string => {
return new Intl.NumberFormat("fa-IR", {
style: "decimal",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(price);
};
+227 -5
View File
@@ -1,15 +1,237 @@
import { type FC } from 'react'
// import { useParams } from 'react-router-dom'
// import { useGetOrderDetailUser } from './hooks/useOrderData'
import { useParams } from 'react-router-dom'
import { useGetOrderDetailUser } from './hooks/useOrderData'
import PageLoading from '@/components/PageLoading'
import { formatDate, formatPrice } from '@/helpers/func'
import StatusWithText from '@/components/StatusWithText'
import { OrdersStatus, PaymentStatus, OrderItemsStatus } from './enum/Enum'
import { Profile, Calendar, Money3, Truck, Box, Location } from 'iconsax-react'
const OrderDetail: FC = () => {
const { id } = useParams()
const { data, isLoading, error } = useGetOrderDetailUser(id!)
// const { id } = useParams()
// const { data, isLoading, error } = useGetOrderDetailUser(id!)
if (isLoading) {
return <PageLoading />
}
if (error) {
return (
<div className="flex justify-center items-center min-h-96">
<div className="text-center">
<div className="text-red-500 text-lg mb-2">خطا در بارگذاری دادهها</div>
<div className="text-gray-500">لطفا دوباره تلاش کنید</div>
</div>
</div>
)
}
if (!data?.results?.order) {
return (
<div className="flex justify-center items-center min-h-96">
<div className="text-center">
<div className="text-gray-500 text-lg">سفارش یافت نشد</div>
</div>
</div>
)
}
const order = data.results.order
const getOrderStatusVariant = (status: string) => {
switch (status) {
case OrdersStatus.Delivered:
return 'success'
case OrdersStatus.Cancelled:
case OrdersStatus.cancelled_system:
return 'error'
default:
return 'warning'
}
}
const getPaymentStatusVariant = (status: string) => {
switch (status) {
case PaymentStatus.Completed:
return 'success'
case PaymentStatus.Cancelled:
return 'error'
default:
return 'warning'
}
}
const getOrderItemStatusVariant = (status: string) => {
switch (status) {
case OrderItemsStatus.Delivered:
return 'success'
case OrderItemsStatus.Returned:
case OrderItemsStatus.cancelled_shop:
case OrderItemsStatus.cancelled_system:
case OrderItemsStatus.cancelled_user:
return 'error'
default:
return 'warning'
}
}
return (
<div className='mt-4 space-y-6'>
{/* هدر */}
<div className='flex justify-between items-center'>
<div className='flex items-center gap-3'>
<h1 className='text-2xl font-bold'>جزئیات سفارش</h1>
<StatusWithText
variant={getOrderStatusVariant(order.orderStatus)}
text={order.orderStatus}
/>
</div>
<div className='text-sm text-gray-600'>
شماره سفارش: <span className='font-mono'>#{order._id}</span>
</div>
</div>
{/* کارت‌های اطلاعاتی */}
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6'>
{/* اطلاعات کاربر */}
<div className='bg-white p-6 rounded-3xl'>
<div className='flex items-center gap-3 mb-4'>
<div className='p-2 bg-blue-100 rounded-xl'>
<Profile size={20} color='#3B82F6' />
</div>
<h3 className='font-medium'>مشتری</h3>
</div>
<div className='space-y-2'>
<div className='text-lg font-semibold'>{order.user.fullName}</div>
<div className='text-sm text-gray-600 flex items-center gap-2'>
<Calendar size={14} color='#6B7280' />
<div className='mt-1 dltr'>
{order.createdAt}
</div>
</div>
</div>
</div>
{/* اطلاعات پرداخت */}
<div className='bg-white p-6 rounded-3xl'>
<div className='flex items-center gap-3 mb-4'>
<div className='p-2 bg-green-100 rounded-xl'>
<Money3 size={20} color='#10B981' />
</div>
<h3 className='font-medium'>پرداخت</h3>
</div>
<div className='space-y-2'>
<div className='text-lg font-bold text-green-600'>
{formatPrice(order.payment.totalPrice)} تومان
</div>
<div className='text-sm text-gray-600'>
{order.payment.payment_method.title_fa}
</div>
<StatusWithText
variant={getPaymentStatusVariant(order.payment.paymentStatus)}
text={order.payment.paymentStatus}
/>
</div>
</div>
{/* آدرس ارسال */}
<div className='bg-white p-6 rounded-3xl md:col-span-2'>
<div className='flex items-center gap-3 mb-4'>
<div className='p-2 bg-orange-100 rounded-xl'>
<Location size={20} color='#F59E0B' />
</div>
<h3 className='font-medium'>آدرس ارسال</h3>
</div>
<div className='space-y-3'>
<div className='text-sm leading-relaxed'>{order.shipmentAddress.address}</div>
<div className='grid grid-cols-2 gap-4 text-sm text-gray-600'>
<div>استان: {order.shipmentAddress.province}</div>
<div>شهر: {order.shipmentAddress.city}</div>
<div>پلاک: {order.shipmentAddress.plaque}</div>
<div>کد پستی: <span className='font-mono'>{order.shipmentAddress.postalCode}</span></div>
<div className='col-span-2'>تلفن: {order.shipmentAddress.phone}</div>
</div>
</div>
</div>
</div>
{/* آیتم‌های سفارش */}
<div className='bg-white py-8 xl:px-10 px-6 rounded-3xl'>
<div className='flex items-center gap-3 mb-6'>
<div className='p-2 bg-purple-100 rounded-xl'>
<Box size={20} color='#8B5CF6' />
</div>
<h2 className='text-lg font-medium'>آیتمهای سفارش</h2>
</div>
<div className='space-y-6'>
{order.orderItems.map((item, index) => (
<div key={index} className='border border-gray-100 rounded-xl p-6 bg-gray-50/50'>
<div className='flex justify-between items-start mb-6'>
<div className='flex items-center gap-3'>
<div className='p-2 bg-white rounded-lg'>
<Truck size={16} color='#6B7280' />
</div>
<div>
asd
<div className='font-medium'>فروشگاه: {item.shop?.shopName}</div>
<div className='flex items-center gap-2 mt-1'>
<StatusWithText
variant={getOrderItemStatusVariant(item.status)}
text={item.status}
/>
{item.trackCode && (
<span className='text-xs text-gray-600 font-mono'>
کد پیگیری: {item.trackCode}
</span>
)}
</div>
</div>
</div>
<div className='text-left'>
<div className='text-sm text-gray-600'>مجموع</div>
<div className='font-bold text-lg text-green-600'>
{formatPrice(item.totalPaymentPrice)} تومان
</div>
{item.postingDate && (
<div className='text-xs text-gray-600 mt-1'>
ارسال: {formatDate(item.postingDate)}
</div>
)}
</div>
</div>
{/* محصولات */}
<div className='space-y-3'>
{item.shipmentItems.map((product, productIndex) => (
<div key={productIndex} className='flex items-center gap-4 p-4 bg-white rounded-lg border border-gray-100'>
<div className='flex-1'>
<div className='font-medium text-base mb-2'>{product.product.title_fa}</div>
<div className='grid grid-cols-2 gap-4 text-sm text-gray-600'>
<div>گارانتی: {product.variant.warranty.name}</div>
<div>تعداد: {product.quantity}</div>
<div>قیمت واحد: {formatPrice(product.selling_price)} تومان</div>
</div>
</div>
<div className='text-left'>
<div className='font-bold text-base text-green-600 mb-1'>
{formatPrice(product.quantity * product.selling_price)} تومان
</div>
{product.discount_percent > 0 && (
<div className='text-sm text-green-600'>
تخفیف: {product.discount_percent}%
</div>
)}
</div>
</div>
))}
</div>
</div>
))}
</div>
</div>
</div>
)
}
+8 -3
View File
@@ -1,5 +1,8 @@
import axios from "../../../config/axios";
import type { NativeOrdersResponseType } from "../types/Types";
import type {
NativeOrdersResponseType,
OrderDetailResponseType,
} from "../types/Types";
export const getNativeOrders = async (
page: number = 1,
@@ -21,7 +24,9 @@ export const getSellerOrders = async (
return data;
};
export const getOrderDetailUser = async (id: string) => {
const { data } = await axios.get(`/order/${id}/user`);
export const getOrderDetailUser = async (
id: string
): Promise<OrderDetailResponseType> => {
const { data } = await axios.get(`/admin/orders/${id}/user`);
return data;
};
+42 -5
View File
@@ -93,7 +93,7 @@ export type ProductType = {
specifications: SpecificationType[];
brand: BrandType;
category: CategoryType;
variants: string[];
variants: { _id: string }[];
};
export type ImagesUrlType = {
@@ -114,6 +114,18 @@ export type CategoryType = {
_id: string;
};
export type WarrantyType = {
_id: number;
duration: string;
logoUrl: string;
name: string;
};
export type MeterageType = {
_id: number;
value: string;
};
export type VariantType = {
_id: string;
market_status: string;
@@ -123,10 +135,8 @@ export type VariantType = {
isFreeShip: boolean;
isWholeSale: boolean;
shop: ShopType;
saleFormat: SaleFormatType;
warranty: number;
size?: number;
meterage?: number;
warranty: WarrantyType;
meterage: MeterageType;
};
export type PriceType = {
@@ -146,6 +156,14 @@ export type SaleFormatType = {
export type ShopType = {
_id: string;
shopName?: string;
shopCode?: number;
owner?: string;
shopDescription?: string;
telephoneNumber?: string;
isChatActive?: boolean;
shopPostalCode?: string;
logo?: string;
};
export type ShipperType = {
@@ -243,3 +261,22 @@ export type NativeOrdersResponseType = {
priceRange: PriceRangeType;
};
};
// تایپ‌های مربوط به جزئیات سفارش
export type OrderDetailType = {
_id: number;
user: UserType;
payment: PaymentType;
orderItems: OrderItemsType[];
shipmentAddress: ShipmentAddressType;
orderStatus: string;
createdAt: string;
};
export type OrderDetailResponseType = {
status: number;
success: boolean;
results: {
order: OrderDetailType;
};
};