src/pages/auth/TwoFactor.tsx
import { zodResolver } from '@hookform/resolvers/zod';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { DynamicData } from '../../@types/DynamicData';
import { roundedLogo } from '../../utils/images';
import backgroundImage from '../../assets/register-login-background.svg';
import FormInput from '../../components/Forms/InputText';
import IconLoader from '../../components/Loaders/IconLoader';
import BackButton from '../../components/buttons/BackButton';
import Button from '../../components/buttons/Button';
import useToast from '../../hooks/useToast';
import useToken from '../../hooks/useToken';
import { verifyOTP } from '../../redux/features/OTPSlice';
import { useAppDispatch, useAppSelector } from '../../redux/hooks/hooks';
import { OTPSchema, OTPSchemaType } from '../../validations/OTP.validation';
const TwoFactorAuth = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const token = searchParams.get('token');
const dispatch = useAppDispatch();
const { isLoading } = useAppSelector((state) => state.otp);
const { showErrorMessage, showSuccessMessage } = useToast();
const { saveAccessToken } = useToken();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<OTPSchemaType>({ resolver: zodResolver(OTPSchema) });
const onSubmit: SubmitHandler<OTPSchemaType> = async (
data: OTPSchemaType,
) => {
if (!token) {
showErrorMessage('Invalid token. Please try again.');
return;
}
try {
const res = await dispatch(verifyOTP({ otp: data.otp, token })).unwrap();
saveAccessToken(res.data);
showSuccessMessage(res.message);
navigate('/dashboard');
} catch (e) {
const err = e as DynamicData;
showErrorMessage(
err?.data?.message ||
err?.message ||
'Unknown error occurred! Please try again!',
);
}
};
return (
<div
className="w-full h-screen relative flex items-center justify-center"
style={{
backgroundImage: `url(${backgroundImage})`,
backgroundPosition: 'center',
backgroundSize: 'cover',
}}
>
<div className="absolute top-0 left-0 bg-neutral-black/70 w-full h-full" />
<div className="absolute z-10 p-4 w-[90%] h-fit bg-neutral-white mx-8 px-6 py-6 rounded-3xl flex flex-col gap-2 items-center justify-center mobile:z-auto mobile:w-[77%] mobile:h-fit ipad:w-[50%] ipad:h-fit">
<div className="flex justify-center w-max h-max mb-8">
<img src={roundedLogo} alt="ShopTrove logo" className="w-[60%]" />
</div>
<h1 className="text-4xl mb-4 font-poppins font-semibold text-center tracking-tighter">
Two-Factor <br />
Authentication
</h1>
<p className="laptop:text-sm w-[60%] text-center">
Enter 6-digit code generated by our app sent to your email
</p>
<div className="flex flex-col gap-8 mobile:w-[60%]">
<form
className="flex flex-col gap-9"
onSubmit={handleSubmit(onSubmit)}
>
<FormInput
type="text"
placeholder="Enter OTP"
otherStyles="text-center bg-neutral-white border border-overlay w-full rounded-sm py-1 mobile:py-2 mb-1"
{...register('otp')}
error={errors.otp}
/>
<Button
url={null}
buttonType="submit"
color="bg-action-success"
otherStyles="w-full h-10 text-base rounded-3xl hover:bg-action-success/95"
title={
isLoading ? (
<>
<IconLoader className="animate-spin mr-1" /> {'Verifying'}
</>
) : (
'Verify'
)
}
/>
</form>
<BackButton
url="/"
otherStyles="
py-2 hover:bg-neutral-grey/20 rounded-3xl gap-4 mobile:text-sm
"
title="Return to site"
/>
</div>
</div>
</div>
);
};
export default TwoFactorAuth;