fix: seperated auth pages

This commit is contained in:
Mahyar Khanbolooki
2025-08-21 20:24:31 +03:30
parent 3cb48023b6
commit ef91469766
7 changed files with 787 additions and 195 deletions
+251
View File
@@ -0,0 +1,251 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import StepEnterOtp from '@/features/auth/components/StepEnterOtp'
import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'
import { useCountdown } from '@/hooks/useCountdown'
import StepNewPassword from '@/features/auth/components/StepNewPassword'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useOtpRequest } from '@/hooks/auth/useOtpRequest'
import { useOtpValidation } from '@/hooks/auth/useOtpValidation'
import { useResetPassword } from '@/hooks/auth/useResetPassword'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const user_auth_store = useAuthStore(state => state.user)
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const [number, setNumber] = useState(
user_temp_store?.number ?? user_auth_store?.number ?? ''
)
const [password, setPassword] = useState('')
const [passwordRepeat, setPasswordRepeat] = useState('')
const [otp, setOtp] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [showPasswordRepeat, setShowPasswordRepeat] = useState(false)
const [step, setStep] = useState(
number == '' ? AUTH_STEP.ENTER_NUMBER : AUTH_STEP.ENTER_OTP
)
const { timerRunning, secondsLeft, restart, stop } = useCountdown(
step === AUTH_STEP.ENTER_OTP
)
const [isPending, startTransition] = useTransition()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const { run: runOtpRequest } = useOtpRequest()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { run: runOtpValidation } = useOtpValidation()
const { run: runResetPassword } = useResetPassword()
const router = useRouter()
useEffect(() => {
if (isAuthenticated) {
redirect('/')
}
}, [isAuthenticated])
useEffect(() => {
if (step === AUTH_STEP.ENTER_OTP) {
startTransition(() => {
setOtp('')
console.log('REQUEST OTP')
runOtpRequest(number)
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) {
setPasswordRepeat(() => e.target.value)
} else if (e.target.name.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) {
const index = +e.target.name.split('-')[2]
const prev = otp.padEnd(6).split('')
prev[index] = e.target.value
const next = prev.join('')
if (otp.length >= 6 && next.trim().length >= otp.length) return
setOtp(next.trimEnd())
}
}
const onClick = async (
e:
| React.MouseEvent<HTMLButtonElement, MouseEvent>
| React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
e.stopPropagation()
const target = e.target as HTMLButtonElement
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword(state => !state)
}
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) {
setShowPasswordRepeat(state => !state)
} else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) {
setShowPassword(false)
setShowPasswordRepeat(false)
setPassword('')
setPasswordRepeat('')
setStep(() => AUTH_STEP.ENTER_OTP)
} else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) {
if (!timerRunning) {
startTransition(async () => {
try {
await runOtpRequest(number)
restart()
} catch {
console.error('Could not ask for otp')
}
})
}
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* END OF DEV SECTION */
/* SECTION DETAILS */
/* if number is entered in the /auth page or is available in main auth store this page directly shows the OTP page */
/* Otherwise it shows the number page */
if (step == AUTH_STEP.ENTER_NUMBER) {
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else {
// TODO: error user not found
}
} else if (step == AUTH_STEP.ENTER_OTP) {
// if (!await runOtpValidation({ phone: number, otp })) {
// setOtp('');
// console.error('Wrong otp');
// }
stop()
console.log(await runUserExistCheck(number))
if (await runUserExistCheck(number)) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else {
// TODO: error user not found
}
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
update_temp_store({
password,
repeatPassword: passwordRepeat
})
try {
await runResetPassword({
phone: number,
newPassword: password,
otp: otp
})
console.log('Password changed')
redirect('/')
} catch (e) {
console.error('Could not reset password: ', e)
}
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
if (step == AUTH_STEP.ENTER_NUMBER) {
update_temp_store({
number
})
setStep(AUTH_STEP.ENTER_OTP)
} else if (step === AUTH_STEP.ENTER_OTP) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
update_temp_store({
password,
repeatPassword: passwordRepeat
})
try {
console.log('Password changed')
router.replace('/auth')
} catch (e) {
console.error('Could not reset password: ', e)
}
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{step === AUTH_STEP.ENTER_NUMBER && (
<StepEnterNumber
pending={isPending}
onChange={onChange}
value={number}
/>
)}
{step === AUTH_STEP.ENTER_OTP && (
<StepEnterOtp
pending={isPending}
onChange={onChange}
onClick={onClick}
phoneNumber={number}
value={otp}
timerRunning={timerRunning}
secondsLeft={secondsLeft}
/>
)}
{step === AUTH_STEP.ENTER_RESET_PASSWORD && (
<StepNewPassword
isReset
pending={isPending}
onChange={onChange}
onClick={onClick}
value={password}
repeatValue={passwordRepeat}
passwordVisible={showPassword}
repeatPasswordVisible={showPasswordRepeat}
/>
)}
</AuthFormWrapper>
)
}
export default AuthIndex
+169
View File
@@ -0,0 +1,169 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useRouter } from 'next/navigation'
import StepEnterPassword from '@/features/auth/components/StepEnterPassword'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useLogin } from '@/hooks/auth/useLogin'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const [number, setNumber] = useState(user_temp_store?.number ?? '')
const [password, setPassword] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [rememberMe, setRememberMe] = useState(false)
const [step, setStep] = useState(
number == '' ? AUTH_STEP.ENTER_NUMBER : AUTH_STEP.ENTER_CURRENT_PASSWORD
)
const [isPending, startTransition] = useTransition()
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const loginMutation = useLogin()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const router = useRouter()
useEffect(() => {
if (isAuthenticated) {
redirect('/')
}
}, [isAuthenticated])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_REMEMBER_ME) {
setRememberMe(state => !state)
}
}
const onClick = async (
e:
| React.MouseEvent<HTMLButtonElement, MouseEvent>
| React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
e.stopPropagation()
const target = e.target as HTMLButtonElement
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword(state => !state)
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* END OF DEV SECTION */
/* SECTION DETAILS */
/* if number is entered in the /auth page this page directly shows the password page */
/* Otherwise it shows the number page */
if (step == AUTH_STEP.ENTER_NUMBER) {
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
setStep(AUTH_STEP.ENTER_CURRENT_PASSWORD)
} else {
router.replace('/auth/signup')
}
} else if (
step == AUTH_STEP.ENTER_OTP ||
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_RESET_PASSWORD
) {
setStep(AUTH_STEP.ENTER_CURRENT_PASSWORD)
} else if (step == AUTH_STEP.ENTER_CURRENT_PASSWORD) {
update_temp_store({
password,
rememberMe
})
try {
await loginMutation.mutateAsync({ phone: number, password })
console.log('Signed in')
redirect('/')
} catch (e) {
console.error('Could not Login: ', e)
}
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
if (step == AUTH_STEP.ENTER_NUMBER) {
update_temp_store({
number,
mustLogin: !number.endsWith('0')
})
setStep(AUTH_STEP.ENTER_CURRENT_PASSWORD)
} else if (
step == AUTH_STEP.ENTER_OTP ||
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_RESET_PASSWORD
) {
setStep(AUTH_STEP.ENTER_CURRENT_PASSWORD)
} else if (step == AUTH_STEP.ENTER_CURRENT_PASSWORD) {
update_temp_store({
password,
rememberMe
})
try {
console.log('Signed in')
redirect('/')
} catch (e) {
console.error('Could not Login: ', e)
}
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{step === AUTH_STEP.ENTER_NUMBER && (
<StepEnterNumber
pending={isPending}
onChange={onChange}
value={number}
/>
)}
{step === AUTH_STEP.ENTER_CURRENT_PASSWORD && (
<StepEnterPassword
pending={isPending}
onChange={onChange}
onClick={onClick}
value={password}
passwordVisible={showPassword}
rememberMe={rememberMe}
/>
)}
</AuthFormWrapper>
)
}
export default AuthIndex
+78 -190
View File
@@ -1,207 +1,95 @@
'use client';
'use client'
import React, { useState, type FormEvent, type ChangeEvent, useEffect, useTransition } from 'react'
import { useAuthStore } from '@/zustand/authStore';
import { redirect } from 'next/navigation';
import StepEnterPassword from '@/features/auth/components/StepEnterPassword';
import StepEnterNumber from '@/features/auth/components/StepEnterNumber';
import StepEnterOtp from '@/features/auth/components/StepEnterOtp';
import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums';
import { useCountdown } from '@/hooks/useCountdown';
import StepNewPassword from '@/features/auth/components/StepNewPassword';
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper';
import { useLogin } from '@/hooks/auth/useLogin';
import { useSignup } from '@/hooks/auth/useSignup';
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists';
import { useOtpRequest } from '@/hooks/auth/useOtpRequest';
import { useOtpValidation } from '@/hooks/auth/useOtpValidation';
import { useResetPassword } from '@/hooks/auth/useResetPassword';
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import { AUTH_PAGE_ELEMENT } from '@/enums'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex({ }: Props) {
const [number, setNumber] = useState("");
const [password, setPassword] = useState("");
const [passwordRepeat, setPasswordRepeat] = useState("");
const [otp, setOtp] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [showPasswordRepeat, setShowPasswordRepeat] = useState(false);
const [rememberMe, setRememberMe] = useState(false);
const [step, setStep] = useState(AUTH_STEP.ENTER_NUMBER);
const { timerRunning, secondsLeft, restart, stop } = useCountdown(step === AUTH_STEP.ENTER_OTP);
const [isPending, startTransition] = useTransition();
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const [number, setNumber] = useState(user_temp_store?.number ?? '')
const [isPending, startTransition] = useTransition()
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
const loginMutation = useLogin()
const signupMutation = useSignup()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const { run: runOtpRequest } = useOtpRequest();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { run: runOtpValidation } = useOtpValidation();
const { run: runResetPassword } = useResetPassword();
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const resetStates = () => {
setNumber('');
setPassword('');
setPasswordRepeat('');
setOtp('');
setShowPassword(false);
setShowPasswordRepeat(false);
setRememberMe(false);
stop();
const router = useRouter()
useEffect(() => {
if (isAuthenticated) {
redirect('/')
}
}, [isAuthenticated])
useEffect(() => {
if (isAuthenticated) {
redirect("/");
}
}, [isAuthenticated])
useEffect(() => {
if (step === AUTH_STEP.ENTER_OTP) {
startTransition(() => {
setOtp('');
console.log("REQUEST OTP")
runOtpRequest(number);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step]);
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
}
else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
}
else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) {
setPasswordRepeat(() => e.target.value)
}
else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_REMEMBER_ME) {
setRememberMe((state) => !state)
}
else if (e.target.name.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) {
const index = +e.target.name.split('-')[2];
const prev = otp.padEnd(6).split('');
prev[index] = e.target.value;
const next = prev.join('');
if (otp.length >= 6 && next.trim().length >= otp.length) return;
setOtp(next.trimEnd());
}
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
}
}
const onClick = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.MouseEvent<HTMLInputElement, MouseEvent>) => {
e.stopPropagation();
const target = e.target as HTMLButtonElement;
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* EMD OF DEV SECTION */
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword((state) => !state);
/* SECTION DETAILS */
/* If user is already registered, go to login page */
/* Otherwise go to signup page */
/* We store auth data in a temp auth store so the next page must */
/* access the phone number entered in this page */
/* and if it doesnt, it will ask for the number again */
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
router.push('/auth/login')
} else {
router.push('/auth/signup')
}
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) {
setShowPasswordRepeat((state) => !state);
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
update_temp_store({
number: number,
mustLogin: !number.endsWith('0')
})
if (number.endsWith('0')) {
router.push('/auth/signup')
} else {
router.push('/auth/login')
}
else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) {
setShowPassword(false);
setShowPasswordRepeat(false);
setPassword('');
setPasswordRepeat('');
setStep(() => AUTH_STEP.ENTER_OTP)
}
else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) {
if (!timerRunning) {
startTransition(async () => {
try {
await runOtpRequest(number);
restart();
} catch {
console.error("Could not ask for otp")
}
});
}
}
};
/* END OF DEV SECTION */
}
})
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
startTransition(async () => {
try {
if (step == AUTH_STEP.ENTER_NUMBER) {
if (await runUserExistCheck(number)) {
setStep(AUTH_STEP.ENTER_PASSWORD)
} else {
setStep(AUTH_STEP.ENTER_OTP);
}
}
else if (step == AUTH_STEP.ENTER_PASSWORD) {
try {
await loginMutation.mutateAsync({ phone: number, password })
console.log("Logged in")
redirect("/")
}
catch (e) {
console.error("Wrong credentials: ", e)
}
}
else if (step == AUTH_STEP.ENTER_OTP) {
// if (!await runOtpValidation({ phone: number, otp })) {
// setOtp('');
// console.error('Wrong otp');
// }
stop();
console.log(await runUserExistCheck(number));
if (await runUserExistCheck(number)) {
console.log("1")
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else {
console.log("2")
setStep(AUTH_STEP.ENTER_NEW_PASSWORD)
}
}
else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
console.log("Password changed")
try {
await runResetPassword({
phone: number,
newPassword: password,
otp
})
resetStates();
setStep(AUTH_STEP.ENTER_NUMBER)
} catch (ex) {
console.error('Password reset failed: ', ex)
}
}
else if (step == AUTH_STEP.ENTER_NEW_PASSWORD) {
try {
await signupMutation.mutateAsync({ phone: number, password })
console.log("Signed up")
redirect("/")
}
catch (e) {
console.error("Could not signup: ", e)
}
}
} catch (ex) {
console.error(ex)
}
});
}
const stepMap = {
[AUTH_STEP.ENTER_NUMBER]: <StepEnterNumber pending={isPending} onChange={onChange} value={number} />,
[AUTH_STEP.ENTER_PASSWORD]: <StepEnterPassword pending={isPending} onChange={onChange} onClick={onClick} value={password} passwordVisible={showPassword} rememberMe={rememberMe} />,
[AUTH_STEP.ENTER_OTP]: <StepEnterOtp pending={isPending} onChange={onChange} onClick={onClick} phoneNumber={number} value={otp} timerRunning={timerRunning} secondsLeft={secondsLeft} />,
[AUTH_STEP.ENTER_NEW_PASSWORD]: <StepNewPassword pending={isPending} onChange={onChange} onClick={onClick} value={password} repeatValue={passwordRepeat} passwordVisible={showPassword} repeatPasswordVisible={showPasswordRepeat} />,
[AUTH_STEP.ENTER_RESET_PASSWORD]: <StepNewPassword isReset pending={isPending} onChange={onChange} onClick={onClick} value={password} repeatValue={passwordRepeat} passwordVisible={showPassword} repeatPasswordVisible={showPasswordRepeat} />,
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{stepMap[step] || 'say what now?'}
</AuthFormWrapper>
)
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
<StepEnterNumber pending={isPending} onChange={onChange} value={number} />
</AuthFormWrapper>
)
}
export default AuthIndex
+241
View File
@@ -0,0 +1,241 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import StepEnterOtp from '@/features/auth/components/StepEnterOtp'
import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'
import { useCountdown } from '@/hooks/useCountdown'
import StepNewPassword from '@/features/auth/components/StepNewPassword'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useSignup } from '@/hooks/auth/useSignup'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useOtpRequest } from '@/hooks/auth/useOtpRequest'
import { useOtpValidation } from '@/hooks/auth/useOtpValidation'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const [number, setNumber] = useState(user_temp_store?.number ?? '')
const [password, setPassword] = useState('')
const [passwordRepeat, setPasswordRepeat] = useState('')
const [otp, setOtp] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [showPasswordRepeat, setShowPasswordRepeat] = useState(false)
const [step, setStep] = useState(
number == '' ? AUTH_STEP.ENTER_NUMBER : AUTH_STEP.ENTER_OTP
)
const { timerRunning, secondsLeft, restart, stop } = useCountdown(
step === AUTH_STEP.ENTER_OTP
)
const [isPending, startTransition] = useTransition()
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const signupMutation = useSignup()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const { run: runOtpRequest } = useOtpRequest()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { run: runOtpValidation } = useOtpValidation()
const router = useRouter()
useEffect(() => {
if (isAuthenticated) {
redirect('/')
}
}, [isAuthenticated])
useEffect(() => {
if (step === AUTH_STEP.ENTER_OTP) {
startTransition(() => {
setOtp('')
console.log('REQUEST OTP')
runOtpRequest(number)
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) {
setPasswordRepeat(() => e.target.value)
} else if (e.target.name.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) {
const index = +e.target.name.split('-')[2]
const prev = otp.padEnd(6).split('')
prev[index] = e.target.value
const next = prev.join('')
if (otp.length >= 6 && next.trim().length >= otp.length) return
setOtp(next.trimEnd())
}
}
const onClick = async (
e:
| React.MouseEvent<HTMLButtonElement, MouseEvent>
| React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
e.stopPropagation()
const target = e.target as HTMLButtonElement
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword(state => !state)
}
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) {
setShowPasswordRepeat(state => !state)
} else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) {
setShowPassword(false)
setShowPasswordRepeat(false)
setPassword('')
setPasswordRepeat('')
setStep(() => AUTH_STEP.ENTER_OTP)
} else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) {
if (!timerRunning) {
startTransition(async () => {
try {
await runOtpRequest(number)
restart()
} catch {
console.error('Could not ask for otp')
}
})
}
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* END OF DEV SECTION */
/* SECTION DETAILS */
/* if number is entered in the /auth page this page directly shows the OTP page */
/* Otherwise it shows the number page */
if (step == AUTH_STEP.ENTER_NUMBER) {
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
setStep(AUTH_STEP.ENTER_CURRENT_PASSWORD)
} else {
setStep(AUTH_STEP.ENTER_OTP)
}
} else if (step == AUTH_STEP.ENTER_OTP) {
// if (!await runOtpValidation({ phone: number, otp })) {
// setOtp('');
// console.error('Wrong otp');
// }
update_temp_store({
otp: otp
})
stop()
setStep(AUTH_STEP.ENTER_CREATE_PASSWORD)
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
update_temp_store({
password: password,
repeatPassword: passwordRepeat
})
try {
await signupMutation.mutateAsync({ phone: number, password })
console.log('Signed up')
redirect('/')
} catch (e) {
console.error('Could not signup: ', e)
}
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
if (step == AUTH_STEP.ENTER_NUMBER) {
update_temp_store({
number: number,
mustLogin: !number.endsWith('0')
})
setStep(AUTH_STEP.ENTER_OTP)
} else if (step == AUTH_STEP.ENTER_OTP) {
stop()
update_temp_store({
otp: otp
})
setStep(AUTH_STEP.ENTER_CREATE_PASSWORD)
} else if (step == AUTH_STEP.ENTER_CREATE_PASSWORD) {
update_temp_store({
password: password,
repeatPassword: passwordRepeat
})
try {
console.log('Signed up')
// redirect('/')
router.replace('/') // TODO: redirect to target menu page
} catch (e) {
console.error('Could not signup: ', e)
}
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{step === AUTH_STEP.ENTER_NUMBER && (
<StepEnterNumber
pending={isPending}
onChange={onChange}
value={number}
/>
)}
{step === AUTH_STEP.ENTER_OTP && (
<StepEnterOtp
pending={isPending}
onChange={onChange}
onClick={onClick}
phoneNumber={number}
value={otp}
timerRunning={timerRunning}
secondsLeft={secondsLeft}
/>
)}
{step === AUTH_STEP.ENTER_CREATE_PASSWORD && (
<StepNewPassword
isReset
pending={isPending}
onChange={onChange}
onClick={onClick}
value={password}
repeatValue={passwordRepeat}
passwordVisible={showPassword}
repeatPasswordVisible={showPasswordRepeat}
/>
)}
</AuthFormWrapper>
)
}
export default AuthIndex
+2 -2
View File
@@ -1,7 +1,7 @@
export enum AUTH_STEP {
ENTER_NUMBER = 0,
ENTER_PASSWORD = 1,
ENTER_CURRENT_PASSWORD = 1,
ENTER_OTP = 2,
ENTER_NEW_PASSWORD = 3,
ENTER_CREATE_PASSWORD = 3,
ENTER_RESET_PASSWORD = 4,
}
@@ -5,6 +5,7 @@ import { EyeToggleIcon } from '@/components/icons/EyeToggleIcon'
import InputField from '@/components/input/InputField'
import { AUTH_PAGE_ELEMENT } from '@/enums'
import Image from 'next/image'
import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -88,14 +89,14 @@ function StepEnterPassword ({
</button>
</InputField>
<div className='inline-flex justify-between items-center w-full pt-3'>
<button
<Link
href={'/auth/forgot-password'}
id={AUTH_PAGE_ELEMENT.RESET_PASSWORD}
type='button'
onClick={onClick}
className='text-sm2 cursor-pointer'
>
{t('EnterPass.ButtonRecover')}
</button>
</Link>
<div className='inline-flex justify-between items-center'>
{/* TODO: customize checkbox */}
+42
View File
@@ -0,0 +1,42 @@
// zustand/userStore.ts
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
type TempUser = {
number?: string
otp?: string
password?: string
repeatPassword?: string
previousPassword?: string
mustLogin?: boolean
rememberMe?: boolean
}
type AuthTempStore = {
user: TempUser | null
update: (userData: TempUser) => void
}
export const useAuthTempStore = create<AuthTempStore>()(
persist(
set => ({
user: null,
mustLogin: false,
update: userData => {
set(state => {
if (!userData) return state // no user to update
return {
user: {
...state.user,
...userData
}
}
})
}
}),
{
name: 'auth-temp-storage', // Key in localStorage
storage: createJSONStorage(() => sessionStorage)
}
)
)