Files
dmenu-plus-front/src/app/auth/page.tsx
T
2025-06-30 21:17:35 +03:30

221 lines
8.9 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.
'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