Vizzuality/landgriffon

View on GitHub
client/src/pages/auth/reset-password/[token].tsx

Summary

Maintainability
A
0 mins
Test Coverage
import Head from 'next/head';
import { useRouter } from 'next/router';
import { signIn } from 'next-auth/react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import toast from 'react-hot-toast';
import { useCallback, type ReactElement, useState } from 'react';

import AuthenticationLayout from 'layouts/authentication';
import { Label, Input } from 'components/forms';
import { Button } from 'components/button';
import { useResetPassword } from 'hooks/profile';

import type { NextPageWithLayout } from 'pages/_app';

const schemaValidation = yup.object({
  password: yup.string().min(8).required('password is required'),
  passwordConfirmation: yup
    .string()
    .required('password confirmation is required')
    .oneOf([yup.ref('password'), null], 'passwords must match'),
});

const ResetPassword: NextPageWithLayout = () => {
  const { replace, query } = useRouter();

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<yup.InferType<typeof schemaValidation>>({
    resolver: yupResolver(schemaValidation),
    shouldUseNativeValidation: false,
  });

  const { mutate: resetPassword, isLoading } = useResetPassword();
  const [isRedirecting, setIsRedirecting] = useState<boolean>(false);

  const handleResetPassword = useCallback(
    ({ password }: yup.InferType<typeof schemaValidation>) => {
      const token = query?.token as string;
      if (!token) {
        toast.error('Invalid token');
        return;
      }
      resetPassword(
        { password, token },
        {
          onSuccess: async ({ email }) => {
            // Automatically sign in after reset password
            const { ok, error } = await signIn('credentials', {
              email: email,
              username: email,
              password,
              redirect: false,
            });
            // Redirect to the callback url if it exists or to the analysis page
            if (ok) {
              setIsRedirecting(true);
              replace((query?.callbackUrl as string) || '/analysis/map', undefined, {
                shallow: true,
              });
            }
            if (error) {
              toast.error(error);
            }
          },
          onError: (error) => {
            toast.error(error.message);
          },
        },
      );
    },
    [replace, query?.callbackUrl, query?.token, resetPassword],
  );

  return (
    <>
      <Head>
        <title>Reset password - Landgriffon</title>
      </Head>
      <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
        <div className="bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
          <div className="mb-10 text-center">
            <h2 className="my-4 font-bold">Reset your password</h2>
          </div>
          <form
            noValidate
            className="space-y-6"
            id="signInForm"
            onSubmit={handleSubmit(handleResetPassword)}
          >
            <div>
              <Label htmlFor="password">New Password</Label>
              <Input
                {...register('password')}
                type="password"
                id="password"
                error={errors.password?.message as string}
              />
            </div>
            <div>
              <Label htmlFor="passwordConfirmation">Confirm new password</Label>
              <Input
                {...register('passwordConfirmation')}
                type="password"
                error={errors.passwordConfirmation?.message}
                data-testid="confirm-password-input"
              />
            </div>
            <div className="pt-8">
              <Button
                type="submit"
                variant="primary"
                className="w-full"
                loading={isLoading || isRedirecting}
              >
                Reset password
              </Button>
            </div>
          </form>
        </div>
      </div>
    </>
  );
};

ResetPassword.Layout = function getLayout(page: ReactElement) {
  return <AuthenticationLayout>{page}</AuthenticationLayout>;
};

export default ResetPassword;