Proyectos de Subversion LeadersLinked - SPA

Rev

Rev 3697 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
3719 stevensc 1
import React, { useRef, useState, useEffect } from 'react';
2
import { Link } from 'react-router-dom';
3
import { useForm } from 'react-hook-form';
4
import { useDispatch, useSelector } from 'react-redux';
5
import { styled, Typography } from '@mui/material';
6
import Mail from '@mui/icons-material/Mail';
7
import Lock from '@mui/icons-material/Lock';
8
import Person from '@mui/icons-material/Person';
9
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
10
import Recaptcha from 'react-recaptcha';
11
 
12
import { axios } from '@utils';
13
import { useFetchHelper } from '@hooks';
14
import { addNotification } from '@store/notification/notification.actions';
15
import CryptoJSAesJson from '@utils/crypto-js/cryptojs-aes-format';
16
 
17
import Input from '@components/UI/inputs/Input';
18
import Button from '@components/UI/buttons/Buttons';
19
import Select from '@components/UI/inputs/Select';
20
import Spinner from '@components/UI/Spinner';
21
import Form from '@components/common/form';
22
import SwitchInput from '@components/UI/SwitchInput';
23
import CheckboxInput from '@components/UI/inputs/Checkbox';
24
 
25
const StyledCheck = styled('div')`
26
  display: flex;
27
  flex-direction: column;
28
  justify-content: center;
29
  align-items: center;
30
  gap: 0.5rem;
31
  p {
32
    text-align: center;
33
  }
34
`;
35
const StyledSpinnerContainer = styled('div')`
36
  position: absolute;
37
  left: 0;
38
  top: 0;
39
  width: 100%;
40
  height: 100%;
41
  background: rgba(255, 255, 255, 0.4);
42
  display: flex;
43
  justify-content: center;
44
  align-items: center;
45
  z-index: 300;
46
`;
47
 
48
const Signup = () => {
49
  const [registered, setRegistered] = useState(false);
50
  const [isLoading, setIsLoading] = useState(false);
51
  const [isAdult, setIsAdult] = useState(false);
52
  const [isVerified, setIsVerified] = useState(false);
53
  const reCaptchaInstance = useRef(null);
54
  const reCaptchaToken = useRef('');
55
  const dispatch = useDispatch();
56
  const { site_key, aes } = useSelector(({ auth }) => auth);
57
 
58
  const { data: timezones } = useFetchHelper('timezones');
59
 
60
  const {
61
    control,
62
    handleSubmit,
63
    setError,
64
    watch,
65
    formState: { errors }
66
  } = useForm({
67
    mode: 'all'
68
  });
69
 
70
  const signupVerifyCallbackHandler = (response) => {
71
    if (!response) return;
72
    reCaptchaToken.current = response;
73
    setIsVerified(true);
74
  };
75
 
76
  const signupExpiredCallbackHandler = () => {
77
    reCaptchaToken.current = '';
78
    setIsVerified(false);
79
  };
80
 
81
  const handleOnRecaptchaLoad = () => {
82
    reCaptchaToken.current = '';
83
  };
84
 
85
  const onSubmitHandler = handleSubmit(
86
    async ({ email, first_name, last_name, password, confirmation, terms_and_conditions }) => {
87
      setIsLoading(true);
88
      const formData = new FormData();
89
      formData.append('first_name', first_name);
90
      formData.append('last_name', last_name);
91
      formData.append('email', CryptoJSAesJson.encrypt(email, aes));
92
      formData.append('password', CryptoJSAesJson.encrypt(password, aes));
93
      formData.append('confirmation', CryptoJSAesJson.encrypt(confirmation, aes));
94
      formData.append('terms_and_conditions', terms_and_conditions ? 1 : 0);
95
 
96
      formData.append('captcha', reCaptchaToken.current);
97
      formData.append('is_adult', isAdult ? 'y' : 'n');
98
 
99
      await axios
100
        .post('/signup', formData)
101
        .then(({ data }) => {
102
          if (!data.success) {
103
            if (typeof data.data !== 'string') {
104
              Object.entries(data.data).forEach(([key, value]) => {
105
                setError(key, {
106
                  type: 'manual',
107
                  message: Array.isArray(value) ? value[0] : value
108
                });
109
              });
110
              return;
111
            }
112
 
113
            dispatch(addNotification({ style: 'danger', msg: data.data }));
114
            reCaptchaInstance.current.reset();
115
            signupVerifyCallbackHandler();
116
            return;
117
          }
118
 
119
          reCaptchaInstance.current.reset();
120
          signupExpiredCallbackHandler();
121
          setRegistered(true);
122
        })
123
        .catch((err) => {
124
          console.log(`Error: ${err}`);
125
          dispatch(
126
            addNotification({
127
              style: 'danger',
128
              msg: 'Disculpe, ha ocurrido un error'
129
            })
130
          );
131
        })
132
        .finally(() => setIsLoading(false));
133
    }
134
  );
135
 
136
  useEffect(() => {
137
    reCaptchaInstance.current?.reset();
138
  }, []);
139
 
140
  if (registered) {
141
    return (
142
      <StyledCheck>
143
        <CheckCircleOutline sx={{ color: '#7FFF00', fontSize: '3rem' }} />
144
 
145
        <Typography>
146
          Se ha registrado correctamente. Por favor, active la cuenta desde su correo
147
        </Typography>
148
 
149
        <Link to='/signin'>
150
          <button className='btn btn-primary'>Entrar</button>
151
        </Link>
152
      </StyledCheck>
153
    );
154
  }
155
 
156
  return (
157
    <Form onSubmit={onSubmitHandler}>
158
      <Typography variant='h3'>Registrarse</Typography>
159
 
160
      <Input
161
        type='email'
162
        name='email'
163
        placeholder='Correo electrónico'
164
        icon={<Mail />}
165
        control={control}
166
        rules={{
167
          required: 'Este campo es requerido',
168
          pattern: {
169
            value: /^[\w-.]+@([\w-]+\.)+[\w-]{2,}$/i,
170
            message: 'Debe ser una dirección de correo electrónico valida'
171
          }
172
        }}
173
        error={errors.email?.message}
174
      />
175
 
176
      <Input
177
        type='text'
178
        name='first_name'
179
        icon={<Person />}
180
        placeholder='Nombre'
181
        control={control}
182
        rules={{
183
          required: 'Este campo es requerido',
184
          maxLength: {
185
            value: 64,
186
            message: 'Limite de carateres superior al permitido'
187
          }
188
        }}
189
        error={errors.first_name?.message}
190
      />
191
 
192
      <Input
193
        type='text'
194
        name='last_name'
195
        icon={<Person />}
196
        placeholder='Apellido'
197
        control={control}
198
        rules={{
199
          required: 'Este campo es requerido',
200
          maxLength: {
201
            value: 64,
202
            message: 'Limite de carateres superior al permitido'
203
          }
204
        }}
205
        error={errors.last_name?.message}
206
      />
207
 
208
      <Input
209
        type='password'
210
        name='password'
211
        icon={<Lock />}
212
        placeholder='Clave'
213
        control={control}
214
        rules={{
215
          required: 'Este campo es requerido',
216
          pattern: {
217
            value: /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$^x%x*-]).{6,16}$/i,
218
            message:
219
              'Debe contener entre 6 y 16 caracteres, incluida una letra mayúscula, un número y un carácter especial #?!@$^%*-'
220
          }
221
        }}
