From 9d18c8c6129f1f42af6dd1d0009dbf601300f824 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Thu, 30 Oct 2025 11:35:14 +0330 Subject: [PATCH] returns list --- src/config/Pages.ts | 1 + src/pages/orders/Returns.tsx | 125 ++++++++++++++++++ .../orders/components/ReturnItemsModal.tsx | 104 +++++++++++++++ src/pages/orders/hooks/useOrderData.ts | 7 + src/pages/orders/service/OrderService.ts | 7 + src/pages/orders/types/ReturnTypes.ts | 43 ++++++ src/router/Main.tsx | 3 + src/shared/components/OrdersSubMenu.tsx | 6 + 8 files changed, 296 insertions(+) create mode 100644 src/pages/orders/Returns.tsx create mode 100644 src/pages/orders/components/ReturnItemsModal.tsx create mode 100644 src/pages/orders/types/ReturnTypes.ts diff --git a/src/config/Pages.ts b/src/config/Pages.ts index e6cddc1..8e5afd0 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -187,6 +187,7 @@ export const Pages = { returnCategories: "/orders/return-categories", detailUser: "/orders/detail-user/", detailSeller: "/orders/detail-seller/", + return: "/orders/returns", }, shipment: { list: "/shipment/list", diff --git a/src/pages/orders/Returns.tsx b/src/pages/orders/Returns.tsx new file mode 100644 index 0000000..a73e674 --- /dev/null +++ b/src/pages/orders/Returns.tsx @@ -0,0 +1,125 @@ +import PageTitle from '@/components/PageTitle' +import { type FC, useState } from 'react' +import { useGetOrderReturns } from './hooks/useOrderData' +import PageLoading from '@/components/PageLoading' +import Error from '@/components/Error' +import PaginationUi from '@/components/PaginationUi' +import Td from '@/components/Td' +import StatusWithText from '@/components/StatusWithText' +import { formatDateShort, formatPrice } from '@/helpers/func' +import { type ReturnOrderType } from '@/pages/orders/types/ReturnTypes' +import { Eye } from 'iconsax-react' +import ReturnItemsModal from '@/pages/orders/components/ReturnItemsModal' + +const Returns: FC = () => { + const [currentPage, setCurrentPage] = useState(1) + const [isModalOpen, setIsModalOpen] = useState(false) + const [selectedReturn, setSelectedReturn] = useState(null) + const { data, isLoading, error } = useGetOrderReturns(currentPage) + + if (isLoading) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+ +
+ ) + } + + const returnsOrders = data?.results?.returnsOrders || [] + const pager = data?.results?.pager + + const getStatusVariant = (status: string) => { + switch (status) { + case 'Approved': return 'success' + case 'Rejected': return 'error' + case 'Pending': + default: return 'warning' + } + } + + const getStatusText = (status: string) => { + switch (status) { + case 'Approved': return 'تایید شده' + case 'Rejected': return 'رد شده' + case 'Pending': return 'در انتظار' + default: return status + } + } + + return ( +
+ + +
+ + + + + + + {returnsOrders.length === 0 ? ( + + + + ) : ( + returnsOrders.map((ret: ReturnOrderType) => ( + + + + + )) + )} + +
+ + + + + + +
+ مرجوعی یافت نشد +
+ + + + + + + + +
+
+ + setCurrentPage(page)} + /> + + { setIsModalOpen(false); setSelectedReturn(null); }} + data={selectedReturn} + /> +
+ ) +} + +export default Returns \ No newline at end of file diff --git a/src/pages/orders/components/ReturnItemsModal.tsx b/src/pages/orders/components/ReturnItemsModal.tsx new file mode 100644 index 0000000..936ec4c --- /dev/null +++ b/src/pages/orders/components/ReturnItemsModal.tsx @@ -0,0 +1,104 @@ +import { type FC, Fragment } from 'react' +import DefaulModal from '@/components/DefaulModal' +import StatusWithText from '@/components/StatusWithText' +import Td from '@/components/Td' +import { formatDateShort, formatPrice } from '@/helpers/func' +import { type ReturnOrderItemType, type ReturnOrderType } from '@/pages/orders/types/ReturnTypes' + +type Props = { + open: boolean + close: () => void + data: ReturnOrderType | null +} + +const getStatusVariant = (status: string) => { + switch (status) { + case 'Approved': return 'success' + case 'Rejected': return 'error' + case 'Pending': + default: return 'warning' + } +} + +const getStatusText = (status: string) => { + switch (status) { + case 'Approved': return 'تایید شده' + case 'Rejected': return 'رد شده' + case 'Pending': return 'در انتظار' + default: return status + } +} + +const ReturnItemsModal: FC = ({ open, close, data }) => { + const items = Array.isArray((data as any)?.returnOrderItems) + ? (data as any).returnOrderItems as ReturnOrderItemType[] + : (data?.returnOrderItems ? [data.returnOrderItems as ReturnOrderItemType] : []) + + return ( + + {!data ? ( +
داده‌ای برای نمایش وجود ندارد
+ ) : ( + +
+
+
شماره سفارش
+
#{data.order?._id ?? '-'}
+
+
+
فروشگاه
+
{data.shopName ?? '-'}
+
+
+
وضعیت
+
+ +
+
+
+
مبلغ کل
+
{formatPrice(Number(data.total_price || 0))} تومان
+
+
+
تاریخ ثبت
+
{formatDateShort(data.createdAt)}
+
+
+ +
+ + + + + + + {items.length === 0 ? ( + + + + ) : ( + items.map((it, idx) => ( + + + )) + )} + +
+ + + +
آیتمی ثبت نشده است
+ + + +
+
+
+ )} +
+ ) +} + +export default ReturnItemsModal + + diff --git a/src/pages/orders/hooks/useOrderData.ts b/src/pages/orders/hooks/useOrderData.ts index 13c401f..90425de 100644 --- a/src/pages/orders/hooks/useOrderData.ts +++ b/src/pages/orders/hooks/useOrderData.ts @@ -71,3 +71,10 @@ export const useDeleteReturnReason = () => { mutationFn: api.deleteReturnReason, }); }; + +export const useGetOrderReturns = (page: number = 1) => { + return useQuery({ + queryKey: ["order-returns", page], + queryFn: () => api.getOrderReturns(page), + }); +}; diff --git a/src/pages/orders/service/OrderService.ts b/src/pages/orders/service/OrderService.ts index 1b0cb28..7b01b95 100644 --- a/src/pages/orders/service/OrderService.ts +++ b/src/pages/orders/service/OrderService.ts @@ -70,3 +70,10 @@ export const deleteReturnReason = async (id: string) => { const { data } = await axios.delete(`/admin/orders/returns/reasons/${id}`); return data; }; + +export const getOrderReturns = async (page: number = 1) => { + const { data } = await axios.get(`/admin/orders/returns`, { + params: { page }, + }); + return data; +}; diff --git a/src/pages/orders/types/ReturnTypes.ts b/src/pages/orders/types/ReturnTypes.ts new file mode 100644 index 0000000..11bc377 --- /dev/null +++ b/src/pages/orders/types/ReturnTypes.ts @@ -0,0 +1,43 @@ +import type { PagerType, ShipmentAddressType } from "@/pages/orders/types/Types"; + +export type ReturnOrderItemType = { + quantity: number; + comment: string; + reason: string; + _id: string; + orderItem: { + _id: string; + totalSellingPrice: number; + }; +}; + +export type ReturnOrderEmbeddedOrderType = { + _id: number; + user: string; + payment: string; + shipmentAddress: ShipmentAddressType; + orderStatus: string; + createdAt: string; + updatedAt: string; + __v: number; +}; + +export type ReturnOrderType = { + _id: string; + order: ReturnOrderEmbeddedOrderType; + status: "Pending" | "Approved" | "Rejected" | string; + total_price: number; + createdAt: string; + updatedAt: string; + returnOrderItems: ReturnOrderItemType; + shopName: string; +}; + +export type ReturnOrdersResponseType = { + status: number; + success: boolean; + results: { + pager: PagerType; + returnsOrders: ReturnOrderType[]; + }; +}; diff --git a/src/router/Main.tsx b/src/router/Main.tsx index bef48db..3a777c4 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -80,6 +80,7 @@ import UpdateCategory from '@/pages/ticket/Update' import TicketMessages from '@/pages/ticket/TicketMessages' import Reviews from '@/pages/products/Reviews' import Questions from '@/pages/products/Questions' +import Returns from '@/pages/orders/Returns' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -196,6 +197,8 @@ const MainRouter: FC = () => { } /> } /> + + } /> diff --git a/src/shared/components/OrdersSubMenu.tsx b/src/shared/components/OrdersSubMenu.tsx index 29cd408..f673804 100644 --- a/src/shared/components/OrdersSubMenu.tsx +++ b/src/shared/components/OrdersSubMenu.tsx @@ -52,6 +52,12 @@ const OrdersSubMenu: FC = () => { isActive={isActive('return-categories')} link={Pages.orders.returnCategories} /> + + )