@@ -27,55 +30,12 @@ const Register: FC = () => {
- {/*
- {t('auth.user_info')}
-
*/}
-
-
-
-
-
-
-
-
- console.log(date)} placeholder={t('auth.select_date')} />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {
+ stepLogin === 1 ?
+
+ :
+
+ }
diff --git a/src/pages/auth/components/LoginStep1.tsx b/src/pages/auth/components/LoginStep1.tsx
index 2951191..c3089cc 100644
--- a/src/pages/auth/components/LoginStep1.tsx
+++ b/src/pages/auth/components/LoginStep1.tsx
@@ -7,13 +7,17 @@ import * as Yup from 'yup'
import { useAuthStore } from '../store/AuthStore'
import Error from '../../../components/Error'
import Button from '../../../components/Button'
-import { Link } from 'react-router-dom'
-import { Pages } from '../../../config/Pages'
+import { isEmail } from '../../../config/func'
+import { useCheckHasAccount, useLoginWithOtp } from '../hooks/useAuthData'
+import { toast } from 'react-toastify'
+import { ErrorType } from '../../../helpers/types'
const LoginStep1: FC = () => {
const { t } = useTranslation('global')
- const { setPhone, phone, setStepLogin } = useAuthStore()
+ const { setPhone, phone, setStepLogin, setEmail } = useAuthStore()
+ const loginWithOtp = useLoginWithOtp()
+ const checkHasAccount = useCheckHasAccount()
const formik = useFormik({
initialValues: {
@@ -24,8 +28,28 @@ const LoginStep1: FC = () => {
.required(t('errors.required'))
}),
onSubmit(values) {
- setPhone(values.phone_email)
- setStepLogin(2)
+ if (isEmail(values.phone_email)) {
+ setEmail(values.phone_email)
+ checkHasAccount.mutate({ email: values.phone_email }, {
+ onSuccess() {
+ setStepLogin(2)
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
+ } else {
+ setPhone(values.phone_email)
+ loginWithOtp.mutate({ phone: values.phone_email }, {
+ onSuccess() {
+ setStepLogin(2)
+ toast.success(t('auth.otp_sent'))
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
+ }
},
})
@@ -65,16 +89,9 @@ const LoginStep1: FC = () => {
label={t('auth.next')}
className='mt-8'
onClick={() => formik.handleSubmit()}
+ isLoading={loginWithOtp.isPending || checkHasAccount.isPending}
/>
-
-
- {t('auth.have_account')}
-
-
-
{t('auth.register')}
-
-
)
}
diff --git a/src/pages/auth/components/LoginStep2.tsx b/src/pages/auth/components/LoginStep2.tsx
index 8acbb0b..0856d98 100644
--- a/src/pages/auth/components/LoginStep2.tsx
+++ b/src/pages/auth/components/LoginStep2.tsx
@@ -1,34 +1,49 @@
-import { FC, useState } from 'react'
+import { FC } from 'react'
import { useTranslation } from 'react-i18next'
-import { LoginType } from '../types/AuthTypes'
+import { OtpVerifyType } from '../types/AuthTypes'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useAuthStore } from '../store/AuthStore'
import Button from '../../../components/Button'
import OTPInput from 'react-otp-input'
import { useCountDown } from '../../../hooks/useCountDown'
-import ArrowLeftIcon from '../../../assets/images/arrow-left.svg'
+// import ArrowLeftIcon from '../../../assets/images/arrow-left.svg'
import { Pages } from '../../../config/Pages'
+import { useOtpVerify } from '../hooks/useAuthData'
+import { ErrorType } from '../../../helpers/types'
+import { toast } from 'react-toastify'
const LoginStep2: FC = () => {
const { t } = useTranslation('global')
- const { setPhone, phone, setStepLogin } = useAuthStore()
- const [otpValue, setOtpValue] = useState
()
+ const { phone, setStepLogin } = useAuthStore()
const { value } = useCountDown(2, true)
+ const otpVerify = useOtpVerify()
- const formik = useFormik({
+ const formik = useFormik({
initialValues: {
- phone_email: phone,
+ phone: phone,
+ code: ''
},
validationSchema: Yup.object({
- phone_email: Yup.string()
+ code: Yup.string()
.required(t('errors.required'))
}),
onSubmit(values) {
- setPhone(values.phone_email)
- localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, 'token')
- window.location.href = Pages.dashboard
+ const params: OtpVerifyType = {
+ phone: phone,
+ code: values.code
+ }
+ otpVerify.mutate(params, {
+ onSuccess(data) {
+ localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, data?.data?.accessToken?.token)
+ localStorage.setItem(import.meta.env.VITE_REFRESH_TOKEN_NAME, data?.data?.refreshToken?.token)
+ window.location.href = Pages.dashboard
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
},
})
@@ -59,14 +74,19 @@ const LoginStep2: FC = () => {
setOtpValue(otp)}
+ onChange={(otp: string) => formik.setFieldValue('code', otp)}
renderInput={(props) => }
/>
+ {
+ formik.touched.code && formik.errors.code &&
+ {formik.errors.code}
+ }
+
{value}
@@ -81,14 +101,15 @@ const LoginStep2: FC = () => {
label={t('auth.login')}
className='mt-8'
onClick={() => formik.handleSubmit()}
+ isLoading={otpVerify.isPending}
/>
-
setStepLogin(3)} className='mt-6 cursor-pointer flex gap-2 items-center text-sm'>
+ {/*
setStepLogin(3)} className='mt-6 cursor-pointer flex gap-2 items-center text-sm'>
{t('auth.login_with_password')}

-
+
*/}
)
}
diff --git a/src/pages/auth/components/LoginStep3.tsx b/src/pages/auth/components/LoginStep3.tsx
index 041f776..7d82016 100644
--- a/src/pages/auth/components/LoginStep3.tsx
+++ b/src/pages/auth/components/LoginStep3.tsx
@@ -1,7 +1,7 @@
import { FC } from 'react'
import Input from '../../../components/Input'
import { useTranslation } from 'react-i18next'
-import { LoginPasswordType } from '../types/AuthTypes'
+import { LoginWithPasswordType } from '../types/AuthTypes'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useAuthStore } from '../store/AuthStore'
@@ -10,24 +10,36 @@ import Button from '../../../components/Button'
import { Link } from 'react-router-dom'
import { Pages } from '../../../config/Pages'
import ArrowLeftIcon from '../../../assets/images/arrow-left.svg'
+import { useLoginWithPassword } from '../hooks/useAuthData'
+import { toast } from 'react-toastify'
+import { ErrorType } from '../../../helpers/types'
const LoginStep3: FC = () => {
const { t } = useTranslation('global')
- const { setStepLogin } = useAuthStore()
+ const { setStepLogin, email } = useAuthStore()
+ const loginWithPassword = useLoginWithPassword()
- const formik = useFormik
({
+ const formik = useFormik({
initialValues: {
password: '',
+ email: email
},
validationSchema: Yup.object({
- phone_email: Yup.string()
+ password: Yup.string()
.required(t('errors.required'))
}),
onSubmit(values) {
- console.log(values)
- localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, 'token')
- window.location.href = Pages.dashboard
+ loginWithPassword.mutate(values, {
+ onSuccess(data) {
+ localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, data?.data?.accessToken?.token)
+ localStorage.setItem(import.meta.env.VITE_REFRESH_TOKEN_NAME, data?.data?.refreshToken?.token)
+ window.location.href = Pages.dashboard
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
},
})
@@ -51,6 +63,7 @@ const LoginStep3: FC = () => {
name='password'
onChange={formik.handleChange}
value={formik.values.password}
+ error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
/>
{
diff --git a/src/pages/auth/components/RegisterStep1.tsx b/src/pages/auth/components/RegisterStep1.tsx
new file mode 100644
index 0000000..9122f13
--- /dev/null
+++ b/src/pages/auth/components/RegisterStep1.tsx
@@ -0,0 +1,77 @@
+import { FC } from 'react'
+import Input from '../../../components/Input'
+import { useTranslation } from 'react-i18next'
+import { CheckHasAccountPhoneType } from '../types/AuthTypes'
+import { useFormik } from 'formik'
+import * as Yup from 'yup'
+import { useAuthStore } from '../store/AuthStore'
+import Error from '../../../components/Error'
+import Button from '../../../components/Button'
+import { useCheckHasAccountRegister, useLoginWithOtp } from '../hooks/useAuthData'
+import { toast } from 'react-toastify'
+import { ErrorType } from '../../../helpers/types'
+
+const RegisterStep1: FC = () => {
+
+ const { t } = useTranslation('global')
+ const { setPhone, setStepLogin } = useAuthStore()
+ const loginWithOtp = useLoginWithOtp()
+ const checkHasAccount = useCheckHasAccountRegister()
+
+ const formik = useFormik({
+ initialValues: {
+ phone: '',
+ },
+ validationSchema: Yup.object({
+ phone: Yup.string()
+ .required(t('errors.required'))
+ }),
+ onSubmit(values) {
+ checkHasAccount.mutate(values, {
+ onSuccess() {
+ setPhone(values.phone)
+ setStepLogin(2)
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
+ },
+ })
+
+ return (
+
+
+
+
+
+
+ {
+ formik.touched.phone && formik.errors.phone &&
+
+ }
+
+
+
+
+
+ )
+}
+
+export default RegisterStep1
\ No newline at end of file
diff --git a/src/pages/auth/components/RegisterStep2.tsx b/src/pages/auth/components/RegisterStep2.tsx
new file mode 100644
index 0000000..483bec2
--- /dev/null
+++ b/src/pages/auth/components/RegisterStep2.tsx
@@ -0,0 +1,139 @@
+import { FC, Fragment } from 'react'
+import { useFormik } from 'formik'
+import { RegisterType } from '../types/AuthTypes'
+import * as Yup from 'yup'
+import { useAuthStore } from '../store/AuthStore'
+import { useTranslation } from 'react-i18next'
+import Input from '../../../components/Input'
+import DatePickerComponent from '../../../components/DatePicker'
+import Button from '../../../components/Button'
+import { useRegister } from '../hooks/useAuthData'
+import { ErrorType } from '../../../helpers/types'
+import { toast } from 'react-toastify'
+import { Pages } from '../../../config/Pages'
+
+const RegisterStep2: FC = () => {
+
+ const { t } = useTranslation('global')
+ const { phone } = useAuthStore()
+ const register = useRegister()
+
+ const formik = useFormik({
+ initialValues: {
+ firstName: '',
+ lastName: '',
+ birthDate: '',
+ code: 0,
+ nationalCode: '',
+ password: '',
+ phone: phone,
+ },
+ validationSchema: Yup.object({
+ firstName: Yup.string()
+ .required(t('errors.required')),
+ lastName: Yup.string()
+ .required(t('errors.required')),
+ birthDate: Yup.string()
+ .required(t('errors.required')),
+ code: Yup.number()
+ .required(t('errors.required')),
+ nationalCode: Yup.string()
+ .required(t('errors.required')),
+ password: Yup.string()
+ .required(t('errors.required')),
+ phone: Yup.string()
+ .required(t('errors.required')),
+ }),
+ onSubmit(values) {
+ register.mutate(values, {
+ onSuccess(data) {
+ localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, data?.data?.accessToken?.token)
+ localStorage.setItem(import.meta.env.VITE_REFRESH_TOKEN_NAME, data?.data?.refreshToken?.token)
+ window.location.href = Pages.dashboard
+ },
+ onError(error: ErrorType) {
+ toast.error(error?.response?.data?.error?.message[0])
+ },
+ })
+ },
+ })
+
+ console.log(formik.errors);
+
+
+ return (
+
+
+
+
+
+
+
+
+ formik.setFieldValue('birthDate', date)}
+ placeholder={t('auth.select_date')}
+ error_text={formik.touched.birthDate && formik.errors.birthDate ? formik.errors.birthDate : ''}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default RegisterStep2
\ No newline at end of file
diff --git a/src/pages/auth/hooks/useAuthData.tsx b/src/pages/auth/hooks/useAuthData.tsx
new file mode 100644
index 0000000..0c480e9
--- /dev/null
+++ b/src/pages/auth/hooks/useAuthData.tsx
@@ -0,0 +1,39 @@
+import { useMutation } from '@tanstack/react-query';
+import * as api from '../service/AuthService'
+import { CheckHasAccountPhoneType, CheckHasAccountType, LoginWithOtpType, LoginWithPasswordType, OtpVerifyType, RegisterType } from '../types/AuthTypes';
+
+export const useLoginWithPassword = () => {
+ return useMutation({
+ mutationFn: (variables: LoginWithPasswordType) => api.loginWithPassword(variables),
+ });
+};
+
+export const useLoginWithOtp = () => {
+ return useMutation({
+ mutationFn: (variables: LoginWithOtpType) => api.loginWithOtp(variables),
+ });
+};
+
+export const useOtpVerify = () => {
+ return useMutation({
+ mutationFn: (variables: OtpVerifyType) => api.otpVerify(variables),
+ });
+};
+
+export const useCheckHasAccount = () => {
+ return useMutation({
+ mutationFn: (variables: CheckHasAccountType) => api.checkHasAccount(variables),
+ });
+};
+
+export const useCheckHasAccountRegister = () => {
+ return useMutation({
+ mutationFn: (variables: CheckHasAccountPhoneType) => api.checkHasAccountRegister(variables),
+ });
+};
+
+export const useRegister = () => {
+ return useMutation({
+ mutationFn: (variables: RegisterType) => api.register(variables),
+ });
+};
\ No newline at end of file
diff --git a/src/pages/auth/service/AuthService.ts b/src/pages/auth/service/AuthService.ts
new file mode 100644
index 0000000..72934f1
--- /dev/null
+++ b/src/pages/auth/service/AuthService.ts
@@ -0,0 +1,39 @@
+import axios from "../../../config/axios";
+import {
+ CheckHasAccountPhoneType,
+ CheckHasAccountType,
+ LoginWithOtpType,
+ LoginWithPasswordType,
+ OtpVerifyType,
+ RegisterType,
+} from "../types/AuthTypes";
+
+export const loginWithPassword = async (params: LoginWithPasswordType) => {
+ const { data } = await axios.post(`/auth/login/password/admin`, params);
+ return data;
+};
+
+export const loginWithOtp = async (params: LoginWithOtpType) => {
+ const { data } = await axios.post(`/auth/otp/send`, params);
+ return data;
+};
+
+export const otpVerify = async (params: OtpVerifyType) => {
+ const { data } = await axios.post(`/auth/otp/verify/admin`, params);
+ return data;
+};
+export const checkHasAccount = async (params: CheckHasAccountType) => {
+ const { data } = await axios.post(`/auth/check`, params);
+ return data;
+};
+
+export const checkHasAccountRegister = async (
+ params: CheckHasAccountPhoneType
+) => {
+ const { data } = await axios.post(`/auth/register/initiate`, params);
+ return data;
+};
+export const register = async (params: RegisterType) => {
+ const { data } = await axios.post(`/auth/register/complete`, params);
+ return data;
+};
diff --git a/src/pages/auth/store/AuthStore.ts b/src/pages/auth/store/AuthStore.ts
index a6cb566..de766e3 100644
--- a/src/pages/auth/store/AuthStore.ts
+++ b/src/pages/auth/store/AuthStore.ts
@@ -6,6 +6,10 @@ export const useAuthStore = create((set) => ({
setPhone(value) {
set({ phone: value });
},
+ email: "",
+ setEmail(value) {
+ set({ email: value });
+ },
stepLogin: 1,
setStepLogin(value) {
set({ stepLogin: value });
diff --git a/src/pages/auth/types/AuthTypes.ts b/src/pages/auth/types/AuthTypes.ts
index d3522b3..dc263bc 100644
--- a/src/pages/auth/types/AuthTypes.ts
+++ b/src/pages/auth/types/AuthTypes.ts
@@ -1,3 +1,5 @@
+import { XOR } from "../../../helpers/types";
+
export type LoginType = {
phone_email: string;
};
@@ -8,6 +10,42 @@ export type LoginPasswordType = {
export type AuthStoreType = {
phone: string;
setPhone: (value: string) => void;
+ email: string;
+ setEmail: (value: string) => void;
stepLogin: number;
setStepLogin: (value: number) => void;
};
+
+export type LoginWithPasswordType = XOR<
+ { email: string },
+ { phone: number }
+> & {
+ password: string;
+};
+
+export type LoginWithOtpType = {
+ phone: string;
+};
+
+export type OtpVerifyType = {
+ phone: string;
+ code: string;
+};
+
+export type CheckHasAccountType = {
+ email: string;
+};
+
+export type CheckHasAccountPhoneType = {
+ phone: string;
+};
+
+export type RegisterType = {
+ phone: string;
+ firstName: string;
+ lastName: string;
+ birthDate: string;
+ nationalCode: string;
+ password: string;
+ code: number;
+};
diff --git a/src/pages/service/Category.tsx b/src/pages/service/Category.tsx
index d1514f2..fb3b7e7 100644
--- a/src/pages/service/Category.tsx
+++ b/src/pages/service/Category.tsx
@@ -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('')
+ const [page, setPage] = useState(1)
+ const [isActive, setIsActive] = useState<'active' | 'deactive' | ''>('')
+ const getCategory = useGetAllCategory(page, search, isActive)
return (
@@ -24,11 +31,12 @@ const Category: FC = () => {
@@ -37,98 +45,70 @@ const Category: FC = () => {
variant='search'
placeholder={t('search')}
className='bg-white'
+ onChangeSearchFinal={(value) => setSearch(value)}
/>
-
-
-
-
- |
- |
- |
- |
- |
- |
- |
-
-
-
-
- |
-
- |
- |
- |
- |
- |
-
- null}
- />
- |
- |
-
-
-
-
+ {
+ getCategory.isPending ?
+
+ :
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ {
+ getCategory.data?.data?.categories?.map((item: ServiceCategoryType) => {
+ return (
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+
+ )
+ })
+ }
+
+
+
+
+ }
+
+