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;