Proyectos de Subversion LeadersLinked - SPA

Rev

Rev 3697 | 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 from '@mui/icons-material/Mail';
import Lock from '@mui/icons-material/Lock';
import Person from '@mui/icons-material/Person';
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
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]) => ({
          label: 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;