From db6157ae5d44f7166aeb19dd0470025c894e7936 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 12 Oct 2025 10:48:53 +0330 Subject: [PATCH] list of request create product --- src/config/Pages.ts | 1 + src/pages/seller/CategoryLearning.tsx | 9 + src/pages/seller/RequestCreateProduct.tsx | 94 +++++++ .../components/ProductRequestDetailModal.tsx | 249 ++++++++++++++++++ .../components/ProductRequestTableRow.tsx | 104 ++++++++ src/pages/seller/hooks/useSellerData.ts | 7 + src/pages/seller/service/SellerService.ts | 10 + src/pages/seller/types/Types.ts | 106 ++++++++ src/router/Main.tsx | 2 + src/shared/SideBar.tsx | 4 +- 10 files changed, 584 insertions(+), 2 deletions(-) create mode 100644 src/pages/seller/CategoryLearning.tsx create mode 100644 src/pages/seller/RequestCreateProduct.tsx create mode 100644 src/pages/seller/components/ProductRequestDetailModal.tsx create mode 100644 src/pages/seller/components/ProductRequestTableRow.tsx diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 42668a4..fcf342e 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -199,5 +199,6 @@ export const Pages = { wholesaleRequests: "/sellers/wholesale-requests", withdrawalRequests: "/sellers/withdrawal-requests", announcement: "/sellers/announcement", + requestCreateProduct: "/sellers/request-create-product", }, }; diff --git a/src/pages/seller/CategoryLearning.tsx b/src/pages/seller/CategoryLearning.tsx new file mode 100644 index 0000000..a254501 --- /dev/null +++ b/src/pages/seller/CategoryLearning.tsx @@ -0,0 +1,9 @@ +import { type FC } from 'react' + +const CategoryLearning = () => { + return ( +
CategoryLearning
+ ) +} + +export default CategoryLearning \ No newline at end of file diff --git a/src/pages/seller/RequestCreateProduct.tsx b/src/pages/seller/RequestCreateProduct.tsx new file mode 100644 index 0000000..09efaae --- /dev/null +++ b/src/pages/seller/RequestCreateProduct.tsx @@ -0,0 +1,94 @@ +import { type FC, useState } from 'react' +import { useGetRequestCreateProduct } from './hooks/useSellerData' +import { type ProductRequest } from './types/Types' +import PageLoading from '../../components/PageLoading' +import Error from '../../components/Error' +import PaginationUi from '../../components/PaginationUi' +import Td from '../../components/Td' +import ProductRequestTableRow from './components/ProductRequestTableRow' +import ProductRequestDetailModal from './components/ProductRequestDetailModal' + +const RequestCreateProduct: FC = () => { + const [currentPage, setCurrentPage] = useState(1); + const [selectedRequest, setSelectedRequest] = useState(null); + const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); + const { data, isLoading, error } = useGetRequestCreateProduct(currentPage); + + const handleRowClick = (request: ProductRequest) => { + setSelectedRequest(request); + setIsDetailModalOpen(true); + }; + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (error) { + return ( +
+ +
+ ); + } + + const productRequests = data?.results?.productRequests || []; + const pager = data?.results?.pager; + + return ( +
+
+ + + + + + + {productRequests.length === 0 ? ( + + + + ) : ( + productRequests.map((request: ProductRequest) => ( + handleRowClick(request)} + /> + )) + )} + +
+ + + + + + + + +
+ هیچ درخواست ایجاد محصولی یافت نشد +
+
+ + { + setCurrentPage(page); + }} + /> + + setIsDetailModalOpen(false)} + request={selectedRequest} + /> +
+ ) +} + +export default RequestCreateProduct \ No newline at end of file diff --git a/src/pages/seller/components/ProductRequestDetailModal.tsx b/src/pages/seller/components/ProductRequestDetailModal.tsx new file mode 100644 index 0000000..9c0411c --- /dev/null +++ b/src/pages/seller/components/ProductRequestDetailModal.tsx @@ -0,0 +1,249 @@ +import { type FC } from 'react' +import DefaulModal from '@/components/DefaulModal' +import { type ProductRequest } from '../types/Types' +import StatusWithText from '@/components/StatusWithText' +import { Calendar, User as UserIcon, Shop, Tag, Box, MessageSquare, Location, Money } from 'iconsax-react' + +interface Props { + open: boolean + close: () => void + request: ProductRequest | null +} + +const ProductRequestDetailModal: FC = ({ open, close, request }) => { + if (!request) return null + + const getStatusVariant = (status: string) => { + switch (status) { + case 'Approved': return 'success' + case 'Rejected': return 'error' + case 'Pending': return 'warning' + case 'Draft': return 'warning' + default: return 'warning' + } + } + + const getStatusText = (status: string) => { + switch (status) { + case 'Approved': return 'تایید شده' + case 'Rejected': return 'رد شده' + case 'Pending': return 'در انتظار' + case 'Draft': return 'پیش‌نویس' + default: return status + } + } + + const formatDate = (dateString: string) => { + if (!dateString) return '-' + return new Date(dateString).toLocaleDateString('fa-IR') + } + + return ( + +
+ {/* وضعیت درخواست */} +
+

وضعیت درخواست

+ +
+ + {/* اطلاعات فروشنده */} +
+

+ + اطلاعات فروشنده +

+
+
+ + نام: + {request.seller?.fullName || '-'} +
+
+ + فروشگاه: + {request.shop?.shopName || '-'} +
+
+ + آدرس: + {request.shop?.address?.province?.name || '-'}، {request.shop?.address?.city?.name || '-'} +
+
+ + کد فروشگاه: + {request.shop?.shopCode || '-'} +
+
+
+ + {/* اطلاعات محصول */} +
+

+ + اطلاعات محصول +

+
+
+ + نام محصول: + {request.productName} +
+
+ + برند: + {request.brand?.title_fa || '-'} +
+
+ + دسته‌بندی: + {request.category?.title_fa || '-'} +
+
+ + منبع: + {request.source || '-'} +
+
+
+ + {/* توضیحات */} +
+

توضیحات

+

{String(request.description || 'توضیحاتی وارد نشده')}

+
+ + {/* ابعاد و وزن */} +
+

+ + ابعاد و وزن +

+
+
+
{request.dimensions?.package_weight || 0}
+
وزن (گرم)
+
+
+
{request.dimensions?.package_length || 0}
+
طول (سانتی‌متر)
+
+
+
{request.dimensions?.package_width || 0}
+
عرض (سانتی‌متر)
+
+
+
{request.dimensions?.package_height || 0}
+
ارتفاع (سانتی‌متر)
+
+
+
+ + {/* مزایا و معایب */} +
+
+

مزایا

+ {request.advantages && request.advantages.length > 0 ? ( +
    + {request.advantages.map((advantage, index) => ( +
  • + + {String(advantage)} +
  • + ))} +
+ ) : ( +

مزایایی وارد نشده

+ )} +
+ +
+

معایب

+ {request.disAdvantages && request.disAdvantages.length > 0 ? ( +
    + {request.disAdvantages.map((disadvantage, index) => ( +
  • + + {String(disadvantage)} +
  • + ))} +
+ ) : ( +

معایبی وارد نشده

+ )} +
+
+ + {/* خدمات درخواست شده */} +
+

خدمات درخواست شده

+
+
+ + 📸 عکاسی محصول + {request.requestPhotosCount && ( + ({request.requestPhotosCount} عکس) + )} +
+
+ + 👨‍💼 بررسی تخصصی +
+
+ + 📦 ویدیو باز کردن بسته +
+
+
+ + {/* اطلاعات پرداخت */} + {request.payment && ( +
+

+ + اطلاعات پرداخت +

+
+
{String(JSON.stringify(request.payment, null, 2))}
+
+
+ )} + + {/* تاریخ ایجاد */} +
+ تاریخ ایجاد: +
+ + {formatDate(request._id ? new Date().toISOString() : '')} +
+
+
+
+ ) +} + +export default ProductRequestDetailModal diff --git a/src/pages/seller/components/ProductRequestTableRow.tsx b/src/pages/seller/components/ProductRequestTableRow.tsx new file mode 100644 index 0000000..25395bc --- /dev/null +++ b/src/pages/seller/components/ProductRequestTableRow.tsx @@ -0,0 +1,104 @@ +import { type FC } from 'react' +import { type ProductRequest } from '../types/Types' +import StatusWithText from '../../../components/StatusWithText' +import Td from '../../../components/Td' +import { Calendar, User as UserIcon, Shop, Tag, Box, MessageSquare, Eye } from 'iconsax-react' + +interface Props { + request: ProductRequest + onClick?: () => void +} + +const ProductRequestTableRow: FC = ({ request, onClick }) => { + const getStatusVariant = (status: string) => { + switch (status) { + case 'Approved': return 'success'; + case 'Rejected': return 'error'; + case 'Pending': return 'warning'; + case 'Draft': return 'warning'; + default: return 'warning'; + } + }; + + const getStatusText = (status: string) => { + switch (status) { + case 'Approved': return 'تایید شده'; + case 'Rejected': return 'رد شده'; + case 'Pending': return 'در انتظار'; + case 'Draft': return 'پیش‌نویس'; + default: return status; + } + }; + + const formatDate = (dateString: string) => { + if (!dateString) return '-'; + return new Date(dateString).toLocaleDateString('fa-IR'); + }; + + return ( + + +
+ + {request.seller?.fullName || '-'} +
+ + +
+ + {request.shop?.shopName || '-'} +
+ + +
+ + {request.productName} +
+ + +
+ + {request.brand?.title_fa || '-'} +
+ + +
+ + {request.category?.title_fa || '-'} +
+ + + + + +
+ + {formatDate(request._id ? new Date().toISOString() : '')} +
+ + +
+ {request.requestPhotography && 📸 عکاسی} + {request.expertReview && 👨‍💼 بررسی تخصصی} + {request.unboxingVideo && 📦 ویدیو باز کردن بسته} +
+ + + { + e.stopPropagation(); + onClick?.(); + }} + /> + + + ) +} + +export default ProductRequestTableRow diff --git a/src/pages/seller/hooks/useSellerData.ts b/src/pages/seller/hooks/useSellerData.ts index 884ef01..f98f9f1 100644 --- a/src/pages/seller/hooks/useSellerData.ts +++ b/src/pages/seller/hooks/useSellerData.ts @@ -59,3 +59,10 @@ export const useCreateAnnouncementSpecificSeller = () => { mutationFn: api.createAnnouncementToSpecificSellers, }); }; + +export const useGetRequestCreateProduct = (page: number = 1) => { + return useQuery({ + queryKey: ["request-create-product", page], + queryFn: () => api.getRequestCreateProduct(page), + }); +}; diff --git a/src/pages/seller/service/SellerService.ts b/src/pages/seller/service/SellerService.ts index 7ed1930..3ab80df 100644 --- a/src/pages/seller/service/SellerService.ts +++ b/src/pages/seller/service/SellerService.ts @@ -6,6 +6,7 @@ import type { CreateAnnouncementSeller, NotificationsResponse, CreateAnnouncementSpecificSeller, + GetRequestCreateProductResponse, } from "../types/Types"; export const getSellers = async ( @@ -77,3 +78,12 @@ export const createAnnouncementToSpecificSellers = async ( ); return data; }; + +export const getRequestCreateProduct = async ( + page: number = 1 +): Promise => { + const { data } = await axios.get( + `/admin/products/requests/add-by-admin?page=${page}` + ); + return data; +}; diff --git a/src/pages/seller/types/Types.ts b/src/pages/seller/types/Types.ts index 6f9807d..457840f 100644 --- a/src/pages/seller/types/Types.ts +++ b/src/pages/seller/types/Types.ts @@ -212,3 +212,109 @@ export interface NotificationsResponse { notifications: Notification[]; }; } + +export interface CreateCategoryLearning { + title: string; +} + +// Product Request Types +export interface City { + _id: number; + name: string; + province: number; + createdAt: string; + updatedAt: string; +} + +export interface Province { + _id: number; + name: string; + createdAt: string; + updatedAt: string; +} + +export interface Address { + _id: string; + address: string; + city: City; + province: Province; + postalCode: string | null; + plaque: string | null; + lat: string; + lon: string; + createdAt: string; + updatedAt: string; +} + +export interface ProductRequestShop { + _id: string; + shopName: string; + address: Address; + shopCode: number; +} + +export interface Brand { + _id: string; + title_fa: string; + title_en: string; + url: string; +} + +export interface Category { + _id: string; + title_fa: string; + title_en: string; + icon: string; + imageUrl: string; + description: string; + variants: string[]; + hierarchy: string[]; + leaf: boolean; + parent: string | null; + deleted: boolean; + createdAt: string; + updatedAt: string; + url: string; + children: Category[]; +} + +export interface Dimensions { + package_weight: number; + package_height: number; + package_length: number; + package_width: number; +} + +export interface ProductRequestSeller { + _id: string; + fullName: string; +} + +export interface ProductRequest { + requestPhotography: boolean; + requestPhotosCount: number | null; + expertReview: boolean; + unboxingVideo: boolean; + payment: unknown | null; + requestStatus: string; + _id: string; + seller: ProductRequestSeller; + shop: ProductRequestShop; + brand: Brand; + category: Category; + source: string; + productName: string; + description: string; + dimensions: Dimensions; + advantages: string[]; + disAdvantages: string[]; +} + +export interface GetRequestCreateProductResponse { + status: number; + success: boolean; + results: { + productRequests: ProductRequest[]; + pager: Pager; + }; +} diff --git a/src/router/Main.tsx b/src/router/Main.tsx index 039f675..effd1c3 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -66,6 +66,7 @@ import SellerList from '@/pages/seller/List' import WholeSaleRequest from '@/pages/seller/WholeSaleRequest' import WithdrawalRequestPage from '@/pages/seller/WithdrawalRequest' import Annoncement from '@/pages/seller/Annoncement' +import RequestCreateProduct from '@/pages/seller/RequestCreateProduct' const MainRouter: FC = () => { const { hasSubMenu } = useSharedStore() @@ -165,6 +166,7 @@ const MainRouter: FC = () => { } /> } /> } /> + } /> diff --git a/src/shared/SideBar.tsx b/src/shared/SideBar.tsx index adec588..d54f000 100644 --- a/src/shared/SideBar.tsx +++ b/src/shared/SideBar.tsx @@ -518,8 +518,8 @@ const SellersSubMenu: FC = () => { />