up
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-24 18:16:47 +03:30
parent 7d4e2865bb
commit 3676688849
8 changed files with 122 additions and 84 deletions
+1 -1
View File
@@ -8,8 +8,8 @@ export const Paths = {
create: '/product/create',
update: '/product/update/',
category: {
create: '/product/category/create',
list: '/product/category/list',
children: '/product/category/list/',
update: '/product/category/update/',
},
attribute: {
+6 -2
View File
@@ -15,6 +15,8 @@ import { toast } from 'react-toastify';
import type { ErrorType } from '@/helpers/types';
import { extractErrorMessage } from '@/config/func';
import { useNavigate } from 'react-router-dom';
import { Paths } from '@/config/Paths'
import BackButton from '@/components/BackButton'
const CreateProduct: FC = () => {
@@ -71,8 +73,10 @@ const CreateProduct: FC = () => {
return (
<div className='mt-5'>
<div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>محصول جدید</h1>
<div className='flex items-center gap-4'>
<BackButton to={Paths.product.list} />
<h1 className='text-lg font-light'>محصول جدید</h1>
</div>
<Button
className='w-fit px-6'
onClick={() => formik.handleSubmit()}
+4
View File
@@ -152,6 +152,10 @@ const ProductList: FC = () => {
)
}
},
{
key: 'order',
title: 'ترتیب',
},
{
key: 'actions',
title: '',
+55 -49
View File
@@ -3,39 +3,45 @@ import { useFormik } from 'formik'
import { useState, type FC } from 'react'
import type { CreateCategoryType } from '../types/Types'
import * as Yup from 'yup'
import { useCreateCategory, useGetCategory } from '../hooks/useProductData'
import Select from '@/components/Select'
import { useCreateCategory } from '../hooks/useProductData'
import Button from '@/components/Button'
import { AddSquare } from 'iconsax-react'
import { AddSquare, TickCircle } from 'iconsax-react'
import UploadBox from '@/components/UploadBox'
import SwitchComponent from '@/components/Switch'
import { useSingleUpload } from '@/pages/uploader/hooks/useUploader'
import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/config/func'
import { useNavigate } from 'react-router-dom'
import { Paths } from '@/config/Paths'
import BackButton from '@/components/BackButton'
import DefaulModal from '@/components/DefaulModal'
const ProductCategory: FC = () => {
type Props = {
parentId?: string
refetch: () => void
}
const navigate = useNavigate()
const { data } = useGetCategory()
const CreateCategory: FC<Props> = ({ parentId, refetch }) => {
const { mutate, isPending } = useCreateCategory()
const { mutate: upload, isPending: isUploading } = useSingleUpload()
const [showModal, setShowModal] = useState(false)
const [file, setFile] = useState<File>()
const handleClose = () => {
setShowModal(false)
setFile(undefined)
formik.resetForm()
}
const handleSave = (values: CreateCategoryType) => {
const payload: CreateCategoryType = {
...values,
parentId: values.parentId || undefined,
parentId: parentId || undefined,
}
mutate(payload, {
onSuccess: () => {
toast.success('با موفقیت ذخیره شد')
navigate(Paths.product.category.list)
refetch()
handleClose()
},
onError: (error) => {
toast.error(extractErrorMessage(error))
@@ -69,55 +75,39 @@ const ProductCategory: FC = () => {
})
return (
<div className='mt-5'>
<div className='flex justify-between items-center'>
<div className='flex items-center gap-4'>
<BackButton to={Paths.product.category.list} />
<h1 className='text-lg font-light'>دسته جدید</h1>
<div>
<Button
className='w-fit px-6'
onClick={() => setShowModal(true)}
>
<div className='flex gap-1.5'>
<AddSquare size={18} color='black' />
<div className='text-[13px] font-light'>دستهبندی جدید</div>
</div>
<Button
className='w-fit px-6'
isLoading={isPending || isUploading}
onClick={() => formik.handleSubmit()}
>
<div className='flex gap-1.5'>
<AddSquare size={18} color='black' />
<div className='text-[13px] font-light'>ساخت دسته</div>
</div>
</Button>
</div>
<div className='mt-8 bg-white p-6 rounded-3xl '>
</Button>
<div>
<DefaulModal
open={showModal}
close={handleClose}
isHeader
title_header='دسته جدید'
>
<div className='mt-5'>
<UploadBox
label='تصویر دسته (اختیاری)'
onChange={(files) => setFile(files[0])}
/>
</div>
<div className='rowTwoInput mt-6'>
<div className='mt-5'>
<Input
label='عنوان'
{...formik.getFieldProps('title')}
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
/>
<Select
label='انتخاب والد درسته'
placeholder='اختیاری'
items={data?.data?.map((item) => {
return {
label: item.title,
value: item.id + ''
}
}) || []}
onChange={formik.handleChange}
name='parentId'
value={formik.values.parentId || ''}
/>
</div>
<div className='mt-6'>
<div className='mt-5'>
<Input
label='ترتیب اولویت'
{...formik.getFieldProps('order')}
@@ -125,15 +115,31 @@ const ProductCategory: FC = () => {
/>
</div>
<div className='mt-6'>
<div className='mt-5'>
<SwitchComponent
active={formik.values.isActive}
onChange={(value) => formik.setFieldValue('isActive', value)}
/>
</div>
</div>
<div className='mt-6 flex justify-end'>
<Button
className='w-fit px-6'
isLoading={isPending || isUploading}
onClick={() => formik.handleSubmit()}
>
<div className='flex gap-2 items-center'>
<TickCircle
size={18}
color='black'
/>
<div>ذخیره</div>
</div>
</Button>
</div>
</DefaulModal>
</div>
)
}
export default ProductCategory
export default CreateCategory
+50 -28
View File
@@ -1,24 +1,34 @@
import { type FC, useMemo } from 'react'
import PresignedImage from '@/components/PresignedImage'
import { useDeleteCategory, useGetCategory } from '../hooks/useProductData'
import { useDeleteCategory, useGetCategory, useGetCategoryDetail } from '../hooks/useProductData'
import Table from '@/components/Table'
import type { ColumnType, RowDataType } from '@/components/types/TableTypes'
import type { CategoryType } from '../types/Types'
import Button from '@/components/Button'
import { AddSquare, Edit } from 'iconsax-react'
import { ArrowLeft2, Edit } from 'iconsax-react'
import TrashWithConfrim from '@/components/TrashWithConfrim'
import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/config/func'
import { Link } from 'react-router-dom'
import { Link, useParams } from 'react-router-dom'
import { Paths } from '@/config/Paths'
import BackButton from '@/components/BackButton'
import CreateCategory from './Create'
const CategoryList: FC = () => {
const { parentId } = useParams<{ parentId?: string }>()
const { data, refetch } = useGetCategory()
const { data, refetch } = useGetCategory({
limit: 1000,
parentId: parentId ?? 'null',
})
const { data: parentDetail } = useGetCategoryDetail(parentId ?? '')
const { mutate, isPending } = useDeleteCategory()
const categories = (data?.data ?? []) as (CategoryType & RowDataType)[]
const parentCategory = parentDetail?.data
const backTo = parentCategory?.parent?.id
? Paths.product.category.children + parentCategory.parent.id
: Paths.product.category.list
const handleDelete = (id: string) => {
mutate(id, {
onSuccess: () => {
@@ -39,19 +49,34 @@ const CategoryList: FC = () => {
{
title: 'عنوان',
key: 'title',
render: (item) => {
return (
<Link
to={Paths.product.category.children + item.id}
className='inline-flex items-center gap-1.5 text-sm text-black hover:opacity-70'
>
<span>{item.title}</span>
<ArrowLeft2 size={16} color='#8C90A3' />
</Link>
)
}
},
{
title: 'والد',
key: 'parent',
render: (item) => item.parent?.title || '-',
},
{
title: 'تعداد زیر دسته',
title: 'زیر دسته‌ها',
key: 'children',
render: (item) => <div>{item.children?.length || 0}</div>
render: (item) => {
if (!item.children?.length) return '0'
return (
<div className='flex flex-col gap-0.5'>
{item.children.map((child) => (
<span key={child.id}>{child.title}</span>
))}
</div>
)
}
},
{
title: 'اولویت',
title: 'ترتیب',
key: 'order',
render: (item) => item.order ?? '-',
},
@@ -75,33 +100,30 @@ const CategoryList: FC = () => {
}
], [isPending])
const title = parentCategory
? `زیر دسته‌های «${parentCategory.title}»`
: 'دسته بندی محصولات'
return (
<div className='mt-5'>
<div className='flex justify-between items-center'>
<div className='flex items-center gap-4'>
<BackButton to={Paths.product.list} />
<h1 className='text-lg font-light'>دسته بندی محصولات</h1>
<BackButton to={parentId ? backTo : Paths.product.list} />
<h1 className='text-lg font-light'>{title}</h1>
</div>
<Link to={Paths.product.category.create}>
<Button className='w-fit px-6'>
<div className='flex gap-1.5'>
<AddSquare size={18} color='black' />
<div className='text-[13px] font-light'>دستهبندی جدید</div>
</div>
</Button>
</Link>
<CreateCategory parentId={parentId} refetch={refetch} />
</div>
<div className='mt-8'>
<Table<CategoryType & RowDataType>
columns={columns}
data={categories}
/>
<Table<CategoryType & RowDataType>
columns={columns}
data={categories}
/>
</div>
</div>
)
}
export default CategoryList
export default CategoryList
+3 -2
View File
@@ -16,10 +16,11 @@ export const useGetCategory = (params?: api.GetCategoryParams) => {
const page = params?.page ?? 1;
const limit = params?.limit;
const search = params?.search;
const parentId = params?.parentId;
return useQuery({
queryKey: ["category", page, limit, search],
queryFn: () => api.getCategory({ page, limit, search }),
queryKey: ["category", page, limit, search, parentId],
queryFn: () => api.getCategory({ page, limit, search, parentId }),
});
};
@@ -5,6 +5,7 @@ export type GetCategoryParams = {
page?: number;
limit?: number;
search?: string;
parentId?: string;
};
export const getCategory = async (params?: GetCategoryParams) => {
@@ -13,6 +14,7 @@ export const getCategory = async (params?: GetCategoryParams) => {
page: params?.page,
limit: params?.limit,
search: params?.search || undefined,
parentId: params?.parentId || undefined,
},
});
return data;
+1 -2
View File
@@ -28,7 +28,6 @@ import LearningList from "@/pages/learning/List";
import LearningCreate from "@/pages/learning/Create";
import LearningCategory from "@/pages/learning/Category";
import CategoryList from "@/pages/product/category/List";
import CreateCategory from "@/pages/product/category/Create";
import UpdateCategory from "@/pages/product/category/Update";
import UpdateProduct from "@/pages/product/Update";
import CreateAttribute from "@/pages/product/attribute/Create";
@@ -108,7 +107,7 @@ const MainRouter: FC = () => {
<Route path={Paths.product.attribute.update + ":id"} element={withPermission(Permissions.MANAGE_PRODUCTS, <UpdateAttribute />)} />
<Route path={Paths.product.attributeValue.list + ":id"} element={withPermission(Permissions.MANAGE_PRODUCTS, <AttributeValues />)} />
<Route path={Paths.product.category.list} element={withPermission(Permissions.MANAGE_PRODUCTS, <CategoryList />)} />
<Route path={Paths.product.category.create} element={withPermission(Permissions.MANAGE_PRODUCTS, <CreateCategory />)} />
<Route path={Paths.product.category.children + ":parentId"} element={withPermission(Permissions.MANAGE_PRODUCTS, <CategoryList />)} />
<Route path={Paths.product.category.update + ":id"} element={withPermission(Permissions.MANAGE_PRODUCTS, <UpdateCategory />)} />
<Route path={Paths.formBuilder.list + ":id/:type"} element={withPermission(Permissions.MANAGE_PRODUCTS, <FormBuilderList />)} />