all repos — caroster @ 9ce866648000343d9681be1a31185970c359b52d

[Octree] Group carpool to your event https://caroster.io

frontend/containers/SignInForm/index.js (view raw)

  1import {useState, useMemo, useEffect} from 'react';
  2import {useTranslation} from 'react-i18next';
  3import TextField from '@material-ui/core/TextField';
  4import {useRouter} from 'next/router';
  5import RouterLink from 'next/link';
  6import Button from '@material-ui/core/Button';
  7import Link from '@material-ui/core/Link';
  8import Typography from '@material-ui/core/Typography';
  9import CardContent from '@material-ui/core/CardContent';
 10import CircularProgress from '@material-ui/core/CircularProgress';
 11import FormHelperText from '@material-ui/core/FormHelperText';
 12import CardActions from '@material-ui/core/CardActions';
 13import {makeStyles} from '@material-ui/core/styles';
 14import useLoginForm from '../../hooks/useLoginForm';
 15import useToastsStore from '../../stores/useToastStore';
 16import useAuthStore from '../../stores/useAuthStore';
 17import useLoginWithProvider from '../../hooks/useLoginWithProvider';
 18
 19const SignIn = () => {
 20  const {t} = useTranslation();
 21  const classes = useStyles();
 22  const router = useRouter();
 23  const token = useAuthStore(s => s.token);
 24  const {loginWithProvider} = useLoginWithProvider();
 25  const [error, setError] = useState('');
 26  const [email, setEmail] = useState('');
 27  const [password, setPassword] = useState('');
 28  const addToast = useToastsStore(s => s.addToast);
 29  const {login, loading} = useLoginForm(email, password);
 30
 31  useEffect(() => {
 32    if (token) router.replace('/dashboard');
 33  }, [token]);
 34
 35  const canSubmit = useMemo(
 36    () => [email, password].filter(s => s.length < 4).length === 0,
 37    [email, password]
 38  );
 39
 40  const onSubmit = async e => {
 41    e.preventDefault?.();
 42    try {
 43      await login();
 44      // TODO add to my event if saved in local storage
 45      // TODO remove from local storage.
 46      router.push('/');
 47    } catch (error) {
 48      handleAuthError(error);
 49    }
 50
 51    return false;
 52  };
 53
 54  // If an access token is given in URL params, login with auth provider
 55  useEffect(() => {
 56    const authWithGoogle = async search => {
 57      try {
 58        await loginWithProvider('google', search);
 59      } catch (error) {
 60        handleAuthError(error);
 61      }
 62    };
 63
 64    const search = getURLSearch(router);
 65    if (search) authWithGoogle(search);
 66  }, [router.route]); // eslint-disable-line react-hooks/exhaustive-deps
 67
 68  const handleAuthError = error => {
 69    const strapiError = getStrapiError(error);
 70    console.error({strapiError});
 71    if (strapiError === 'Auth.form.error.invalid') {
 72      setError(t('signin.errors'));
 73      addToast(t('signin.errors'));
 74    } else if (strapiError === 'Auth.form.error.confirmed') {
 75      setError(t('signin.unconfirmed'));
 76      addToast(t('signin.unconfirmed'));
 77    } else if (strapiError === 'Auth.form.error.email.taken') {
 78      addToast(t('signup.errors.email_taken'));
 79    } else console.error(error);
 80  };
 81
 82  return (
 83    <form onSubmit={onSubmit}>
 84      <CardContent className={classes.content}>
 85        <Typography variant="h6">{t('signin.title')}</Typography>
 86        {error && <FormHelperText error={true}>{error}</FormHelperText>}
 87        <TextField
 88          label={t('signin.email')}
 89          fullWidth
 90          required={true}
 91          margin="dense"
 92          value={email}
 93          onChange={({target: {value = ''}}) => setEmail(value)}
 94          id="SignInEmail"
 95          name="email"
 96          type="email"
 97          error={!!error}
 98        />
 99        <TextField
100          label={t('signin.password')}
101          fullWidth
102          required={true}
103          margin="dense"
104          value={password}
105          onChange={({target: {value = ''}}) => setPassword(value)}
106          id="SignInEmail"
107          name="password"
108          type="password"
109          error={!!error}
110          gutterBottom
111        />
112        <RouterLink href="/auth/lost-password">
113          <Link>
114            <Typography align="center" variant="body2">
115              {t('lost_password.message')}
116            </Typography>
117          </Link>
118        </RouterLink>
119      </CardContent>
120      <CardActions className={classes.actions} align="center">
121        <Button size="small" id="SignInRegister" href="/auth/register">
122          {t('signin.register')}
123        </Button>
124        <Button
125          color="primary"
126          variant="contained"
127          type="submit"
128          disabled={!canSubmit}
129          aria-disabled={!canSubmit}
130          id="SignInSubmit"
131          endIcon={
132            loading && <CircularProgress className={classes.loader} size={20} />
133          }
134        >
135          {t('signin.login')}
136        </Button>
137      </CardActions>
138    </form>
139  );
140};
141
142const getStrapiError = error => {
143  if (error.message?.[0]?.messages?.[0]) return error.message[0].messages[0].id;
144  return error?.graphQLErrors?.[0].extensions.exception.data.message[0]
145    .messages[0].id;
146};
147
148const getURLSearch = router => router.asPath.replace(router.route, '');
149
150const useStyles = makeStyles(theme => ({
151  content: {
152    display: 'flex',
153    flexDirection: 'column',
154  },
155  loader: {
156    marginLeft: '14px',
157    color: theme.palette.background.paper,
158  },
159  actions: {
160    justifyContent: 'center',
161    marginBottom: theme.spacing(2),
162  },
163}));
164export default SignIn;