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
+6 -1
View File
@@ -601,7 +601,12 @@
"state": "استان", "state": "استان",
"city": "شهر", "city": "شهر",
"password": "رمز عبور", "password": "رمز عبور",
"reapeat_password": "تکرار رمز عبور" "reapeat_password": "تکرار رمز عبور",
"copany_name": "نام شرکت",
"enter_company_name": "نام ثبتی شرکت را وارد کنید",
"postal_code": "کد پستی",
"enter_postal_code": "کد پستی را وارد کنید",
"address": "آدرس"
}, },
"messages": { "messages": {
"messages_list": "لیست پیام ها", "messages_list": "لیست پیام ها",
+255 -107
View File
@@ -1,4 +1,4 @@
import { FC } from 'react' import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Input from '../../components/Input' import Input from '../../components/Input'
import UploadBoxDraggble from '../../components/UploadBoxDraggble'; import UploadBoxDraggble from '../../components/UploadBoxDraggble';
@@ -6,10 +6,100 @@ import Button from '../../components/Button';
import { TickCircle } from 'iconsax-react'; import { TickCircle } from 'iconsax-react';
import DatePickerComponent from '../../components/DatePicker'; import DatePickerComponent from '../../components/DatePicker';
import Select from '../../components/Select'; 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 AddCustomer: FC = () => {
const { t } = useTranslation('global') 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 ( return (
<div className='w-full mt-4'> <div className='w-full mt-4'>
@@ -20,6 +110,8 @@ const AddCustomer: FC = () => {
<div> <div>
<Button <Button
className='px-5' className='px-5'
onClick={() => formik.handleSubmit()}
isLoading={createCustomer.isPending || singleUpload.isPending}
> >
<div className='flex gap-2'> <div className='flex gap-2'>
<TickCircle <TickCircle
@@ -31,121 +123,177 @@ const AddCustomer: FC = () => {
</Button> </Button>
</div> </div>
</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='flex-1 mt-10 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div className='rowTwoInput'> <div className='rowTwoInput'>
<Input <Input
label={t('customer.name')} label={t('customer.name')}
/> {...formik.getFieldProps('firstName')}
error_text={formik.touched.firstName && formik.errors.firstName ? formik.errors.firstName : ''}
/>
<Input <Input
label={t('customer.family')} 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>
<div className='mt-8 rowTwoInput'> <div className='bg-white mt-10 w-sidebar text-xs hidden 2xl:block h-fit px-5 py-7 rounded-3xl'>
<DatePickerComponent
label={t('customer.dateofbirth')}
onChange={() => null}
placeholder=''
/>
<Input
label={t('customer.national_code')}
placeholder={t('customer.enter_nation_code')}
/>
</div>
<div className='mt-8 rowTwoInput'> <div>
<Input <Input
label={t('customer.email')} label={t('customer.password')}
placeholder={t('customer.enter_email')} type='password'
/> {...formik.getFieldProps('password')}
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
/>
</div>
<Input {/* <div className='mt-5'>
label={t('customer.phone_number')} <Input
placeholder={t('customer.enter_phone_number')} label={t('customer.reapeat_password')}
/> type='password'
</div> {...formik.getFieldProps('password')}
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
/>
</div> */}
<div className='mt-8'> <div className='mt-8'>
{t('customer.information_legal')} <div>{t('service.service_icon')}</div>
</div> <div className='mt-2'>
<UploadBoxDraggble
<div className='mt-8 rowTwoInput'> label={t('service.upload_icon_service')}
<Input onChange={(files) => setFile(files[0])}
label={t('customer.economic_code')} />
placeholder={t('customer.enter_economic_code')} </div>
/> </div>
<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> </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> </div>
) )
} }
+2 -2
View File
@@ -22,7 +22,7 @@ const CustomerList: FC = () => {
<div> <div>
{t('customer.customer_list')} {t('customer.customer_list')}
</div> </div>
<div> <Link to={Pages.customer.create}>
<Button <Button
className='px-5' className='px-5'
> >
@@ -34,7 +34,7 @@ const CustomerList: FC = () => {
<div>{t('customer.add_cutomer')}</div> <div>{t('customer.add_cutomer')}</div>
</div> </div>
</Button> </Button>
</div> </Link>
</div> </div>
<div className='flex justify-between items-center mt-12'> <div className='flex justify-between items-center mt-12'>
+31 -2
View File
@@ -1,9 +1,38 @@
import * as api from "../service/CustomerService"; 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 = () => { export const useGetCustomers = () => {
return useQuery({ return useQuery({
queryKey: ["invoices"], queryKey: ["customers"],
queryFn: () => api.getCustomers(), 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 axios from "../../../config/axios";
import { CreateCustomerType } from "../types/CustomerTypes";
export const getCustomers = async () => { export const getCustomers = async () => {
const { data } = await axios.get(`/users/customers`); const { data } = await axios.get(`/users/customers`);
return data; 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; ticketsCount: string;
invoicesCount: 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;
};