skyderby/skyderby

View on GitHub
app/javascript/components/Users/SignIn/index.tsx

Summary

Maintainability
C
1 day
Test Coverage
import React from 'react'
import { Link, useNavigate, useLocation } from 'react-router-dom'
import { Formik, Field, FormikHelpers } from 'formik'
import toast from 'react-hot-toast'

import { useLoginMutation } from 'api/sessions'
import Separator from 'components/Users/Separator'
import { useI18n } from 'components/TranslationsProvider'
import validationSchema from './validationSchema'
import styles from 'components/Users/styles.module.scss'
import RequestErrorToast from 'components/RequestErrorToast'

interface FormValues {
  email: string
  password: string
}

const initialValues = { email: '', password: '' }

const SignIn = (): JSX.Element => {
  const navigate = useNavigate()
  const location = useLocation()
  const { t } = useI18n()
  const loginMutation = useLoginMutation()
  const returnTo = location.state?.returnTo ?? '/'

  const handleSubmit = async (
    values: FormValues,
    formikBag: FormikHelpers<FormValues>
  ) => {
    loginMutation.mutate(values, {
      onSuccess: () => navigate(returnTo),
      onSettled: () => formikBag.setSubmitting(false),
      onError: error => {
        toast.error(<RequestErrorToast response={error.response} />)
      }
    })
  }

  return (
    <Formik
      initialValues={initialValues}
      onSubmit={handleSubmit}
      validationSchema={validationSchema}
    >
      {({ touched, errors, handleSubmit, isSubmitting }) => (
        <form onSubmit={handleSubmit} className={styles.container}>
          {loginMutation.error && (
            <p className={styles.serverError}>
              {loginMutation.error.response?.data?.error || loginMutation.error.message}
            </p>
          )}

          <div>
            <Field
              autoFocus
              name="email"
              className={styles.input}
              placeholder="Email"
              autoComplete="username"
              data-invalid={errors.email && touched.email}
            />
            {errors.email && touched.email && (
              <span className={styles.error}>{errors.email}</span>
            )}
          </div>

          <div>
            <Field
              name="password"
              className={styles.input}
              type="password"
              placeholder="Password"
              autoComplete="current-password"
              data-invalid={errors.password && touched.password}
            />
            {errors.password && touched.password && (
              <span className={styles.error}>{errors.password}</span>
            )}
          </div>

          <Link to="/users/forgot-password" className={styles.link}>
            {t('devise.shared.links.forgot_your_password')}
          </Link>

          <button type="submit" className={styles.primaryButton} disabled={isSubmitting}>
            {t('devise.sessions.new.sign_in')}
          </button>

          <Separator>{t('general.or')}</Separator>

          <Link to="/users/sign-up" className={styles.secondaryButton}>
            {t('devise.shared.links.sign_up')}
          </Link>

          <a href="/users/auth/facebook" className={styles.secondaryButton}>
            {t('devise.shared.links.sign_in_with_provider', { provider: 'Facebook' })}
          </a>
        </form>
      )}
    </Formik>
  )
}

export default SignIn