221 lines
8.9 KiB
TypeScript
221 lines
8.9 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState, type FormEvent, type ChangeEvent, useEffect, useRef } from 'react'
|
||
import PrimaryButton from '@/components/button/PrimaryButton'
|
||
import { EyeToggleIcon } from '@/components/icons/EyeToggleIcon';
|
||
import InputField from '@/components/input/InputField';
|
||
import OTPInputField from '@/components/input/MultiInputField';
|
||
|
||
enum LOGIN_STEPS {
|
||
ENTER_NUMBER = 0,
|
||
ENTER_PASSWORD = 1,
|
||
ENTER_OTP = 2,
|
||
}
|
||
|
||
enum CLICK_ID {
|
||
TOGGLE_PASSWORD = "togglePassword",
|
||
RESET_PASSWORD = "resetPassword",
|
||
RESEND_OTP = "resendOtp"
|
||
}
|
||
|
||
type Props = object
|
||
|
||
function AuthIndex({ }: Props) {
|
||
const [number, setNumber] = useState("");
|
||
const [password, setPassword] = useState("");
|
||
const [otp, setOtp] = useState("");
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [rememberMe, setRememberMe] = useState(false);
|
||
const [step, setStep] = useState(LOGIN_STEPS.ENTER_NUMBER);
|
||
const [timerRunning, setTimerRunning] = useState(false);
|
||
const [secondLeft, setSecondsLeft] = useState(0);
|
||
const intervalRef = useRef<NodeJS.Timeout | null>(null); // ← useRef for interval
|
||
|
||
useEffect(() => {
|
||
if (timerRunning && intervalRef.current === null) {
|
||
intervalRef.current = setInterval(() => {
|
||
setSecondsLeft((state) => {
|
||
if (state <= 1) { // changed to <= 1 to prevent negative values
|
||
setTimerRunning(false);
|
||
clearInterval(intervalRef.current!);
|
||
intervalRef.current = null;
|
||
return 0;
|
||
}
|
||
return state - 1;
|
||
});
|
||
}, 1000);
|
||
}
|
||
|
||
return () => {
|
||
// Cleanup if component unmounts or timerRunning changes
|
||
if (intervalRef.current) {
|
||
clearInterval(intervalRef.current);
|
||
intervalRef.current = null;
|
||
}
|
||
};
|
||
}, [timerRunning]);
|
||
|
||
|
||
useEffect(() => {
|
||
if (step == LOGIN_STEPS.ENTER_OTP) {
|
||
setSecondsLeft(30);
|
||
setTimerRunning(true);
|
||
}
|
||
return () => {
|
||
|
||
}
|
||
}, [step])
|
||
|
||
const updateInput = (e: ChangeEvent<HTMLInputElement>) => {
|
||
if (e.target.id == "phone") {
|
||
setNumber(() => e.target.value)
|
||
}
|
||
else if (e.target.id == "password") {
|
||
setPassword(() => e.target.value)
|
||
}
|
||
else if (e.target.id == "rememberMe") {
|
||
setRememberMe((state) => !state)
|
||
}
|
||
else if (e.target.id.startsWith("otp-data")) {
|
||
const index = +e.target.id.split('-')[2];
|
||
const prev = otp.padEnd(6).split('');
|
||
prev[index] = e.target.value;
|
||
const next = prev.join('');
|
||
setOtp(next.trimEnd());
|
||
}
|
||
}
|
||
|
||
const onClickEvent = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||
e.stopPropagation();
|
||
const target = e.target as HTMLButtonElement;
|
||
|
||
if (target.id === CLICK_ID.TOGGLE_PASSWORD) {
|
||
setShowPassword((state) => !state);
|
||
}
|
||
else if (target.id === CLICK_ID.RESET_PASSWORD) {
|
||
setShowPassword((state) => !state);
|
||
}
|
||
else if (target.id === CLICK_ID.RESEND_OTP) {
|
||
setSecondsLeft(30);
|
||
setTimerRunning(true);
|
||
}
|
||
};
|
||
|
||
const submit = (e: FormEvent<HTMLFormElement>) => {
|
||
e.preventDefault();
|
||
console.log("STEP: ", step)
|
||
if (step == LOGIN_STEPS.ENTER_NUMBER) {
|
||
setStep(LOGIN_STEPS.ENTER_PASSWORD)
|
||
}
|
||
if (step == LOGIN_STEPS.ENTER_PASSWORD) {
|
||
setStep(LOGIN_STEPS.ENTER_OTP)
|
||
}
|
||
if (step == LOGIN_STEPS.ENTER_OTP) {
|
||
setStep(LOGIN_STEPS.ENTER_NUMBER)
|
||
setTimerRunning(false);
|
||
}
|
||
}
|
||
|
||
if (step == LOGIN_STEPS.ENTER_NUMBER) {
|
||
// First Step -> Enter number
|
||
return (
|
||
<form onSubmit={submit} className='p-6 h-full flex flex-col justify-between'>
|
||
<div className='block'>
|
||
<img src='/assets/images/login-banner.png' />
|
||
<div className='pt-4' dir='rtl'>
|
||
<h6 className='text-lg font-bold'>ورود به سیستم</h6>
|
||
<p className='mt-3 text-[13px]'>برای ورود شماره همراه خود را وارد نمایید.</p>
|
||
</div>
|
||
<InputField
|
||
className='mt-10'
|
||
htmlFor='phone'
|
||
labelText='شماره همراه'
|
||
value={number}
|
||
placeholder='09120000000'
|
||
onChange={updateInput}
|
||
type='number' />
|
||
</div>
|
||
<PrimaryButton type='submit'>بعدی</PrimaryButton>
|
||
</form>
|
||
)
|
||
}
|
||
|
||
if (step == LOGIN_STEPS.ENTER_PASSWORD) {
|
||
return (
|
||
<form onSubmit={submit} className='p-6 h-full flex flex-col justify-between'>
|
||
<div className='block'>
|
||
<img src='/assets/images/login-banner.png' />
|
||
<div className='pt-4' dir='rtl'>
|
||
<h6 className='text-lg font-bold'>ورود به سیستم</h6>
|
||
<p className='mt-3 text-[13px]'>کلمه عبور خود را وارد نمایید.</p>
|
||
</div>
|
||
<InputField
|
||
className='mt-10'
|
||
htmlFor='password'
|
||
labelText='کلمه عبور'
|
||
value={password}
|
||
placeholder=''
|
||
onChange={updateInput}
|
||
type={showPassword ? 'text' : 'password'}>
|
||
<button className='ps-3' id={CLICK_ID.TOGGLE_PASSWORD} type='button' onClick={onClickEvent}>
|
||
<EyeToggleIcon slash={showPassword} className="pointer-events-none" />
|
||
</button>
|
||
</InputField>
|
||
<div className='inline-flex justify-between items-center w-full pt-3'>
|
||
<button id={CLICK_ID.RESET_PASSWORD} type='button' onClick={onClickEvent} className='text-sm2 cursor-pointer'>بازیابی کلمه عبور</button>
|
||
|
||
<div className='inline-flex justify-between items-center'>
|
||
{/* TODO: customize checkbox */}
|
||
<label htmlFor='rememberMe' className='text-sm2 px-2 py-0.5'>مرا به خاطر بسپار</label>
|
||
<input id='rememberMe' className='h-4.5 w-4.5 checked:accent-primary' onChange={updateInput} type='checkbox' checked={rememberMe} /> </div>
|
||
</div>
|
||
</div>
|
||
<PrimaryButton type='submit'>ورود به منو</PrimaryButton>
|
||
</form>
|
||
)
|
||
}
|
||
|
||
if (step == LOGIN_STEPS.ENTER_OTP) {
|
||
return (
|
||
<form onSubmit={submit} className='p-6 h-full flex flex-col justify-between'>
|
||
<div className='block'>
|
||
<img src='/assets/images/login-banner.png' />
|
||
<div className='pt-4' dir='rtl'>
|
||
{/* // TODO: add persian digits font */}
|
||
<h6 className='text-lg font-bold'>کد فعالسازی را وارد کنید</h6>
|
||
<p className='mt-3 text-[13px]'>کد ۶ رقمی ارسال شده به شماره {number} را وارد نمایید.</p>
|
||
</div>
|
||
<OTPInputField
|
||
className='mt-10'
|
||
maxLength={6}
|
||
htmlFor='otp'
|
||
labelText=''
|
||
value={otp}
|
||
placeholder=''
|
||
onChange={updateInput}
|
||
type='text'>
|
||
|
||
</OTPInputField>
|
||
<div className='inline-flex justify-center items-center w-full pt-6 text-xs'>
|
||
<div dir='rtl'>
|
||
{timerRunning ?
|
||
<div>{secondLeft} ثانیه دیگر تا دریافت کد</div> :
|
||
<div>
|
||
کد را دریافت نکردید؟
|
||
<span className='text-primary px-1'>
|
||
<button id={CLICK_ID.RESEND_OTP} className='cursor-pointer' onClick={onClickEvent}>
|
||
دوباره امتحان کنید
|
||
</button>
|
||
</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<PrimaryButton type='submit'>بعدی</PrimaryButton>
|
||
</form>
|
||
)
|
||
}
|
||
}
|
||
|
||
export default AuthIndex |