admin login
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
VITE_BASE_URL = 'https://api.shinan.ir'
|
||||||
|
VITE_TOKEN_NAME = 'sh_admin_token'
|
||||||
|
VITE_REFRESH_TOKEN_NAME = 'sh_admin_token'
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import LogoImage from '../../assets/images/logo.svg'
|
import LogoImage from '../../assets/images/logo.svg'
|
||||||
import LogoSmallImage from '../../assets/images/logo-small.svg'
|
import LogoSmallImage from '../../assets/images/logo-small.svg'
|
||||||
import { useAuthStore } from './store/AuthStore'
|
|
||||||
import LoginStep1 from './components/LoginStep1'
|
import LoginStep1 from './components/LoginStep1'
|
||||||
import LoginStep2 from './components/LoginStep2'
|
|
||||||
import LoginStep3 from './components/LoginStep3'
|
|
||||||
|
|
||||||
const Login: FC = () => {
|
const Login: FC = () => {
|
||||||
|
|
||||||
const { stepLogin, phone, email } = useAuthStore()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
||||||
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
||||||
@@ -18,21 +13,9 @@ const Login: FC = () => {
|
|||||||
<img src={LogoSmallImage} className='w-8' />
|
<img src={LogoSmallImage} className='w-8' />
|
||||||
|
|
||||||
<div className='flex flex-1 flex-col h-full justify-center'>
|
<div className='flex flex-1 flex-col h-full justify-center'>
|
||||||
{
|
<LoginStep1 />
|
||||||
stepLogin === 1 ?
|
|
||||||
<LoginStep1 />
|
|
||||||
:
|
|
||||||
stepLogin === 2 && !!phone ?
|
|
||||||
<LoginStep2 />
|
|
||||||
: stepLogin === 2 && !!email ?
|
|
||||||
<LoginStep3 />
|
|
||||||
: stepLogin === 3 ?
|
|
||||||
<LoginStep3 />
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
import RegisterStep1 from './components/RegisterStep1'
|
import RegisterStep1 from './components/RegisterStep1'
|
||||||
import { useAuthStore } from './store/AuthStore'
|
// import RegisterStep2 from './components/RegisterStep2' // Not used in current implementation
|
||||||
import RegisterStep2 from './components/RegisterStep2'
|
|
||||||
|
|
||||||
|
|
||||||
const Register: FC = () => {
|
const Register: FC = () => {
|
||||||
|
|
||||||
const { stepLogin } = useAuthStore()
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
||||||
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
||||||
@@ -27,15 +23,10 @@ const Register: FC = () => {
|
|||||||
</h2>
|
</h2>
|
||||||
<p className='text-description text-sm mt-2'>
|
<p className='text-description text-sm mt-2'>
|
||||||
{t('auth.enter_information')}
|
{t('auth.enter_information')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
<RegisterStep1 />
|
||||||
stepLogin === 1 ?
|
|
||||||
<RegisterStep1 />
|
|
||||||
:
|
|
||||||
<RegisterStep2 />
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className='mt-6 flex justify-center gap-2 items-center text-sm'>
|
<div className='mt-6 flex justify-center gap-2 items-center text-sm'>
|
||||||
<p className='text-description'>
|
<p className='text-description'>
|
||||||
|
|||||||
@@ -1,55 +1,50 @@
|
|||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import Input from '../../../components/Input'
|
import Input from '../../../components/Input'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { LoginType } from '../types/AuthTypes'
|
import { type LoginWithPasswordType } from '../types/AuthTypes'
|
||||||
import { useFormik } from 'formik'
|
import { useFormik } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { useAuthStore } from '../store/AuthStore'
|
import { useAuthStore } from '../store/AuthStore'
|
||||||
import Error from '../../../components/Error'
|
import Error from '../../../components/Error'
|
||||||
import Button from '../../../components/Button'
|
import Button from '../../../components/Button'
|
||||||
import { isEmail } from '../../../config/func'
|
import { useLoginWithPassword } from '../hooks/useAuthData'
|
||||||
import { useCheckHasAccount, useLoginWithOtp } from '../hooks/useAuthData'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { type ErrorType } from '../../../helpers/types'
|
||||||
|
import { setRefreshToken, setToken } from '../../../config/func'
|
||||||
|
import { Pages } from '../../../config/Pages'
|
||||||
|
|
||||||
const LoginStep1: FC = () => {
|
const LoginStep1: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { setPhone, phone, setStepLogin, setEmail } = useAuthStore()
|
const { setEmail } = useAuthStore()
|
||||||
const loginWithOtp = useLoginWithOtp()
|
const loginWithPassword = useLoginWithPassword()
|
||||||
const checkHasAccount = useCheckHasAccount()
|
|
||||||
|
|
||||||
const formik = useFormik<LoginType>({
|
const formik = useFormik<LoginWithPasswordType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
phone_email: phone,
|
email: '',
|
||||||
|
password: '',
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
phone_email: Yup.string()
|
email: Yup.string()
|
||||||
|
.email(t('errors.invalid_email'))
|
||||||
|
.required(t('errors.required')),
|
||||||
|
password: Yup.string()
|
||||||
.required(t('errors.required'))
|
.required(t('errors.required'))
|
||||||
}),
|
}),
|
||||||
onSubmit(values) {
|
onSubmit(values) {
|
||||||
if (isEmail(values.phone_email)) {
|
setEmail(values.email)
|
||||||
setEmail(values.phone_email)
|
loginWithPassword.mutate(values, {
|
||||||
checkHasAccount.mutate({ email: values.phone_email }, {
|
onSuccess(data) {
|
||||||
onSuccess() {
|
if (data?.results?.accessToken?.token && data?.results?.refreshToken?.token) {
|
||||||
setStepLogin(2)
|
setToken(data.results.accessToken.token)
|
||||||
},
|
setRefreshToken(data.results.refreshToken.token)
|
||||||
onError(error: ErrorType) {
|
window.location.href = Pages.dashboard
|
||||||
toast.error(error?.response?.data?.error?.message[0])
|
}
|
||||||
},
|
},
|
||||||
})
|
onError(error: ErrorType) {
|
||||||
} else {
|
toast.error(error?.response?.data?.error?.message[0])
|
||||||
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])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -64,32 +59,49 @@ const LoginStep1: FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-16'>
|
<div className='mt-16 space-y-6'>
|
||||||
<Input
|
<Input
|
||||||
label={t('auth.mobile_or_email')}
|
label={t('auth.email')}
|
||||||
placeholder={t('auth.enter_mobile_or_email')}
|
placeholder={t('auth.enter_email')}
|
||||||
type='text'
|
type='email'
|
||||||
className='text-right'
|
className='text-right'
|
||||||
name='phone_email'
|
name='email'
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
value={formik.values.phone_email}
|
value={formik.values.email}
|
||||||
|
error_text={formik.touched.email && formik.errors.email ? formik.errors.email : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('auth.password')}
|
||||||
|
placeholder={t('auth.enter_password')}
|
||||||
|
type='password'
|
||||||
|
className='text-right'
|
||||||
|
name='password'
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.password}
|
||||||
|
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
formik.touched.phone_email && formik.errors.phone_email &&
|
formik.touched.email && formik.errors.email &&
|
||||||
<Error
|
<Error
|
||||||
errorText={formik.errors.phone_email}
|
errorText={formik.errors.email}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
formik.touched.password && formik.errors.password &&
|
||||||
|
<Error
|
||||||
|
errorText={formik.errors.password}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={t('auth.next')}
|
label={t('auth.login')}
|
||||||
className='mt-8'
|
className='mt-8'
|
||||||
onClick={() => formik.handleSubmit()}
|
onClick={() => formik.handleSubmit()}
|
||||||
isLoading={loginWithOtp.isPending || checkHasAccount.isPending}
|
isLoading={loginWithPassword.isPending}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
import { FC } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
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 { Pages } from '../../../config/Pages'
|
|
||||||
import { useOtpVerify } from '../hooks/useAuthData'
|
|
||||||
import { ErrorType } from '../../../helpers/types'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
import { setToken, setRefreshToken } from '../../../config/func'
|
|
||||||
|
|
||||||
const LoginStep2: FC = () => {
|
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
|
||||||
const { phone, setStepLogin } = useAuthStore()
|
|
||||||
const { value } = useCountDown(2, true)
|
|
||||||
const otpVerify = useOtpVerify()
|
|
||||||
|
|
||||||
const formik = useFormik<OtpVerifyType>({
|
|
||||||
initialValues: {
|
|
||||||
phone: phone,
|
|
||||||
code: ''
|
|
||||||
},
|
|
||||||
validationSchema: Yup.object({
|
|
||||||
code: Yup.string()
|
|
||||||
.required(t('errors.required'))
|
|
||||||
}),
|
|
||||||
onSubmit(values) {
|
|
||||||
const params: OtpVerifyType = {
|
|
||||||
phone: phone,
|
|
||||||
code: values.code
|
|
||||||
}
|
|
||||||
otpVerify.mutate(params, {
|
|
||||||
onSuccess(data) {
|
|
||||||
setToken(data?.data?.accessToken?.token)
|
|
||||||
setRefreshToken(data?.data?.refreshToken?.token)
|
|
||||||
window.location.href = Pages.dashboard
|
|
||||||
},
|
|
||||||
onError(error: ErrorType) {
|
|
||||||
toast.error(error?.response?.data?.error?.message[0])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className='mt-5'>
|
|
||||||
<h2 className='lg:text-2xl font-bold'>
|
|
||||||
{t('auth.enter_otp')}
|
|
||||||
</h2>
|
|
||||||
<p className='text-description flex lg:flex-row flex-col lg:gap-1 gap-2 text-sm lg:mt-2 mt-3'>
|
|
||||||
<div className='flex gap-1'>
|
|
||||||
<div>{t('auth.verfify_code_text_1')}</div>
|
|
||||||
<div>{phone}</div>
|
|
||||||
<div>
|
|
||||||
{t('auth.verfify_code_text_2')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div onClick={() => setStepLogin(1)} className='text-black cursor-pointer'>
|
|
||||||
{t('auth.change_number')}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-16'>
|
|
||||||
<div className='text-sm'>
|
|
||||||
{t('auth.verify_code')}
|
|
||||||
</div>
|
|
||||||
<div className='mt-2 w-full flex justify-center dltr otp'>
|
|
||||||
<OTPInput
|
|
||||||
shouldAutoFocus
|
|
||||||
value={formik.values.code}
|
|
||||||
numInputs={5}
|
|
||||||
inputType='tel'
|
|
||||||
onChange={(otp: string) => formik.setFieldValue('code', otp)}
|
|
||||||
renderInput={(props) => <input {...props} className='w-full h-[50px] flex-1 mx-2 bg-white border rounded-2.5' />}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
formik.touched.code && formik.errors.code &&
|
|
||||||
<div className='text-xs mt-2 text-red-500'>{formik.errors.code}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className='flex mt-4 justify-end'>
|
|
||||||
<div className='flex gap-1 text-description text-xs'>
|
|
||||||
<div>{value}</div>
|
|
||||||
<div>{t('auth.counter_otp')}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<Button
|
|
||||||
label={t('auth.login')}
|
|
||||||
className='mt-8'
|
|
||||||
onClick={() => formik.handleSubmit()}
|
|
||||||
isLoading={otpVerify.isPending}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* <div onClick={() => setStepLogin(3)} className='mt-6 cursor-pointer flex gap-2 items-center text-sm'>
|
|
||||||
<p className='text-description'>
|
|
||||||
{t('auth.login_with_password')}
|
|
||||||
</p>
|
|
||||||
<img src={ArrowLeftIcon} className='w-5' />
|
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginStep2
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import { FC } from 'react'
|
|
||||||
import Input from '../../../components/Input'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { LoginWithPasswordType } 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 { 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'
|
|
||||||
import { setRefreshToken } from '../../../config/func'
|
|
||||||
import { setToken } from '../../../config/func'
|
|
||||||
|
|
||||||
const LoginStep3: FC = () => {
|
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
|
||||||
const { setStepLogin, email } = useAuthStore()
|
|
||||||
const loginWithPassword = useLoginWithPassword()
|
|
||||||
|
|
||||||
const formik = useFormik<LoginWithPasswordType>({
|
|
||||||
initialValues: {
|
|
||||||
password: '',
|
|
||||||
email: email
|
|
||||||
},
|
|
||||||
validationSchema: Yup.object({
|
|
||||||
password: Yup.string()
|
|
||||||
.required(t('errors.required'))
|
|
||||||
}),
|
|
||||||
onSubmit(values) {
|
|
||||||
loginWithPassword.mutate(values, {
|
|
||||||
onSuccess(data) {
|
|
||||||
setToken(data?.data?.accessToken?.token)
|
|
||||||
setRefreshToken(data?.data?.refreshToken?.token)
|
|
||||||
window.location.href = Pages.dashboard
|
|
||||||
},
|
|
||||||
onError(error: ErrorType) {
|
|
||||||
toast.error(error?.response?.data?.error?.message[0])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className='mt-5'>
|
|
||||||
<h2 className='text-2xl font-bold'>
|
|
||||||
{t('auth.enter_password_step3')}
|
|
||||||
</h2>
|
|
||||||
<p className='text-description text-sm mt-2'>
|
|
||||||
{t('auth.enter_your_password')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-16'>
|
|
||||||
<Input
|
|
||||||
label={t('auth.password')}
|
|
||||||
type='password'
|
|
||||||
className='text-right'
|
|
||||||
name='password'
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
value={formik.values.password}
|
|
||||||
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
formik.touched.password && formik.errors.password &&
|
|
||||||
<Error
|
|
||||||
errorText={formik.errors.password}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<Link to={Pages.auth.forgotPassword}>
|
|
||||||
<div className='mt-4 flex justify-end text-sm'>
|
|
||||||
{t('auth.forgot_password')}
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
label={t('auth.login')}
|
|
||||||
className='mt-8'
|
|
||||||
onClick={() => formik.handleSubmit()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div onClick={() => setStepLogin(2)} className='mt-6 cursor-pointer flex gap-2 items-center text-sm'>
|
|
||||||
<p className='text-description'>
|
|
||||||
{t('auth.login_with_otp')}
|
|
||||||
</p>
|
|
||||||
<img src={ArrowLeftIcon} className='w-4' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginStep3
|
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import Input from '../../../components/Input'
|
import Input from '../../../components/Input'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { CheckHasAccountPhoneType } from '../types/AuthTypes'
|
import { type CheckHasAccountType } from '../types/AuthTypes'
|
||||||
import { useFormik } from 'formik'
|
import { useFormik } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { useAuthStore } from '../store/AuthStore'
|
import { useAuthStore } from '../store/AuthStore'
|
||||||
import Error from '../../../components/Error'
|
import Error from '../../../components/Error'
|
||||||
import Button from '../../../components/Button'
|
import Button from '../../../components/Button'
|
||||||
import { useCheckHasAccountRegister, useLoginWithOtp } from '../hooks/useAuthData'
|
import { useCheckHasAccount } from '../hooks/useAuthData'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { type ErrorType } from '../../../helpers/types'
|
||||||
|
|
||||||
const RegisterStep1: FC = () => {
|
const RegisterStep1: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { setPhone, setStepLogin } = useAuthStore()
|
const { setEmail } = useAuthStore()
|
||||||
const loginWithOtp = useLoginWithOtp()
|
const checkHasAccount = useCheckHasAccount()
|
||||||
const checkHasAccount = useCheckHasAccountRegister()
|
|
||||||
|
|
||||||
const formik = useFormik<CheckHasAccountPhoneType>({
|
const formik = useFormik<CheckHasAccountType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
phone: '',
|
email: '',
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
phone: Yup.string()
|
email: Yup.string()
|
||||||
|
.email(t('errors.invalid_email'))
|
||||||
.required(t('errors.required'))
|
.required(t('errors.required'))
|
||||||
}),
|
}),
|
||||||
onSubmit(values) {
|
onSubmit(values) {
|
||||||
checkHasAccount.mutate(values, {
|
checkHasAccount.mutate(values, {
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
setPhone(values.phone)
|
setEmail(values.email)
|
||||||
setStepLogin(2)
|
// Redirect to register step 2 or handle as needed
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast.error(error?.response?.data?.error?.message[0])
|
toast.error(error?.response?.data?.error?.message[0])
|
||||||
@@ -42,33 +42,31 @@ const RegisterStep1: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
|
|
||||||
|
|
||||||
<div className='mt-16'>
|
<div className='mt-16'>
|
||||||
<Input
|
<Input
|
||||||
label={t('auth.mobile_number')}
|
label={t('auth.email')}
|
||||||
placeholder={t('auth.enter_mobile_number')}
|
placeholder={t('auth.enter_email')}
|
||||||
type='tel'
|
type='email'
|
||||||
className='text-right'
|
className='text-right'
|
||||||
name='phone'
|
name='email'
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
value={formik.values.phone}
|
value={formik.values.email}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
formik.touched.phone && formik.errors.phone &&
|
formik.touched.email && formik.errors.email &&
|
||||||
<Error
|
<Error
|
||||||
errorText={formik.errors.phone}
|
errorText={formik.errors.email}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={t('auth.next')}
|
label={t('auth.next')}
|
||||||
className='mt-8'
|
className='mt-8'
|
||||||
onClick={() => formik.handleSubmit()}
|
onClick={() => formik.handleSubmit()}
|
||||||
isLoading={loginWithOtp.isPending || checkHasAccount.isPending}
|
isLoading={checkHasAccount.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { FC, Fragment } from 'react'
|
import { type FC, Fragment } from 'react'
|
||||||
import { useFormik } from 'formik'
|
import { useFormik } from 'formik'
|
||||||
import { RegisterType } from '../types/AuthTypes'
|
import { type RegisterType } from '../types/AuthTypes'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { useAuthStore } from '../store/AuthStore'
|
// import { useAuthStore } from '../store/AuthStore' // Not used in current implementation
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Input from '../../../components/Input'
|
import Input from '../../../components/Input'
|
||||||
import DatePickerComponent from '../../../components/DatePicker'
|
import DatePickerComponent from '../../../components/DatePicker'
|
||||||
import Button from '../../../components/Button'
|
import Button from '../../../components/Button'
|
||||||
import { useRegister } from '../hooks/useAuthData'
|
import { useRegister } from '../hooks/useAuthData'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { type ErrorType } from '../../../helpers/types'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { Pages } from '../../../config/Pages'
|
import { Pages } from '../../../config/Pages'
|
||||||
|
|
||||||
const RegisterStep2: FC = () => {
|
const RegisterStep2: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { phone } = useAuthStore()
|
// const { email } = useAuthStore() // Not used in current implementation
|
||||||
const register = useRegister()
|
const register = useRegister()
|
||||||
|
|
||||||
const formik = useFormik<RegisterType>({
|
const formik = useFormik<RegisterType>({
|
||||||
@@ -26,7 +26,7 @@ const RegisterStep2: FC = () => {
|
|||||||
code: 0,
|
code: 0,
|
||||||
nationalCode: '',
|
nationalCode: '',
|
||||||
password: '',
|
password: '',
|
||||||
phone: phone,
|
phone: '', // We'll need to add phone field to the form
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
firstName: Yup.string()
|
firstName: Yup.string()
|
||||||
@@ -105,10 +105,12 @@ const RegisterStep2: FC = () => {
|
|||||||
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('auth.phonen_number')}
|
label={t('auth.phone_number')}
|
||||||
placeholder={t('auth.enter_phone_number')}
|
placeholder={t('auth.enter_phone_number')}
|
||||||
readOnly
|
type='tel'
|
||||||
value={phone}
|
name='phone'
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import * as api from '../service/AuthService'
|
import * as api from '../service/AuthService'
|
||||||
import { CheckHasAccountPhoneType, CheckHasAccountType, LoginWithOtpType, LoginWithPasswordType, OtpVerifyType, RegisterType } from '../types/AuthTypes';
|
import { type CheckHasAccountType, type LoginWithPasswordType, type RegisterType } from '../types/AuthTypes';
|
||||||
|
|
||||||
export const useLoginWithPassword = () => {
|
export const useLoginWithPassword = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@@ -8,30 +8,12 @@ export const useLoginWithPassword = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = () => {
|
export const useCheckHasAccount = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (variables: CheckHasAccountType) => api.checkHasAccount(variables),
|
mutationFn: (variables: CheckHasAccountType) => api.checkHasAccount(variables),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCheckHasAccountRegister = () => {
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: (variables: CheckHasAccountPhoneType) => api.checkHasAccountRegister(variables),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRegister = () => {
|
export const useRegister = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (variables: RegisterType) => api.register(variables),
|
mutationFn: (variables: RegisterType) => api.register(variables),
|
||||||
|
|||||||
@@ -1,39 +1,21 @@
|
|||||||
import axios from "../../../config/axios";
|
import axios from "../../../config/axios";
|
||||||
import {
|
import {
|
||||||
type CheckHasAccountPhoneType,
|
|
||||||
type CheckHasAccountType,
|
type CheckHasAccountType,
|
||||||
type LoginWithOtpType,
|
|
||||||
type LoginWithPasswordType,
|
type LoginWithPasswordType,
|
||||||
type OtpVerifyType,
|
|
||||||
type RefreshTokenType,
|
type RefreshTokenType,
|
||||||
type RegisterType,
|
type RegisterType,
|
||||||
} from "../types/AuthTypes";
|
} from "../types/AuthTypes";
|
||||||
|
|
||||||
export const loginWithPassword = async (params: LoginWithPasswordType) => {
|
export const loginWithPassword = async (params: LoginWithPasswordType) => {
|
||||||
const { data } = await axios.post(`/auth/login/password/admin`, params);
|
const { data } = await axios.post(`/admin/login/password`, params);
|
||||||
return data;
|
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) => {
|
export const checkHasAccount = async (params: CheckHasAccountType) => {
|
||||||
const { data } = await axios.post(`/auth/check`, params);
|
const { data } = await axios.post(`/auth/check`, params);
|
||||||
return data;
|
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) => {
|
export const register = async (params: RegisterType) => {
|
||||||
const { data } = await axios.post(`/auth/register/complete`, params);
|
const { data } = await axios.post(`/auth/register/complete`, params);
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { AuthStoreType } from "../../auth/types/AuthTypes";
|
|
||||||
|
export type AuthStoreType = {
|
||||||
|
email: string;
|
||||||
|
setEmail: (value: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export const useAuthStore = create<AuthStoreType>((set) => ({
|
export const useAuthStore = create<AuthStoreType>((set) => ({
|
||||||
phone: "",
|
|
||||||
setPhone(value) {
|
|
||||||
set({ phone: value });
|
|
||||||
},
|
|
||||||
email: "",
|
email: "",
|
||||||
setEmail(value) {
|
setEmail(value) {
|
||||||
set({ email: value });
|
set({ email: value });
|
||||||
},
|
},
|
||||||
stepLogin: 1,
|
|
||||||
setStepLogin(value) {
|
|
||||||
set({ stepLogin: value });
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,45 +1,12 @@
|
|||||||
import { type XOR } from "../../../helpers/types";
|
export type LoginWithPasswordType = {
|
||||||
|
|
||||||
export type LoginType = {
|
|
||||||
phone_email: string;
|
|
||||||
};
|
|
||||||
export type LoginPasswordType = {
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AuthStoreType = {
|
|
||||||
phone: string;
|
|
||||||
setPhone: (value: string) => void;
|
|
||||||
email: string;
|
email: string;
|
||||||
setEmail: (value: string) => void;
|
|
||||||
stepLogin: number;
|
|
||||||
setStepLogin: (value: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LoginWithPasswordType = XOR<
|
|
||||||
{ email: string },
|
|
||||||
{ phone: number }
|
|
||||||
> & {
|
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LoginWithOtpType = {
|
|
||||||
phone: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type OtpVerifyType = {
|
|
||||||
phone: string;
|
|
||||||
code: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CheckHasAccountType = {
|
export type CheckHasAccountType = {
|
||||||
email: string;
|
email: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CheckHasAccountPhoneType = {
|
|
||||||
phone: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegisterType = {
|
export type RegisterType = {
|
||||||
phone: string;
|
phone: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -49,6 +16,7 @@ export type RegisterType = {
|
|||||||
password: string;
|
password: string;
|
||||||
code: number;
|
code: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RefreshTokenType = {
|
export type RefreshTokenType = {
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
};
|
};
|
||||||
|
|||||||
+6
-2
@@ -1,8 +1,12 @@
|
|||||||
import React from 'react'
|
import Login from '../pages/auth/Login'
|
||||||
|
import { Route, Routes } from 'react-router-dom'
|
||||||
|
import { Pages } from '../config/Pages'
|
||||||
|
|
||||||
const Auth = () => {
|
const Auth = () => {
|
||||||
return (
|
return (
|
||||||
<div>Auth</div>
|
<Routes>
|
||||||
|
<Route path={Pages.auth.login} element={<Login />} />
|
||||||
|
</Routes>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+786
-232
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
import { DocumentText } from 'iconsax-react'
|
import { DocumentText } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
@@ -14,7 +14,7 @@ const BlogSubMenu: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='py-12'>
|
<div className='py-12'>
|
||||||
<div className='flex gap-3 items-center border-b pb-10 px-6'>
|
<div className='flex gap-3 items-center border-b border-border pb-10 px-6'>
|
||||||
<DocumentText
|
<DocumentText
|
||||||
size={24}
|
size={24}
|
||||||
color='#000'
|
color='#000'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { People } from 'iconsax-react'
|
import { People } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Teacher } from 'iconsax-react'
|
import { Teacher } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Receipt21 } from 'iconsax-react'
|
import { Receipt21 } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Element3 } from 'iconsax-react'
|
import { Element3 } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { clx } from '../../helpers/utils'
|
import { clx } from '../../helpers/utils'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Headphone } from 'iconsax-react'
|
import { Headphone } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Messages3 } from 'iconsax-react'
|
import { Messages3 } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Card } from 'iconsax-react'
|
import { Card } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Profile } from 'iconsax-react'
|
import { Profile } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SubMenuItem from './SubMenuItem'
|
import SubMenuItem from './SubMenuItem'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
|||||||
Reference in New Issue
Block a user