atlp-rwanda/hackers-ec-Fe

View on GitHub
src/pages/auth/TwoFactor.tsx

Summary

Maintainability
C
1 day
Test Coverage
A
91%
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;