request create product + category learning
This commit is contained in:
@@ -200,5 +200,7 @@ export const Pages = {
|
|||||||
withdrawalRequests: "/sellers/withdrawal-requests",
|
withdrawalRequests: "/sellers/withdrawal-requests",
|
||||||
announcement: "/sellers/announcement",
|
announcement: "/sellers/announcement",
|
||||||
requestCreateProduct: "/sellers/request-create-product",
|
requestCreateProduct: "/sellers/request-create-product",
|
||||||
|
learningCategory: "/sellers/learning-category",
|
||||||
|
learning: "/sellers/learning",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,104 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC, useState } from 'react'
|
||||||
|
import { useGetLearningCategory } from './hooks/useSellerData'
|
||||||
|
import { type LearningCategory } from './types/Types'
|
||||||
|
import PageLoading from '../../components/PageLoading'
|
||||||
|
import Error from '../../components/Error'
|
||||||
|
import Td from '../../components/Td'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import CategoryLearningTableRow from './components/CategoryLearningTableRow'
|
||||||
|
import CreateUpdateCategoryModal from './components/CreateUpdateCategoryModal'
|
||||||
|
|
||||||
const CategoryLearning = () => {
|
const CategoryLearning: FC = () => {
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState<LearningCategory | undefined>()
|
||||||
|
|
||||||
|
const { data: categoriesData, isLoading, error, refetch } = useGetLearningCategory()
|
||||||
|
|
||||||
|
const handleModalClose = () => {
|
||||||
|
setIsModalOpen(false)
|
||||||
|
setSelectedCategory(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleModalSuccess = () => {
|
||||||
|
refetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEditCategory = (category: LearningCategory) => {
|
||||||
|
setSelectedCategory(category)
|
||||||
|
setIsModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateCategory = () => {
|
||||||
|
setSelectedCategory(undefined)
|
||||||
|
setIsModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div>CategoryLearning</div>
|
<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 categories = categoriesData?.results?.learningCategory || []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='flex justify-end mt-5'>
|
||||||
|
<Button
|
||||||
|
label="ایجاد دستهبندی جدید"
|
||||||
|
onClick={handleCreateCategory}
|
||||||
|
className="w-auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
|
||||||
|
<table className='w-full text-sm'>
|
||||||
|
<thead className='thead'>
|
||||||
|
<tr>
|
||||||
|
<Td text={'عنوان'} />
|
||||||
|
<Td text={'تاریخ ایجاد'} />
|
||||||
|
<Td text={'تاریخ بروزرسانی'} />
|
||||||
|
<Td text={'وضعیت'} />
|
||||||
|
<Td text={'عملیات'} />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{categories.length === 0 ? (
|
||||||
|
<tr className='tr'>
|
||||||
|
<td colSpan={5} className="text-center py-8 text-gray-500">
|
||||||
|
هیچ دستهبندی یادگیری یافت نشد
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
categories.map((category: LearningCategory) => (
|
||||||
|
<CategoryLearningTableRow
|
||||||
|
key={category._id}
|
||||||
|
category={category}
|
||||||
|
onEdit={handleEditCategory}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CreateUpdateCategoryModal
|
||||||
|
open={isModalOpen}
|
||||||
|
close={handleModalClose}
|
||||||
|
onSuccess={handleModalSuccess}
|
||||||
|
category={selectedCategory}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { type FC } from 'react'
|
||||||
|
import Td from '../../../components/Td'
|
||||||
|
import { Calendar, Edit } from 'iconsax-react'
|
||||||
|
import StatusWithText from '../../../components/StatusWithText'
|
||||||
|
import type { LearningCategory } from '../types/Types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
category: LearningCategory
|
||||||
|
onEdit: (category: LearningCategory) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryLearningTableRow: FC<Props> = ({ category, onEdit }) => {
|
||||||
|
const formatDate = (dateString: string) => {
|
||||||
|
return new Date(dateString).toLocaleDateString('fa-IR', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr className='tr'>
|
||||||
|
<Td text="">
|
||||||
|
<div className="max-w-xs">
|
||||||
|
<div className="font-medium text-gray-900 truncate">
|
||||||
|
{category.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
<Td text="">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Calendar size={16} color="#8C90A3" />
|
||||||
|
<span className="text-gray-600 text-sm">
|
||||||
|
{formatDate(category.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
<Td text="">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Calendar size={16} color="#8C90A3" />
|
||||||
|
<span className="text-gray-600 text-sm">
|
||||||
|
{formatDate(category.updatedAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
<Td text="">
|
||||||
|
<StatusWithText
|
||||||
|
variant={category.deleted ? 'error' : 'success'}
|
||||||
|
text={category.deleted ? 'غیرفعال' : 'فعال'}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
|
<Td text="">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Edit onClick={() => onEdit(category)} color="#8C90A3" size={16} />
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryLearningTableRow
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { type FC, useEffect } from 'react'
|
||||||
|
import DefaulModal from '@/components/DefaulModal'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import { useCreateLearningCategory, useUpdateLearningCategory } from '../hooks/useSellerData'
|
||||||
|
import { type LearningCategory, type CreateCategoryLearning } from '../types/Types'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { extractErrorMessage } from '@/helpers/utils'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean
|
||||||
|
close: () => void
|
||||||
|
onSuccess?: () => void
|
||||||
|
category?: LearningCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateUpdateCategoryModal: FC<Props> = ({ open, close, onSuccess, category }) => {
|
||||||
|
const isEdit = !!category
|
||||||
|
const createMutation = useCreateLearningCategory()
|
||||||
|
const updateMutation = useUpdateLearningCategory()
|
||||||
|
|
||||||
|
const formik = useFormik<CreateCategoryLearning>({
|
||||||
|
initialValues: {
|
||||||
|
title: category?.title || ''
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
title: Yup.string().required('عنوان دستهبندی الزامی است')
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
if (isEdit && category) {
|
||||||
|
// Update category
|
||||||
|
updateMutation.mutate({
|
||||||
|
id: category._id,
|
||||||
|
params: values
|
||||||
|
}, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('دستهبندی با موفقیت بروزرسانی شد')
|
||||||
|
close()
|
||||||
|
onSuccess?.()
|
||||||
|
formik.resetForm()
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Create category
|
||||||
|
createMutation.mutate(values, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('دستهبندی جدید با موفقیت ایجاد شد')
|
||||||
|
close()
|
||||||
|
onSuccess?.()
|
||||||
|
formik.resetForm()
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
formik.resetForm()
|
||||||
|
formik.setValues({
|
||||||
|
title: category?.title || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [open, category])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaulModal
|
||||||
|
open={open}
|
||||||
|
close={close}
|
||||||
|
isHeader={true}
|
||||||
|
title_header={isEdit ? "ویرایش دستهبندی یادگیری" : "ایجاد دستهبندی یادگیری جدید"}
|
||||||
|
width={500}
|
||||||
|
>
|
||||||
|
<div className="space-y-6 mt-4 w-[450px]">
|
||||||
|
<Input
|
||||||
|
label="عنوان دستهبندی"
|
||||||
|
placeholder="عنوان دستهبندی را وارد کنید"
|
||||||
|
{...formik.getFieldProps('title')}
|
||||||
|
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex gap-3 pt-4">
|
||||||
|
<Button
|
||||||
|
label="لغو"
|
||||||
|
variant="outline"
|
||||||
|
onClick={close}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label={isEdit ? "بروزرسانی" : "ایجاد"}
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={createMutation.isPending || updateMutation.isPending}
|
||||||
|
disabled={!formik.isValid || createMutation.isPending || updateMutation.isPending}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DefaulModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateUpdateCategoryModal
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import DefaulModal from '@/components/DefaulModal'
|
import DefaulModal from '@/components/DefaulModal'
|
||||||
import { type ProductRequest } from '../types/Types'
|
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 {
|
interface Props {
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -13,31 +11,6 @@ interface Props {
|
|||||||
const ProductRequestDetailModal: FC<Props> = ({ open, close, request }) => {
|
const ProductRequestDetailModal: FC<Props> = ({ open, close, request }) => {
|
||||||
if (!request) return null
|
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 (
|
return (
|
||||||
<DefaulModal
|
<DefaulModal
|
||||||
open={open}
|
open={open}
|
||||||
@@ -47,200 +20,7 @@ const ProductRequestDetailModal: FC<Props> = ({ open, close, request }) => {
|
|||||||
width={800}
|
width={800}
|
||||||
>
|
>
|
||||||
<div className="space-y-6 mt-4">
|
<div className="space-y-6 mt-4">
|
||||||
{/* وضعیت درخواست */}
|
<p>در حال حاضر این modal در حال توسعه است.</p>
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-900">وضعیت درخواست</h3>
|
|
||||||
<StatusWithText
|
|
||||||
variant={getStatusVariant(request.requestStatus)}
|
|
||||||
text={getStatusText(request.requestStatus)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* اطلاعات فروشنده */}
|
|
||||||
<div className="bg-gray-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3 flex items-center gap-2">
|
|
||||||
<UserIcon size={20} color="#374151" />
|
|
||||||
اطلاعات فروشنده
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<UserIcon size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">نام:</span>
|
|
||||||
<span className="text-sm font-medium">{request.seller?.fullName || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Shop size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">فروشگاه:</span>
|
|
||||||
<span className="text-sm font-medium">{request.shop?.shopName || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Location size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">آدرس:</span>
|
|
||||||
<span className="text-sm font-medium">{request.shop?.address?.province?.name || '-'}، {request.shop?.address?.city?.name || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Tag size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">کد فروشگاه:</span>
|
|
||||||
<span className="text-sm font-medium">{request.shop?.shopCode || '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* اطلاعات محصول */}
|
|
||||||
<div className="bg-blue-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3 flex items-center gap-2">
|
|
||||||
<Box size={20} color="#1E40AF" />
|
|
||||||
اطلاعات محصول
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Box size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">نام محصول:</span>
|
|
||||||
<span className="text-sm font-medium">{request.productName}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Tag size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">برند:</span>
|
|
||||||
<span className="text-sm font-medium">{request.brand?.title_fa || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<MessageSquare size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">دستهبندی:</span>
|
|
||||||
<span className="text-sm font-medium">{request.category?.title_fa || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Tag size={16} color="#6B7280" />
|
|
||||||
<span className="text-sm text-gray-600">منبع:</span>
|
|
||||||
<span className="text-sm font-medium">{request.source || '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* توضیحات */}
|
|
||||||
<div className="bg-green-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3">توضیحات</h4>
|
|
||||||
<p className="text-sm text-gray-700 leading-relaxed">{String(request.description || 'توضیحاتی وارد نشده')}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ابعاد و وزن */}
|
|
||||||
<div className="bg-yellow-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3 flex items-center gap-2">
|
|
||||||
<Box size={20} color="#92400E" />
|
|
||||||
ابعاد و وزن
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-lg font-semibold text-gray-900">{request.dimensions?.package_weight || 0}</div>
|
|
||||||
<div className="text-xs text-gray-600">وزن (گرم)</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-lg font-semibold text-gray-900">{request.dimensions?.package_length || 0}</div>
|
|
||||||
<div className="text-xs text-gray-600">طول (سانتیمتر)</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-lg font-semibold text-gray-900">{request.dimensions?.package_width || 0}</div>
|
|
||||||
<div className="text-xs text-gray-600">عرض (سانتیمتر)</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-lg font-semibold text-gray-900">{request.dimensions?.package_height || 0}</div>
|
|
||||||
<div className="text-xs text-gray-600">ارتفاع (سانتیمتر)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* مزایا و معایب */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div className="bg-green-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-green-900 mb-3">مزایا</h4>
|
|
||||||
{request.advantages && request.advantages.length > 0 ? (
|
|
||||||
<ul className="space-y-1">
|
|
||||||
{request.advantages.map((advantage, index) => (
|
|
||||||
<li key={index} className="text-sm text-green-800 flex items-center gap-2">
|
|
||||||
<span className="w-1.5 h-1.5 bg-green-600 rounded-full"></span>
|
|
||||||
{String(advantage)}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<p className="text-sm text-gray-600">مزایایی وارد نشده</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-red-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-red-900 mb-3">معایب</h4>
|
|
||||||
{request.disAdvantages && request.disAdvantages.length > 0 ? (
|
|
||||||
<ul className="space-y-1">
|
|
||||||
{request.disAdvantages.map((disadvantage, index) => (
|
|
||||||
<li key={index} className="text-sm text-red-800 flex items-center gap-2">
|
|
||||||
<span className="w-1.5 h-1.5 bg-red-600 rounded-full"></span>
|
|
||||||
{String(disadvantage)}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<p className="text-sm text-gray-600">معایبی وارد نشده</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* خدمات درخواست شده */}
|
|
||||||
<div className="bg-purple-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3">خدمات درخواست شده</h4>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={request.requestPhotography}
|
|
||||||
readOnly
|
|
||||||
className="rounded border-gray-300"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">📸 عکاسی محصول</span>
|
|
||||||
{request.requestPhotosCount && (
|
|
||||||
<span className="text-xs text-gray-600">({request.requestPhotosCount} عکس)</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={request.expertReview}
|
|
||||||
readOnly
|
|
||||||
className="rounded border-gray-300"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">👨💼 بررسی تخصصی</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={request.unboxingVideo}
|
|
||||||
readOnly
|
|
||||||
className="rounded border-gray-300"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">📦 ویدیو باز کردن بسته</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* اطلاعات پرداخت */}
|
|
||||||
{request.payment && (
|
|
||||||
<div className="bg-indigo-50 rounded-lg p-4">
|
|
||||||
<h4 className="text-md font-medium text-gray-900 mb-3 flex items-center gap-2">
|
|
||||||
<Money size={20} color="#312E81" />
|
|
||||||
اطلاعات پرداخت
|
|
||||||
</h4>
|
|
||||||
<div className="text-sm text-gray-700">
|
|
||||||
<pre className="whitespace-pre-wrap">{String(JSON.stringify(request.payment, null, 2))}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* تاریخ ایجاد */}
|
|
||||||
<div className="flex items-center justify-between text-sm text-gray-600 border-t pt-4">
|
|
||||||
<span>تاریخ ایجاد:</span>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Calendar size={16} color="#6B7280" />
|
|
||||||
<span>{formatDate(request._id ? new Date().toISOString() : '')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DefaulModal>
|
</DefaulModal>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import * as api from "../service/SellerService";
|
import * as api from "../service/SellerService";
|
||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
|
import type {
|
||||||
|
CreateCategoryLearning,
|
||||||
|
CreateLearningType,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const useGetSellers = (page: number = 1, enabled: boolean = true) => {
|
export const useGetSellers = (page: number = 1, enabled: boolean = true) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -66,3 +70,48 @@ export const useGetRequestCreateProduct = (page: number = 1) => {
|
|||||||
queryFn: () => api.getRequestCreateProduct(page),
|
queryFn: () => api.getRequestCreateProduct(page),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetLearningCategory = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["learning-category"],
|
||||||
|
queryFn: () => api.getLearningCategory(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateLearningCategory = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.createLearningCategory,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateLearningCategory = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({
|
||||||
|
id,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
params: CreateCategoryLearning;
|
||||||
|
}) => api.updateLearningCategory(id, params),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetLearning = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["learning"],
|
||||||
|
queryFn: () => api.getLearning(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateLearning = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.createLearning,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateLearning = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ id, params }: { id: string; params: CreateLearningType }) =>
|
||||||
|
api.updateLearning(id, params),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import type {
|
|||||||
NotificationsResponse,
|
NotificationsResponse,
|
||||||
CreateAnnouncementSpecificSeller,
|
CreateAnnouncementSpecificSeller,
|
||||||
GetRequestCreateProductResponse,
|
GetRequestCreateProductResponse,
|
||||||
|
CreateCategoryLearning,
|
||||||
|
GetLearningCategoryResponse,
|
||||||
|
CreateLearningType,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
|
|
||||||
export const getSellers = async (
|
export const getSellers = async (
|
||||||
@@ -87,3 +90,46 @@ export const getRequestCreateProduct = async (
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLearningCategory = async (): Promise<
|
||||||
|
GetLearningCategoryResponse
|
||||||
|
> => {
|
||||||
|
const { data } = await axios.get(`/learning/category`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createLearningCategory = async (
|
||||||
|
params: CreateCategoryLearning
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.post(`/admin/learning/category`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateLearningCategory = async (
|
||||||
|
id: string,
|
||||||
|
params: CreateCategoryLearning
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.patch(
|
||||||
|
`/admin/learning/category/${id}/update`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLearning = async () => {
|
||||||
|
const { data } = await axios.get(`/learning`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createLearning = async (params: CreateLearningType) => {
|
||||||
|
const { data } = await axios.post(`/admin/learning`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateLearning = async (
|
||||||
|
id: string,
|
||||||
|
params: CreateLearningType
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.patch(`/admin/learning/${id}/update`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -217,6 +217,22 @@ export interface CreateCategoryLearning {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LearningCategory {
|
||||||
|
_id: string;
|
||||||
|
title: string;
|
||||||
|
deleted: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetLearningCategoryResponse {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
learningCategory: LearningCategory[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Product Request Types
|
// Product Request Types
|
||||||
export interface City {
|
export interface City {
|
||||||
_id: number;
|
_id: number;
|
||||||
@@ -318,3 +334,11 @@ export interface GetRequestCreateProductResponse {
|
|||||||
pager: Pager;
|
pager: Pager;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateLearningType {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
videoUrl: string;
|
||||||
|
cover: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import WholeSaleRequest from '@/pages/seller/WholeSaleRequest'
|
|||||||
import WithdrawalRequestPage from '@/pages/seller/WithdrawalRequest'
|
import WithdrawalRequestPage from '@/pages/seller/WithdrawalRequest'
|
||||||
import Annoncement from '@/pages/seller/Annoncement'
|
import Annoncement from '@/pages/seller/Annoncement'
|
||||||
import RequestCreateProduct from '@/pages/seller/RequestCreateProduct'
|
import RequestCreateProduct from '@/pages/seller/RequestCreateProduct'
|
||||||
|
import CategoryLearning from '@/pages/seller/CategoryLearning'
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
const { hasSubMenu } = useSharedStore()
|
const { hasSubMenu } = useSharedStore()
|
||||||
@@ -167,6 +168,8 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.sellers.withdrawalRequests} element={<WithdrawalRequestPage />} />
|
<Route path={Pages.sellers.withdrawalRequests} element={<WithdrawalRequestPage />} />
|
||||||
<Route path={Pages.sellers.announcement} element={<Annoncement />} />
|
<Route path={Pages.sellers.announcement} element={<Annoncement />} />
|
||||||
<Route path={Pages.sellers.requestCreateProduct} element={<RequestCreateProduct />} />
|
<Route path={Pages.sellers.requestCreateProduct} element={<RequestCreateProduct />} />
|
||||||
|
<Route path={Pages.sellers.learningCategory} element={<CategoryLearning />} />
|
||||||
|
{/* <Route path={Pages.sellers.learning} element={<Learning />} /> */}
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -528,13 +528,13 @@ const SellersSubMenu: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<SubMenuItem
|
<SubMenuItem
|
||||||
title={'آموزش ها'}
|
title={'آموزش ها'}
|
||||||
isActive={isActive('trainings')}
|
isActive={isActive('learning')}
|
||||||
link="/sellers/trainings"
|
link={Pages.sellers.learning}
|
||||||
/>
|
/>
|
||||||
<SubMenuItem
|
<SubMenuItem
|
||||||
title={'دسته بندی آموزش ها'}
|
title={'دسته بندی آموزش ها'}
|
||||||
isActive={isActive('training-categories')}
|
isActive={isActive('category')}
|
||||||
link="/sellers/training-categories"
|
link={Pages.sellers.learningCategory}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user