222
        error={errors.password?.message}
223
      />
224
 
225
      <Input
226
        type='password'
227
        name='confirmation'
228
        icon={<Lock />}
229
        placeholder='Confirme su clave'
230
        control={control}
231
        rules={{
232
          required: 'Este campo es requerido',
233
          validate: (v) => v === watch('password') || 'Disculpe, las claves tienen que coincidir'
234
        }}
235
        error={errors.confirmation?.message}
236
      />
237
 
238
      <Select
239
        label='Zona horaria'
240
        name='timezone'
241
        control={control}
242
        rules={{ required: 'Este campo es requerido' }}
243
        error={errors.timezone?.message}
244
        options={Object.entries(timezones).map(([key, value]) => ({
245
          label: key,
246
          value
247
        }))}
248
      />
249
 
250
      <SwitchInput label='Eres mayor de 18' setValue={(value) => setIsAdult(value)} />
251
 
252
      <CheckboxInput
253
        name='terms_and_conditions'
254
        control={control}
255
        label='Si, acepto los Términos y Condiciones.'
256
        rules={{ required: 'Este campo es requerido' }}
257
        error={errors.terms_and_conditions?.message}
258
      />
259
 
260
      <Recaptcha
261
        sitekey={site_key}
262
        verifyCallback={signupVerifyCallbackHandler}
263
        verifyCallbackName='signupVerifyCallbackHandler'
264
        expiredCallback={signupExpiredCallbackHandler}
265
        expiredCallbackName='signupExpiredCallbackHandler'
266
        ref={reCaptchaInstance}
267
        render='explicit'
268
        onloadCallback={handleOnRecaptchaLoad}
269
        hl='es'
270
      />
271
 
272
      <div className='links'>
273
        <Link to='/signin'>¿Ya tienes cuenta?</Link>
274
      </div>
275
 
276
      <Button color='secondary' type='submit' disabled={!isVerified}>
277
        Registrarse
278
      </Button>
279
 
280
      {isLoading && (
281
        <StyledSpinnerContainer>
282
          <Spinner />
283
        </StyledSpinnerContainer>
284
      )}
285
    </Form>
286
  );
287
};
288
 
289
export default Signup;