select category and subcategory
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
VITE_TOKEN_NAME = 'dkala_a_t'
|
VITE_TOKEN_NAME = 'dkala_a_t'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'dkala-a-rt'
|
VITE_REFRESH_TOKEN_NAME = 'dkala-a-rt'
|
||||||
|
|
||||||
VITE_BASE_URL = 'https://dkala-api.danakcorp.com'
|
# VITE_BASE_URL = 'https://dkala-api.danakcorp.com'
|
||||||
# VITE_BASE_URL = 'http://10.191.241.88:4000'
|
VITE_BASE_URL = 'http://10.191.241.88:4000'
|
||||||
|
|
||||||
VITE_SOCKET_URL = 'https://dkala-api.danakcorp.com'
|
VITE_SOCKET_URL = 'https://dkala-api.danakcorp.com'
|
||||||
@@ -176,6 +176,7 @@ const CategoryProduct: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const columns = CategoryTableColumns({
|
const columns = CategoryTableColumns({
|
||||||
|
categories,
|
||||||
onStatusChange: handleStatusChange,
|
onStatusChange: handleStatusChange,
|
||||||
onEdit: handleEdit,
|
onEdit: handleEdit,
|
||||||
onDelete: handleDelete,
|
onDelete: handleDelete,
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ const CreateProduct: FC = () => {
|
|||||||
const { mutate: createProduct, isPending } = useCreateProduct()
|
const { mutate: createProduct, isPending } = useCreateProduct()
|
||||||
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
|
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
|
||||||
|
|
||||||
const categories = data?.data?.map(category => ({
|
const categories = data?.data ?? []
|
||||||
label: category.title,
|
|
||||||
value: category.id
|
|
||||||
})) || []
|
|
||||||
|
|
||||||
const formik = useFormik<CreateProductType>({
|
const formik = useFormik<CreateProductType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ const UpdateProduct: FC = () => {
|
|||||||
const { mutate: updateProduct, isPending } = useUpdateProduct()
|
const { mutate: updateProduct, isPending } = useUpdateProduct()
|
||||||
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
|
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const categories = categoriesData?.data?.map(category => ({
|
const categories = categoriesData?.data ?? []
|
||||||
label: category.title,
|
|
||||||
value: category.id
|
|
||||||
})) || []
|
|
||||||
|
|
||||||
const product = productData?.data
|
const product = productData?.data
|
||||||
|
|
||||||
@@ -75,11 +72,11 @@ const UpdateProduct: FC = () => {
|
|||||||
const variants =
|
const variants =
|
||||||
values.variants.length > 0
|
values.variants.length > 0
|
||||||
? values.variants.map((v) => ({
|
? values.variants.map((v) => ({
|
||||||
...(v.id && { id: v.id }),
|
...(v.id && { id: v.id }),
|
||||||
value: v.value ?? '',
|
value: v.value ?? '',
|
||||||
price: v.price ?? 0,
|
price: v.price ?? 0,
|
||||||
...(v.stock !== undefined && v.stock !== null && { stock: v.stock })
|
...(v.stock !== undefined && v.stock !== null && { stock: v.stock })
|
||||||
}))
|
}))
|
||||||
: [{ value: '', price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }]
|
: [{ value: '', price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }]
|
||||||
const productData: CreateProductType = {
|
const productData: CreateProductType = {
|
||||||
...values,
|
...values,
|
||||||
@@ -192,9 +189,9 @@ const UpdateProduct: FC = () => {
|
|||||||
formik={formik}
|
formik={formik}
|
||||||
categories={categories}
|
categories={categories}
|
||||||
initialHasVariants={
|
initialHasVariants={
|
||||||
(product?.variants?.length ?? 0) > 1 ||
|
(product?.variants?.length ?? 0) > 1 ||
|
||||||
((product?.variants?.length === 1) && (product.variants[0].value ?? '') !== '')
|
((product?.variants?.length === 1) && (product.variants[0].value ?? '') !== '')
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { type FC, useState, useEffect } from 'react'
|
import { type FC, useState, useEffect, useMemo } from 'react'
|
||||||
import type { FormikProps } from 'formik'
|
import type { FormikProps } from 'formik'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import Select from '@/components/Select'
|
import Select from '@/components/Select'
|
||||||
import Textarea from '@/components/Textarea'
|
import Textarea from '@/components/Textarea'
|
||||||
import Button from '@/components/Button'
|
import Button from '@/components/Button'
|
||||||
import RadioGroup from '@/components/RadioGroup'
|
import RadioGroup from '@/components/RadioGroup'
|
||||||
import type { CreateProductType, ProductVariant } from '../types/Types'
|
import type { CreateProductType, ProductVariant, Category } from '../types/Types'
|
||||||
import { Add, Trash } from 'iconsax-react'
|
import { Add, Trash } from 'iconsax-react'
|
||||||
|
|
||||||
type BasicInfoSectionProps = {
|
type BasicInfoSectionProps = {
|
||||||
formik: FormikProps<CreateProductType>
|
formik: FormikProps<CreateProductType>
|
||||||
categories: Array<{ label: string; value: string }>
|
categories: Category[]
|
||||||
/** در صفحه ویرایش، اگر کالا تنوع دارد مقدار true پاس داده شود */
|
/** در صفحه ویرایش، اگر کالا تنوع دارد مقدار true پاس داده شود */
|
||||||
initialHasVariants?: boolean
|
initialHasVariants?: boolean
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,24 @@ const newVariant = (): ProductVariant => ({ value: '', price: 0 })
|
|||||||
|
|
||||||
const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories, initialHasVariants }) => {
|
const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories, initialHasVariants }) => {
|
||||||
const [hasVariants, setHasVariants] = useState<boolean>(initialHasVariants ?? false)
|
const [hasVariants, setHasVariants] = useState<boolean>(initialHasVariants ?? false)
|
||||||
|
/** دسته اصلی انتخابشده برای نمایش زیردستهها */
|
||||||
|
const [parentCategoryId, setParentCategoryId] = useState<string>('')
|
||||||
|
|
||||||
|
const mainCategories = useMemo(
|
||||||
|
() => categories.filter((c) => !c.parent).map((c) => ({ label: c.title, value: c.id })),
|
||||||
|
[categories]
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedParent = useMemo(
|
||||||
|
() => categories.find((c) => c.id === parentCategoryId),
|
||||||
|
[categories, parentCategoryId]
|
||||||
|
)
|
||||||
|
|
||||||
|
const subCategories = useMemo(
|
||||||
|
() =>
|
||||||
|
(selectedParent?.children || []).map((c) => ({ label: c.title, value: c.id })),
|
||||||
|
[selectedParent]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialHasVariants !== undefined) {
|
if (initialHasVariants !== undefined) {
|
||||||
@@ -26,6 +44,19 @@ const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories, initi
|
|||||||
}
|
}
|
||||||
}, [initialHasVariants])
|
}, [initialHasVariants])
|
||||||
|
|
||||||
|
/** همگامسازی parentCategoryId با categoryId وقتی فرم مقداردهی میشود (مثلاً در ویرایش) */
|
||||||
|
useEffect(() => {
|
||||||
|
const catId = formik.values.categoryId
|
||||||
|
if (!catId) return
|
||||||
|
const cat = categories.find((c) => c.id === catId)
|
||||||
|
const child = categories.flatMap((c) => c.children || []).find((ch) => ch.id === catId)
|
||||||
|
if (child) {
|
||||||
|
setParentCategoryId(child.parent || '')
|
||||||
|
} else if (cat && !cat.parent) {
|
||||||
|
setParentCategoryId(catId)
|
||||||
|
}
|
||||||
|
}, [formik.values.categoryId, categories])
|
||||||
|
|
||||||
const handleHasVariantsChange = (value: string | boolean) => {
|
const handleHasVariantsChange = (value: string | boolean) => {
|
||||||
const next = value === true
|
const next = value === true
|
||||||
setHasVariants(next)
|
setHasVariants(next)
|
||||||
@@ -65,20 +96,44 @@ const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories, initi
|
|||||||
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='mt-5'>
|
<div className='mt-5 rowTwoInput flex flex-wrap gap-4'>
|
||||||
<Select
|
<div className='flex-1 min-w-[200px]'>
|
||||||
items={categories}
|
<Select
|
||||||
label='دسته بندی'
|
items={mainCategories}
|
||||||
placeholder='انتخاب کنید'
|
label='دسته اصلی'
|
||||||
name='categoryId'
|
placeholder='انتخاب کنید'
|
||||||
value={formik.values.categoryId}
|
name='parentCategoryId'
|
||||||
onChange={(e) => {
|
value={parentCategoryId}
|
||||||
const value = e.target.value
|
onChange={(e) => {
|
||||||
formik.setFieldValue('categoryId', value)
|
const value = e.target.value
|
||||||
}}
|
setParentCategoryId(value)
|
||||||
error_text={formik.touched.categoryId && formik.errors.categoryId ? String(formik.errors.categoryId) : ''}
|
formik.setFieldValue('categoryId', value)
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{subCategories.length > 0 && (
|
||||||
|
<div className='flex-1 min-w-[200px]'>
|
||||||
|
<Select
|
||||||
|
items={subCategories}
|
||||||
|
label='زیر دسته'
|
||||||
|
placeholder='انتخاب کنید (اختیاری)'
|
||||||
|
name='subcategoryId'
|
||||||
|
value={
|
||||||
|
subCategories.some((s) => s.value === formik.values.categoryId)
|
||||||
|
? formik.values.categoryId
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value
|
||||||
|
formik.setFieldValue('categoryId', value || parentCategoryId)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{formik.touched.categoryId && formik.errors.categoryId && (
|
||||||
|
<div className='mt-1 text-xs text-red-500'>{formik.errors.categoryId}</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -74,12 +74,11 @@ const CategoryForm: FC<Props> = ({
|
|||||||
<div className='mb-3 lg:mb-4'>
|
<div className='mb-3 lg:mb-4'>
|
||||||
<Select
|
<Select
|
||||||
label='والد (اختیاری)'
|
label='والد (اختیاری)'
|
||||||
items={categories?.data?.map((item) => {
|
items={
|
||||||
return {
|
categories?.data
|
||||||
label: item.title,
|
?.filter((item) => !item.parent)
|
||||||
value: item.id
|
?.map((item) => ({ label: item.title, value: item.id })) ?? []
|
||||||
}
|
}
|
||||||
}) || []}
|
|
||||||
placeholder='انتخاب'
|
placeholder='انتخاب'
|
||||||
{...formik.getFieldProps('parentId')}
|
{...formik.getFieldProps('parentId')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,14 +3,25 @@ import SwitchComponent from '@/components/Switch'
|
|||||||
import TrashWithConfrim from '@/components/TrashWithConfrim'
|
import TrashWithConfrim from '@/components/TrashWithConfrim'
|
||||||
import type { Category } from '../types/Types'
|
import type { Category } from '../types/Types'
|
||||||
|
|
||||||
|
function findCategoryById(cats: Category[], id: string | null): Category | undefined {
|
||||||
|
if (!id) return undefined
|
||||||
|
for (const c of cats) {
|
||||||
|
if (c.id === id) return c
|
||||||
|
const found = findCategoryById(c.children || [], id)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
categories: Category[]
|
||||||
onStatusChange: (id: string, value: boolean) => void
|
onStatusChange: (id: string, value: boolean) => void
|
||||||
onEdit: (category: Category) => void
|
onEdit: (category: Category) => void
|
||||||
onDelete: (id: string) => void
|
onDelete: (id: string) => void
|
||||||
isDeleting?: boolean
|
isDeleting?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryTableColumns = ({ onStatusChange, onEdit, onDelete, isDeleting = false }: Props) => {
|
const CategoryTableColumns = ({ categories, onStatusChange, onEdit, onDelete, isDeleting = false }: Props) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: 'آیکون',
|
title: 'آیکون',
|
||||||
@@ -39,8 +50,9 @@ const CategoryTableColumns = ({ onStatusChange, onEdit, onDelete, isDeleting = f
|
|||||||
title: 'والد',
|
title: 'والد',
|
||||||
key: 'categoryId',
|
key: 'categoryId',
|
||||||
render: (item: Category) => {
|
render: (item: Category) => {
|
||||||
|
const parent = typeof item.parent === 'string' ? findCategoryById(categories, item.parent) : item.parent
|
||||||
return (
|
return (
|
||||||
<div>{item.parent?.title || '-'}</div>
|
<div>{parent?.title || '-'}</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,12 +29,13 @@ export type Category = {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
|
parent: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
restId: string;
|
shop: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
order: number | null;
|
order: number | null;
|
||||||
parent?: Category;
|
children: Category[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProductDetailsCategory = {
|
export type ProductDetailsCategory = {
|
||||||
|
|||||||
Reference in New Issue
Block a user