Proyectos de Subversion LeadersLinked - SPA

Rev

Rev 3416 | Rev 3694 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

import React, { useRef, useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useForm } from 'react-hook-form'
import { useDispatch, useSelector } from 'react-redux'
import { styled, Typography } from '@mui/material'
import { Mail, Lock, Person, CheckCircleOutline } from '@mui/icons-material'
import Recaptcha from 'react-recaptcha'

import { axios } from '@utils'
import { useFetchHelper } from '@hooks'
import { addNotification } from '@store/notification/notification.actions'
import CryptoJSAesJson from '@utils/crypto-js/cryptojs-aes-format'

import Input from '@components/UI/inputs/Input'
import Button from '@components/UI/buttons/Buttons'
import Select from '@components/UI/inputs/Select'
import Spinner from '@components/UI/Spinner'
import Form from '@components/common/form'
import SwitchInput from '@components/UI/SwitchInput'
import CheckboxInput from '@components/UI/inputs/Checkbox'

const StyledCheck = styled('div')`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  p {
    text-align: center;
  }
`
const StyledSpinnerContainer = styled('div')`
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 300;
`

const Signup = () => {
  const [registered, setRegistered] = useState(false)
  const [isLoading, setIsLoading] = useState(false)
  const [isAdult, setIsAdult] = useState(false)
  const [isVerified, setIsVerified] = useState(false)
  const reCaptchaInstance = useRef(null)
  const reCaptchaToken = useRef('')
  const dispatch = useDispatch()
  const { site_key, aes } = useSelector(({ auth }) => auth)

  const { data: timezones } = useFetchHelper('timezones')

  const {
    control,
    handleSubmit,
    setError,
    watch,
    formState: { errors }
  } = useForm({
    mode: 'all'
  })

  const signupVerifyCallbackHandler = (response) => {
    if (!response) return
    reCaptchaToken.current = response
    setIsVerified(true)
  }

  const signupExpiredCallbackHandler = () => {
    reCaptchaToken.current = ''
    setIsVerified(false)
  }

  const handleOnRecaptchaLoad = () => {
    reCaptchaToken.current = ''
  }

  const onSubmitHandler = handleSubmit(
    async ({
      email,
      first_name,
      last_name,
      password,
      confirmation,
      terms_and_conditions
    }) => {
      setIsLoading(true)
      const formData = new FormData()
      formData.append('first_name', first_name)
      formData.append('last_name', last_name)
      formData.append('email', CryptoJSAesJson.encrypt(email, aes))
      formData.append('password', CryptoJSAesJson.encrypt(password, aes))
      formData.append(
        'confirmation',
        CryptoJSAesJson.encrypt(confirmation, aes)
      )
      formData.append('terms_and_conditions', terms_and_conditions ? 1 : 0)

      formData.append('captcha', reCaptchaToken.current)
      formData.append('is_adult', isAdult ? 'y' : 'n')

      await axios
        .post('/signup', formData)
        .then(({ data }) => {
          if (!data.success) {
            if (typeof data.data !== 'string') {
              Object.entries(data.data).forEach(([key, value]) => {
                setError(key, {
                  type: 'manual',
                  message: Array.isArray(value) ? value[0] : value
                })
              })
              return
            }

            dispatch(addNotification({ style: 'danger', msg: data.data }))
            reCaptchaInstance.current.reset()
            signupVerifyCallbackHandler()
            return
          }

          reCaptchaInstance.current.reset()
          signupExpiredCallbackHandler()
          setRegistered(true)
        })
        .catch((err) => {
          console.log(`Error: ${err}`)
          dispatch(
            addNotification({
              style: 'danger',
              msg: 'Disculpe, ha ocurrido un error'
            })
          )
        })
        .finally(() => setIsLoading(false))
    }
  )

  useEffect(() => {
    reCaptchaInstance.current?.reset()
  }, [])

  if (registered) {
    return (
      <StyledCheck>
        <CheckCircleOutline sx={{ color: '#7FFF00', fontSize: '3rem' }} />

        <Typography>
          Se ha registrado correctamente. Por favor, active la cuenta desde su
          correo
        </Typography>

        <Link to='/signin'>
          <button className='btn btn-primary'>Entrar</button>
        </Link>
      </StyledCheck>
    )
  }

  return (
    <Form onSubmit={onSubmitHandler}>
      <Typography variant='h3'>Registrarse</Typography>

      <Input
        type='email'
        name='email'
        placeholder='Correo electrónico'
        icon={<Mail />}
        control={control}
        rules={{
          required: 'Este campo es requerido',
          pattern: {
            value: /^[\w-.]+@([\w-]+\.)+[\w-]{2,}$/i,
            message: 'Debe ser una dirección de correo electrónico valida'
          }
        }}
        error={errors.email?.message}
      />

      <Input
        type='text'
        name='first_name'
        icon={<Person />}
        placeholder='Nombre'
        control={control}
        rules={{
          required: 'Este campo es requerido',
          maxLength: {
            value: 64,
            message: 'Limite de carateres superior al permitido'
          }
        }}
        error={errors.first_name?.message}
      />

      <Input
        type='text'
        name='last_name'
        icon={<Person />}
        placeholder='Apellido'
        control={control}
        rules={{
          required: 'Este campo es requerido',
          maxLength: {
            value: 64,
            message: 'Limite de carateres superior al permitido'
          }
        }}
        error={errors.last_name?.message}
      />

      <Input
        type='password'
        name='password'
        icon={<Lock />}
        placeholder='Clave'
        control={control}
        rules={{
          required: 'Este campo es requerido',
          pattern: {
            value:
              /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$^x%x*-]).{6,16}$/i,
            message:
              'Debe contener entre 6 y 16 caracteres, incluida una letra mayúscula, un número y un carácter especial #?!@$^%*-'
          }
        }}
        error={errors.password?.message}
      />

      <Input
        type='password'
        name='confirmation'
        icon={<Lock />}
        placeholder='Confirme su clave'
        control={control}
        rules={{
          required: 'Este campo es requerido',
          validate: (v) =>
            v === watch('password') ||
            'Disculpe, las claves tienen que coincidir'
        }}
        error={errors.confirmation?.message}
      />

      <Select
        label='Zona horaria'
        name='timezone'
        control={control}
        rules={{ required: 'Este campo es requerido' }}
        error={errors.timezone?.message}
        options={Object.entries(timezones).map(([key, value]) => ({
          name: key,
          value
        }))}
      />

      <SwitchInput
        label='Eres mayor de 18'
        setValue={(value) => setIsAdult(value)}
      />

      <CheckboxInput
        name='terms_and_conditions'
        control={control}
        label='Si, acepto los Términos y Condiciones.'
        rules={{ required: 'Este campo es requerido' }}
        error={errors.terms_and_conditions?.message}
      />

      <Recaptcha
        sitekey={site_key}
        verifyCallback={signupVerifyCallbackHandler}
        verifyCallbackName='signupVerifyCallbackHandler'
        expiredCallback={signupExpiredCallbackHandler}
        expiredCallbackName='signupExpiredCallbackHandler'
        ref={reCaptchaInstance}
        render='explicit'
        onloadCallback={handleOnRecaptchaLoad}
        hl='es'
      />

      <div className='links'>
        <Link to='/signin'>¿Ya tienes cuenta?</Link>
      </div>

      <Button color='secondary' type='submit' disabled={!isVerified}>
        Registrarse
      </Button>

      {isLoading && (
        <StyledSpinnerContainer>
          <Spinner />
        </StyledSpinnerContainer>
      )}
    </Form>
  )
}

export default Signup