fix: seperated auth pages
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user