product list
This commit is contained in:
+133
-1
@@ -1,9 +1,141 @@
|
||||
import { type FC } from 'react'
|
||||
import { type FC, useState } from 'react'
|
||||
import { useGetProducts } from './hooks/useProductData';
|
||||
import { type ProductListType } from './types/Types';
|
||||
import { ProductStatus } from './enum/ProductEnum';
|
||||
import PageLoading from '../../components/PageLoading';
|
||||
import Error from '../../components/Error';
|
||||
import PaginationUi from '../../components/PaginationUi';
|
||||
import StatusWithText from '../../components/StatusWithText';
|
||||
import Td from '../../components/Td';
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim';
|
||||
import { Edit, Eye } from 'iconsax-react';
|
||||
|
||||
const List: FC = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const { data: productsData, isLoading, error } = useGetProducts(currentPage);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<PageLoading />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<Error errorText="خطا در بارگذاری محصولات" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const products = productsData?.results?.products || [];
|
||||
const pager = productsData?.results?.pager;
|
||||
|
||||
const getStatusVariant = (status: ProductStatus) => {
|
||||
switch (status) {
|
||||
case ProductStatus.Approved: return 'success';
|
||||
case ProductStatus.Rejected: return 'error';
|
||||
case ProductStatus.Pending: return 'warning';
|
||||
case ProductStatus.Draft: return 'warning';
|
||||
default: return 'warning';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: ProductStatus) => {
|
||||
switch (status) {
|
||||
case ProductStatus.Approved: return 'تایید شده';
|
||||
case ProductStatus.Rejected: return 'رد شده';
|
||||
case ProductStatus.Pending: return 'در انتظار';
|
||||
case ProductStatus.Draft: return 'پیشنویس';
|
||||
default: return status;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
<table className='w-full text-sm'>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={'تصویر'} />
|
||||
<Td text={'عنوان'} />
|
||||
<Td text={'برند'} />
|
||||
<Td text={'دستهبندی'} />
|
||||
<Td text={'وضعیت'} />
|
||||
<Td text={'قیمت'} />
|
||||
<Td text={'موجودی'} />
|
||||
<Td text={'عملیات'} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{products.length === 0 ? (
|
||||
<tr className='tr'>
|
||||
<td colSpan={8} className="text-center py-8 text-gray-500">
|
||||
هیچ محصولی یافت نشد
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
products.map((product: ProductListType) => (
|
||||
<tr key={product._id} className='tr'>
|
||||
<Td text="">
|
||||
<div className="w-12 h-12 rounded-lg overflow-hidden">
|
||||
<img
|
||||
src={product.imagesUrl?.cover || '/placeholder-image.png'}
|
||||
alt={product.title_fa}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">
|
||||
{product.title_fa}
|
||||
</div>
|
||||
<div className="text-gray-500 text-xs">
|
||||
{product.title_en}
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={product.brand_title_fa} />
|
||||
<Td text={product.category_title_fa} />
|
||||
<Td text="">
|
||||
<StatusWithText
|
||||
variant={getStatusVariant(product.status)}
|
||||
text={getStatusText(product.status)}
|
||||
/>
|
||||
</Td>
|
||||
<Td text={`${product.default_variant?.price?.selling_price?.toLocaleString('fa-IR') || 0} تومان`} />
|
||||
<Td text={String(product.default_variant?.stock || 0)} />
|
||||
<Td text="">
|
||||
<div className="flex items-center gap-2">
|
||||
<Eye color='#8C90A3' size={19} />
|
||||
|
||||
<Edit color='#8C90A3' size={16} />
|
||||
|
||||
<TrashWithConfrim
|
||||
onDelete={() => {
|
||||
// TODO: Implement delete product
|
||||
console.log('Delete product:', product._id);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<PaginationUi
|
||||
pager={pager}
|
||||
onPageChange={(page) => {
|
||||
setCurrentPage(page);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export enum ProductMarketStatus {
|
||||
Out_of_stock = "out_of_stock",
|
||||
Marketable = "Marketable",
|
||||
stop_production = "stopProduction",
|
||||
}
|
||||
|
||||
export enum ProductStatus {
|
||||
Draft = "Draft",
|
||||
Pending = "Pending",
|
||||
Approved = "Approved",
|
||||
Rejected = "Rejected",
|
||||
}
|
||||
|
||||
export enum ProductSource {
|
||||
LOCAL = "local",
|
||||
IMPORT = "import",
|
||||
}
|
||||
|
||||
export enum ProductDiscountType {
|
||||
Fixed = "fixed",
|
||||
Percent = "percent",
|
||||
}
|
||||
|
||||
export enum CreateProductStep {
|
||||
Detail = 1,
|
||||
Attribute,
|
||||
Image,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import * as api from "../service/ProductService";
|
||||
import {
|
||||
type CreateProductDetailRequestType,
|
||||
@@ -6,23 +6,27 @@ import {
|
||||
type SaveProductRequestType
|
||||
} from "../types/Types";
|
||||
|
||||
// Hook برای مرحله اول: ایجاد جزئیات محصول
|
||||
export const useCreateProductDetail = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: CreateProductDetailRequestType) => api.createProductDetail(variables),
|
||||
});
|
||||
};
|
||||
|
||||
// Hook برای مرحله دوم: افزودن ویژگیها و توضیحات
|
||||
export const useCreateProductAttribute = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: CreateProductAttributeRequestType) => api.createProductAttribute(variables),
|
||||
});
|
||||
};
|
||||
|
||||
// Hook برای مرحله سوم: ذخیره نهایی محصول
|
||||
export const useSaveProduct = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: SaveProductRequestType) => api.saveProduct(variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetProducts = (page: number = 1, limit: number = 10) => {
|
||||
return useQuery({
|
||||
queryKey: ['products', page, limit],
|
||||
queryFn: () => api.getProducts(page, limit),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,23 +5,28 @@ import {
|
||||
type CreateProductAttributeRequestType,
|
||||
type CreateProductAttributeResponseType,
|
||||
type SaveProductRequestType,
|
||||
type SaveProductResponseType
|
||||
type SaveProductResponseType,
|
||||
type GetProductsResponseType
|
||||
} from "../types/Types";
|
||||
|
||||
// مرحله اول: ایجاد جزئیات محصول
|
||||
export const createProductDetail = async (params: CreateProductDetailRequestType): Promise<CreateProductDetailResponseType> => {
|
||||
const { data } = await axios.post(`/admin/products/creation/detail`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
// مرحله دوم: افزودن ویژگیها و توضیحات
|
||||
export const createProductAttribute = async (params: CreateProductAttributeRequestType): Promise<CreateProductAttributeResponseType> => {
|
||||
const { data } = await axios.post(`/admin/products/creation/attribute`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
// مرحله سوم: ذخیره نهایی محصول
|
||||
export const saveProduct = async (params: SaveProductRequestType): Promise<SaveProductResponseType> => {
|
||||
const { data } = await axios.post(`/admin/products/creation/save`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getProducts = async (page: number = 1, limit: number = 10): Promise<GetProductsResponseType> => {
|
||||
const { data } = await axios.get(`/admin/products/native`, {
|
||||
params: { page, limit }
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// تایپهای مربوط به ایجاد محصول سه مرحلهای
|
||||
import { ProductStatus } from "../enum/ProductEnum";
|
||||
|
||||
// مرحله اول: جزئیات محصول
|
||||
export type CreateProductDetailRequestType = {
|
||||
@@ -104,3 +105,95 @@ export type ProductType = {
|
||||
imagesList?: string[];
|
||||
coverImage?: string;
|
||||
};
|
||||
|
||||
// تایپهای مربوط به لیست محصولات
|
||||
|
||||
export type PagerType = {
|
||||
page: number;
|
||||
limit: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
prevPage: boolean | null;
|
||||
nextPage: string | null;
|
||||
};
|
||||
|
||||
export type ProductPriceType = {
|
||||
order_limit: number;
|
||||
retailPrice: number;
|
||||
selling_price: number;
|
||||
is_specialSale: boolean;
|
||||
discount_percent: number;
|
||||
specialSale_order_limit: number | null;
|
||||
specialSale_quantity: number | null;
|
||||
specialSale_endDate: string | null;
|
||||
};
|
||||
|
||||
export type ProductVariantType = {
|
||||
_id: string;
|
||||
market_status: string;
|
||||
price: ProductPriceType;
|
||||
stock: number;
|
||||
postingTime: number;
|
||||
isFreeShip: boolean;
|
||||
isWholeSale: boolean;
|
||||
shop: {
|
||||
_id: string;
|
||||
};
|
||||
warranty: number;
|
||||
size?: number;
|
||||
meterage?: number;
|
||||
};
|
||||
|
||||
export type ProductImagesType = {
|
||||
cover: string;
|
||||
list: string[];
|
||||
};
|
||||
|
||||
export type ProductListType = {
|
||||
_id: number;
|
||||
title_fa: string;
|
||||
title_en: string;
|
||||
seoTitle: string | null;
|
||||
seoDescription: string | null;
|
||||
description: string;
|
||||
metaDescription: string;
|
||||
shopName: string;
|
||||
category_title_fa: string;
|
||||
category_id: string;
|
||||
brand_title_fa: string;
|
||||
status: ProductStatus;
|
||||
adminComments: string | null;
|
||||
step: number;
|
||||
isFake: boolean;
|
||||
imagesUrl: ProductImagesType;
|
||||
variantCount: number;
|
||||
default_variant: ProductVariantType;
|
||||
popular: boolean;
|
||||
incredible: boolean;
|
||||
voice: string | null;
|
||||
};
|
||||
|
||||
export type BrandListType = {
|
||||
_id: string;
|
||||
status: string;
|
||||
title_en: string;
|
||||
title_fa: string;
|
||||
images: string[];
|
||||
logoUrl: string;
|
||||
};
|
||||
|
||||
export type CategoryListType = {
|
||||
_id: string;
|
||||
title_fa: string;
|
||||
};
|
||||
|
||||
export type GetProductsResponseType = {
|
||||
status: number;
|
||||
success: boolean;
|
||||
results: {
|
||||
pager: PagerType;
|
||||
products: ProductListType[];
|
||||
brands: BrandListType[];
|
||||
categories: CategoryListType[];
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user