category service

This commit is contained in:
hamid zarghami
2025-01-26 15:45:46 +03:30
parent bfb9f3cbcb
commit 22482aa20a
34 changed files with 1146 additions and 204 deletions
+74 -94
View File
@@ -1,16 +1,23 @@
import { FC } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Select from '../../components/Select'
import Input from '../../components/Input'
import Td from '../../components/Td'
import SwitchComponent from '../../components/Switch'
import UploadBoxDraggble from '../../components/UploadBoxDraggble'
import Button from '../../components/Button'
import { TickCircle } from 'iconsax-react'
import CreateCategory from './components/CreateCategory'
import PageLoading from '../../components/PageLoading'
import { ServiceCategoryType } from './types/ServiceTypes'
import { useGetAllCategory } from './hooks/useServiceData'
import StatusCategory from './components/StatusCategory'
import Pagination from '../../components/Pagination'
const Category: FC = () => {
const { t } = useTranslation('global')
const [search, setSearch] = useState<string>('')
const [page, setPage] = useState<number>(1)
const [isActive, setIsActive] = useState<'active' | 'deactive' | ''>('')
const getCategory = useGetAllCategory(page, search, isActive)
return (
<div className='mt-4'>
@@ -24,11 +31,12 @@ const Category: FC = () => {
<div className='w-32'>
<Select
items={[
{ label: 'All', value: 'all' },
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
{ label: t('all'), value: '' },
{ label: t('service.active'), value: 'active' },
{ label: t('service.deactive'), value: 'deactive' },
]}
placeholder={t('service.category')}
placeholder={t('status')}
onChange={(e) => setIsActive(e.target.value as 'active' | 'deactive' | '')}
/>
</div>
@@ -37,98 +45,70 @@ const Category: FC = () => {
variant='search'
placeholder={t('search')}
className='bg-white'
onChangeSearchFinal={(value) => setSearch(value)}
/>
</div>
</div>
<div className='flex-1 mt-8 bg-white py-2 xl:px-10 px-5 rounded-3xl'>
<div className='relative overflow-x-auto rounded-3xl w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text='' />
<Td text={t('service.title')} />
<Td text={t('service.parent_category')} />
<Td text={t('service.count_sub_category')} />
<Td text={t('service.count_service')} />
<Td text={t('ticket.status')} />
<Td text={''} />
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text=''>
<div className='size-10 rounded-lg bg-red-100'></div>
</Td>
<Td text={'کسب و کار'} />
<Td text={'-'} />
<Td text={'2'} />
<Td text={'14'} />
<Td text={''}>
<SwitchComponent
active
onChange={() => null}
/>
</Td>
<Td text={''} />
</tr>
</tbody>
</table>
</div>
{
getCategory.isPending ?
<div className='mt-4'>
<PageLoading />
</div>
:
<div className='relative overflow-x-auto rounded-3xl w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text='' />
<Td text={t('service.title')} />
<Td text={t('service.parent_category')} />
<Td text={t('service.count_sub_category')} />
<Td text={t('service.count_service')} />
<Td text={t('ticket.status')} />
<Td text={''} />
</tr>
</thead>
<tbody>
{
getCategory.data?.data?.categories?.map((item: ServiceCategoryType) => {
return (
<tr key={item.id} className='tr'>
<Td text=''>
<img src={item.icon} className='size-10 rounded-lg' />
</Td>
<Td text={item.title} />
<Td text={item.parent ? item.parent.title : '-'} />
<Td text={item.childrenCount + ''} />
<Td text={item.servicesCount + ''} />
<Td text={''}>
<StatusCategory
defaultActive={item.isActive}
id={item.id}
/>
</Td>
<Td text={''} />
</tr>
)
})
}
</tbody>
</table>
<div className='flex justify-end pb-3'>
<Pagination
currentPage={page}
totalPages={getCategory.data?.data?.pager?.totalPages}
onPageChange={setPage}
/>
</div>
</div>
}
</div>
</div>
<div className='bg-white p-8 w-sidebar hidden xl:block rounded-3xl overflow-hidden relative'>
<div>
{t('service.add_category')}
</div>
<div className='mt-8 flex justify-between'>
<div className='text-sm'>
{t('service.status_category')}
</div>
<div className='flex'>
<SwitchComponent active onChange={() => { }} />
</div>
</div>
<div className='mt-8'>
<Input
label={t('service.title_category')}
/>
</div>
<div className='mt-6'>
<Select
label={t('service.category_parent_2')}
items={[
{ label: 'All', value: 'all' },
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
]}
placeholder={t('select')}
/>
</div>
<div className='mt-6'>
<UploadBoxDraggble
label={t('service.icon_serice_category')}
onChange={() => null}
/>
</div>
<div className='flex flex-1 justify-end items-end mt-8'>
<Button
className='w-fit px-5'
>
<div className='flex items-center gap-2'>
<TickCircle
className='size-5'
color='white'
/>
<div>{t('save')}</div>
</div>
</Button>
</div>
</div>
<CreateCategory />
</div>
</div>
)
@@ -0,0 +1,123 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import SwitchComponent from '../../../components/Switch'
import Input from '../../../components/Input'
import Select from '../../../components/Select'
import UploadBoxDraggble from '../../../components/UploadBoxDraggble'
import Button from '../../../components/Button'
import { TickCircle } from 'iconsax-react'
import { useCreateServiceCategory, useGetCategoryParents } from '../hooks/useServiceData'
import { CreateServiceCategoryType, ServiceCategoryType } from '../types/ServiceTypes'
import * as Yup from 'yup'
import { useFormik } from 'formik'
import { toast } from 'react-toastify'
import { ErrorType } from '../../../helpers/types'
const CreateCategory: FC = () => {
const { t } = useTranslation('global')
const getCategory = useGetCategoryParents()
const createCategory = useCreateServiceCategory()
const formik = useFormik<CreateServiceCategoryType>({
initialValues: {
title: '',
isActive: true,
parentId: ''
},
validationSchema: Yup.object({
title: Yup.string().required(t('errors.required')),
}),
onSubmit: (values) => {
const params = {
...values,
icon: 'https://picsum.photos/200/300',
parentId: values.parentId ? values.parentId : undefined
}
createCategory.mutate(params, {
onSuccess: () => {
formik.resetForm()
getCategory.refetch()
toast.success(t('success'))
},
onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0])
}
})
}
})
return (
<div className='bg-white p-8 w-sidebar hidden xl:block rounded-3xl overflow-hidden relative'>
<div>
{t('service.add_category')}
</div>
<div className='mt-8 flex justify-between'>
<div className='text-sm'>
{t('service.status_category')}
</div>
<div className='flex'>
<SwitchComponent
active={formik.values.isActive}
onChange={(value) => formik.setFieldValue('isActive', value)}
/>
</div>
</div>
<div className='mt-8'>
<Input
label={t('service.title_category')}
value={formik.values.title}
name='title'
onChange={formik.handleChange}
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
/>
</div>
{
getCategory.data &&
<div className='mt-6'>
<Select
label={t('service.category_parent_2')}
items={getCategory.data?.data?.categories?.map((item: ServiceCategoryType) => {
return {
label: item.title,
value: item.id
}
})}
placeholder={t('select')}
name='parentId'
onChange={formik.handleChange}
error_text={formik.touched.parentId && formik.errors.parentId ? formik.errors.parentId : ''}
/>
</div>
}
<div className='mt-6'>
<UploadBoxDraggble
label={t('service.icon_serice_category')}
onChange={() => null}
/>
</div>
<div className='flex flex-1 justify-end items-end mt-8'>
<Button
className='w-fit px-5'
onClick={() => formik.handleSubmit()}
isLoading={createCategory.isPending}
>
<div className='flex items-center gap-2'>
<TickCircle
className='size-5'
color='white'
/>
<div>{t('save')}</div>
</div>
</Button>
</div>
</div>
)
}
export default CreateCategory
@@ -0,0 +1,28 @@
import { FC, useState } from 'react'
import SwitchComponent from '../../../components/Switch'
import { useChangeStatusCategory } from '../hooks/useServiceData'
type Props = {
defaultActive: boolean,
id: string
}
const StatusCategory: FC<Props> = (props: Props) => {
const [isActive, setIsActive] = useState<boolean>(props.defaultActive)
const toggleStatus = useChangeStatusCategory()
const handleChange = (value: boolean) => {
setIsActive(value)
toggleStatus.mutate({ id: props.id })
}
return (
<SwitchComponent
active={isActive}
onChange={handleChange}
/>
)
}
export default StatusCategory
+44
View File
@@ -0,0 +1,44 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/ServiceService";
import {
CreateServiceCategoryType,
ToggleStatusCategoryType,
} from "../types/ServiceTypes";
export const useGetCategoryParents = () => {
return useQuery({
queryKey: ["service-category"],
queryFn: api.getCategoryParent,
});
};
export const useGetAllCategory = (
page: number,
search: string,
isActive: "active" | "deactive" | ""
) => {
return useQuery({
queryKey: ["service-category-all", page, search, isActive],
queryFn: () => api.getAllCategory(page, search, isActive),
});
};
export const useCreateServiceCategory = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (variables: CreateServiceCategoryType) =>
api.createServiceCategory(variables),
onSuccess: () => {
queryClient.refetchQueries({
queryKey: ["service-category-all"],
});
},
});
};
export const useChangeStatusCategory = () => {
return useMutation({
mutationFn: (variables: ToggleStatusCategoryType) =>
api.changeStatusCategory(variables),
});
};
@@ -0,0 +1,39 @@
import axios from "../../../config/axios";
import {
CreateServiceCategoryType,
ToggleStatusCategoryType,
} from "../types/ServiceTypes";
export const getCategoryParent = async () => {
const { data } = await axios.get(`/services/category`);
return data;
};
export const getAllCategory = async (
page: number,
search: string,
isActive: "active" | "deactive" | ""
) => {
let query = `?page=${page}&q=${search}`;
if (isActive) {
query += `&isActive=${isActive === "active" ? "1" : "0"}`;
}
const { data } = await axios.get(`/services/category-list${query}`);
return data;
};
export const createServiceCategory = async (
params: CreateServiceCategoryType
) => {
const { data } = await axios.post(`/services/category`, params);
return data;
};
export const changeStatusCategory = async (
params: ToggleStatusCategoryType
) => {
const { data } = await axios.post(
`/services/category/toggle-status/${params.id}`
);
return data;
};
+27
View File
@@ -0,0 +1,27 @@
export type ServiceCategoryType = {
id: string;
title: string;
icon: string;
isActive: boolean;
parent: ServiceCategoryType;
children: ServiceCategoryType[];
parentId: string;
childrenCount: number;
servicesCount: number;
};
export type CreateServiceCategoryType = {
title: string;
icon?: string;
isActive: boolean;
parentId?: string;
};
export type ToggleStatusCategoryType = {
id: string;
};