customers

This commit is contained in:
hamid zarghami
2025-02-25 16:29:40 +03:30
parent a94f31db04
commit 4d29331b6b
6 changed files with 334 additions and 112 deletions
+255 -107
View File
@@ -1,4 +1,4 @@
import { FC } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import UploadBoxDraggble from '../../components/UploadBoxDraggble';
@@ -6,10 +6,100 @@ import Button from '../../components/Button';
import { TickCircle } from 'iconsax-react';
import DatePickerComponent from '../../components/DatePicker';
import Select from '../../components/Select';
import * as Yup from 'yup'
import { useFormik } from 'formik';
import { CreateCustomerType, ProvinesItemType } from './types/CustomerTypes';
import { useCreateCustomer, useGetCities, useGetProvines } from './hooks/useCustomerData';
import PageLoading from '../../components/PageLoading';
import { toast } from 'react-toastify';
import { ErrorType } from '../../helpers/types';
import { useSingleUpload } from '../service/hooks/useServiceData';
import { useNavigate } from 'react-router-dom';
import { Pages } from '../../config/Pages';
import Textarea from '../../components/Textarea';
const AddCustomer: FC = () => {
const { t } = useTranslation('global')
const navigate = useNavigate()
const [provinesId, setProvinesId] = useState<string>('')
const [file, setFile] = useState<File>()
const getProvines = useGetProvines()
const getCities = useGetCities(provinesId)
const singleUpload = useSingleUpload()
const createCustomer = useCreateCustomer()
const handleCreateCustomer = (values: CreateCustomerType) => {
createCustomer.mutate(values, {
onSuccess: () => {
toast.success(t('success'))
navigate(Pages.customer.list)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0])
}
})
}
const formik = useFormik<CreateCustomerType>({
initialValues: {
phone: '',
firstName: '',
lastName: '',
email: '',
birthDate: '',
nationalCode: '',
password: '',
economicCode: '',
companyRegisteredName: '',
registrationId: '',
nationalId: '',
number: '',
postalCode: '',
address: '',
cityId: '',
profilePic: ''
},
validationSchema: Yup.object({
phone: Yup.string().required(t('errors.required')),
firstName: Yup.string().required(t('errors.required')),
lastName: Yup.string().required(t('errors.required')),
email: Yup.string().email(t('errors.email')).required(t('errors.required')),
birthDate: Yup.string().required(t('errors.required')),
nationalCode: Yup.string().required(t('errors.required')),
password: Yup.string().required(t('errors.required')),
economicCode: Yup.string().required(t('errors.required')),
companyRegisteredName: Yup.string().required(t('errors.required')),
registrationId: Yup.string().required(t('errors.required')),
nationalId: Yup.string().required(t('errors.required')),
number: Yup.string().required(t('errors.required')),
postalCode: Yup.string().required(t('errors.required')),
address: Yup.string().required(t('errors.required')),
cityId: Yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
if (file) {
const formData = new FormData()
formData.append('file', file)
await singleUpload.mutateAsync(formData, {
onSuccess: (data) => {
values.profilePic = data?.data?.url
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
handleCreateCustomer(values)
} else {
values.profilePic = undefined
handleCreateCustomer(values)
}
}
})
return (
<div className='w-full mt-4'>
@@ -20,6 +110,8 @@ const AddCustomer: FC = () => {
<div>
<Button
className='px-5'
onClick={() => formik.handleSubmit()}
isLoading={createCustomer.isPending || singleUpload.isPending}
>
<div className='flex gap-2'>
<TickCircle
@@ -31,121 +123,177 @@ const AddCustomer: FC = () => {
</Button>
</div>
</div>
<div className='flex gap-6'>
<div className='flex-1'>
{
getProvines.isPending ?
<PageLoading />
:
<div className='flex gap-6'>
<div className='flex-1'>
<div className='flex-1 mt-10 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div className='rowTwoInput'>
<Input
label={t('customer.name')}
/>
<div className='flex-1 mt-10 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div className='rowTwoInput'>
<Input
label={t('customer.name')}
{...formik.getFieldProps('firstName')}
error_text={formik.touched.firstName && formik.errors.firstName ? formik.errors.firstName : ''}
/>
<Input
label={t('customer.family')}
/>
<Input
label={t('customer.family')}
{...formik.getFieldProps('lastName')}
error_text={formik.touched.lastName && formik.errors.lastName ? formik.errors.lastName : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<DatePickerComponent
label={t('customer.dateofbirth')}
onChange={(value) => formik.setFieldValue('birthDate', value)}
error_text={formik.touched.birthDate && formik.errors.birthDate ? formik.errors.birthDate : ''}
placeholder=''
/>
<Input
label={t('customer.national_code')}
placeholder={t('customer.enter_nation_code')}
{...formik.getFieldProps('nationalCode')}
error_text={formik.touched.nationalCode && formik.errors.nationalCode ? formik.errors.nationalCode : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.email')}
placeholder={t('customer.enter_email')}
{...formik.getFieldProps('email')}
error_text={formik.touched.email && formik.errors.email ? formik.errors.email : ''}
/>
<Input
label={t('customer.phone_number')}
placeholder={t('customer.enter_phone_number')}
{...formik.getFieldProps('phone')}
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
/>
</div>
<div className='mt-8'>
{t('customer.information_legal')}
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.copany_name')}
placeholder={t('customer.enter_company_name')}
{...formik.getFieldProps('companyRegisteredName')}
error_text={formik.touched.companyRegisteredName && formik.errors.companyRegisteredName ? formik.errors.companyRegisteredName : ''}
/>
<Input
label={t('customer.id_national')}
placeholder={t('customer.enter_id_nation')}
{...formik.getFieldProps('nationalId')}
error_text={formik.touched.nationalId && formik.errors.nationalId ? formik.errors.nationalId : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.economic_code')}
placeholder={t('customer.enter_economic_code')}
{...formik.getFieldProps('economicCode')}
error_text={formik.touched.economicCode && formik.errors.economicCode ? formik.errors.economicCode : ''}
/>
<Input
label={t('customer.postal_code')}
placeholder={t('customer.enter_postal_code')}
{...formik.getFieldProps('postalCode')}
error_text={formik.touched.postalCode && formik.errors.postalCode ? formik.errors.postalCode : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.registeration_id')}
placeholder={t('customer.enter_registeration_id')}
{...formik.getFieldProps('registrationId')}
error_text={formik.touched.registrationId && formik.errors.registrationId ? formik.errors.registrationId : ''}
/>
<Input
label={t('customer.your_phone_fixed')}
placeholder={t('customer.enter_phone_fixed')}
{...formik.getFieldProps('number')}
error_text={formik.touched.number && formik.errors.number ? formik.errors.number : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Select
label={t('customer.state')}
items={getProvines.data?.data?.provinces.map((item: ProvinesItemType) => ({ label: item.name, value: item.id }))}
onChange={(e) => setProvinesId(e.target.value)}
placeholder={t('select')}
/>
<Select
label={t('customer.city')}
items={getCities.data?.data?.cities ? getCities.data?.data?.cities.map((item: ProvinesItemType) => ({ label: item.name, value: item.id })) : []}
placeholder={t('select')}
{...formik.getFieldProps('cityId')}
error_text={formik.touched.cityId && formik.errors.cityId ? formik.errors.cityId : ''}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Textarea
{...formik.getFieldProps('address')}
placeholder={t('customer.address')}
error_text={formik.touched.address && formik.errors.address ? formik.errors.address : ''}
/>
</div>
</div>
</div>
<div className='mt-8 rowTwoInput'>
<DatePickerComponent
label={t('customer.dateofbirth')}
onChange={() => null}
placeholder=''
/>
<Input
label={t('customer.national_code')}
placeholder={t('customer.enter_nation_code')}
/>
</div>
<div className='bg-white mt-10 w-sidebar text-xs hidden 2xl:block h-fit px-5 py-7 rounded-3xl'>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.email')}
placeholder={t('customer.enter_email')}
/>
<div>
<Input
label={t('customer.password')}
type='password'
{...formik.getFieldProps('password')}
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
/>
</div>
<Input
label={t('customer.phone_number')}
placeholder={t('customer.enter_phone_number')}
/>
</div>
{/* <div className='mt-5'>
<Input
label={t('customer.reapeat_password')}
type='password'
{...formik.getFieldProps('password')}
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
/>
</div> */}
<div className='mt-8'>
{t('customer.information_legal')}
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.economic_code')}
placeholder={t('customer.enter_economic_code')}
/>
<Input
label={t('customer.id_national')}
placeholder={t('customer.enter_id_nation')}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Input
label={t('customer.registeration_id')}
placeholder={t('customer.enter_registeration_id')}
/>
<Input
label={t('customer.your_phone_fixed')}
placeholder={t('customer.enter_phone_fixed')}
/>
</div>
<div className='mt-8 rowTwoInput'>
<Select
label={t('customer.state')}
items={[
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
]}
placeholder={t('select')}
/>
<Select
label={t('customer.city')}
items={[
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
]}
placeholder={t('select')}
/>
<div className='mt-8'>
<div>{t('service.service_icon')}</div>
<div className='mt-2'>
<UploadBoxDraggble
label={t('service.upload_icon_service')}
onChange={(files) => setFile(files[0])}
/>
</div>
</div>
</div>
</div>
</div>
<div className='bg-white mt-10 w-sidebar text-xs hidden 2xl:block h-fit px-5 py-7 rounded-3xl'>
<div>
<Input
label={t('customer.password')}
/>
</div>
<div className='mt-5'>
<Input
label={t('customer.reapeat_password')}
/>
</div>
<div className='mt-8'>
<div>{t('service.service_icon')}</div>
<div className='mt-2'>
<UploadBoxDraggble
label={t('service.upload_icon_service')}
onChange={() => null}
/>
</div>
</div>
</div>
</div>
}
</div>
)
}
+2 -2
View File
@@ -22,7 +22,7 @@ const CustomerList: FC = () => {
<div>
{t('customer.customer_list')}
</div>
<div>
<Link to={Pages.customer.create}>
<Button
className='px-5'
>
@@ -34,7 +34,7 @@ const CustomerList: FC = () => {
<div>{t('customer.add_cutomer')}</div>
</div>
</Button>
</div>
</Link>
</div>
<div className='flex justify-between items-center mt-12'>
+31 -2
View File
@@ -1,9 +1,38 @@
import * as api from "../service/CustomerService";
import { useQuery } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { CreateCustomerType } from "../types/CustomerTypes";
export const useGetCustomers = () => {
return useQuery({
queryKey: ["invoices"],
queryKey: ["customers"],
queryFn: () => api.getCustomers(),
});
};
export const useGetProvines = () => {
return useQuery({
queryKey: ["provines"],
queryFn: () => api.getProvines(),
});
};
export const useGetCities = (provincesId: string) => {
return useQuery({
queryKey: ["cities", provincesId],
queryFn: () => api.getCities(provincesId),
enabled: !!provincesId,
});
};
export const useCreateCustomer = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (variables: CreateCustomerType) =>
api.createCustomer(variables),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["customers"],
});
},
});
};
@@ -1,6 +1,22 @@
import axios from "../../../config/axios";
import { CreateCustomerType } from "../types/CustomerTypes";
export const getCustomers = async () => {
const { data } = await axios.get(`/users/customers`);
return data;
};
export const getProvines = async () => {
const { data } = await axios.get(`/address/provinces/list?limit=50`);
return data;
};
export const getCities = async (id: string) => {
const { data } = await axios.get(`/address/provinces/${id}/cities`);
return data;
};
export const createCustomer = async (params: CreateCustomerType) => {
const { data } = await axios.post(`/users/customer`, params);
return data;
};
+24
View File
@@ -21,3 +21,27 @@ export type CustomerItemType = {
ticketsCount: string;
invoicesCount: string;
};
export type CreateCustomerType = {
phone: string;
firstName: string;
lastName: string;
email: string | null;
birthDate: string;
nationalCode: string;
password: string;
economicCode: string;
companyRegisteredName: string;
registrationId: string;
nationalId: string;
number: string;
postalCode: string;
address: string;
cityId: string;
profilePic?: string;
};
export type ProvinesItemType = {
id: string;
name: string;
};