Files
negareh-admin/src/pages/auth/components/LoginStep2.tsx
T
morteza 1e8579bbfb
deploy to danak / build_and_deploy (push) Has been cancelled
print form
2026-07-03 16:30:25 +03:30

207 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type FC, useEffect, useRef } from 'react'
import { type 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 { useLoginWithOtp, useOtpVerify } from '../hooks/useAuthData'
import { extractErrorMessage, setRefreshToken } from '../../../config/func'
import { setToken } from '../../../config/func'
import { toast } from '@/shared/toast'
import { Paths } from '@/config/Paths'
const LoginStep2: FC = () => {
const { phone, setStepLogin, devOtpCode, setDevOtpCode } = useAuthStore()
const { value, reset } = useCountDown(2, true)
const otpVerify = useOtpVerify()
const loginWithOtp = useLoginWithOtp()
const firstOtpInputRef = useRef<HTMLInputElement | null>(null)
const formik = useFormik<OtpVerifyType>({
initialValues: {
phone: phone,
// TODO: remove before production — dev-only OTP autofill
otp: devOtpCode,
},
validationSchema: Yup.object({
otp: Yup.string()
.required('این فیلد اجباری است.')
}),
onSubmit(values) {
const params: OtpVerifyType = {
phone: phone,
otp: values.otp
}
otpVerify.mutate(params, {
onSuccess(data) {
setToken(data?.data?.tokens?.accessToken?.token)
setRefreshToken(data?.data?.tokens?.refreshToken?.token)
// Check if there's a redirect parameter in the URL
window.location.href = Paths.home
},
onError(error) {
toast(extractErrorMessage(error), 'error')
},
})
},
})
useEffect(() => {
const frameId = requestAnimationFrame(() => {
firstOtpInputRef.current?.focus()
})
return () => cancelAnimationFrame(frameId)
}, [])
useEffect(() => {
// بررسی پشتیبانی از Web OTP API
if (!("OTPCredential" in window)) return;
const ac = new AbortController();
navigator.credentials
.get({
otp: { transport: ["sms"] },
signal: ac.signal,
} as CredentialRequestOptions) // اطمینان از تطابق تایپ‌ها
.then((otpCredential) => {
if (otpCredential && "otp" in otpCredential) {
formik.setFieldValue("otp", (otpCredential).otp);
}
})
.catch((err) => console.error("SMS AutoFill Error:", err));
return () => ac.abort();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const reSend = () => {
loginWithOtp.mutate({ phone: phone }, {
onSuccess: (data) => {
// TODO: remove before production — dev-only OTP autofill
const code = data?.data?.code
if (code) {
const otp = String(code)
setDevOtpCode(otp)
formik.setFieldValue('otp', otp)
}
reset()
toast('کد مجدد ارسال شد', 'success')
},
onError: (error) => {
toast(extractErrorMessage(error), 'error')
}
})
}
useEffect(() => {
if (formik.values.otp.length === 5) {
formik.handleSubmit()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formik.values.otp])
return (
<form onSubmit={formik.handleSubmit}>
<div className='mt-5'>
<h2 className='lg:text-2xl font-bold'>
کد تایید را وارد کنید
</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>کد تایید برای</div>
<div>{phone}</div>
<div>
ارسال شد.
</div>
</div>
<div onClick={() => setStepLogin(1)} className='text-black cursor-pointer'>
تغییر شماره
</div>
</p>
</div>
<div className='mt-16'>
<div className='text-sm'>
کد تایید
</div>
<div className='mt-2 w-full flex justify-center dltr otp'>
<OTPInput
shouldAutoFocus
value={formik.values.otp}
numInputs={5}
inputType='tel'
onChange={(otp: string) => formik.setFieldValue('otp', otp)}
renderInput={(props, index) => {
const { ref, ...inputProps } = props
return (
<input
{...inputProps}
ref={(element) => {
ref(element)
if (index === 0) {
firstOtpInputRef.current = element
}
}}
type='tel'
autoComplete="one-time-otp"
inputMode="numeric"
className='w-full h-[50px] min-w-[50px] flex-1 mx-2 bg-white border border-border rounded-[10px]' />
)
}}
/>
</div>
{
formik.touched.otp && formik.errors.otp &&
<div className='text-xs mt-2 text-red-500'>{formik.errors.otp}</div>
}
<div className='flex mt-4 justify-end'>
{
value !== '00:00' ?
<div className='flex gap-1 text-description text-xs'>
<div>{value}</div>
<div>مانده تا دریافت مجدد کد</div>
</div>
:
<div onClick={reSend} className='flex cursor-pointer gap-1 pl-2 text-black text-sm'>
<div className=''>
ارسال مجدد
</div>
</div>
}
</div>
</div>
<Button
label={'ورود'}
className='mt-8'
type='submit'
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> */}
</form>
)
}
export default LoginStep2