Proyectos de Subversion LeadersLinked - SPA

Rev

Rev 3410 | Rev 3432 | 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 [loading, setloading] = 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,
    }) => {
      setloading(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(() => setloading(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>

      {loading && (
        <StyledSpinnerContainer>
          <Spinner />
        </StyledSpinnerContainer>
      )}
    </Form>
  );
};

export default Signup;