16212: Set focus on username input element.
[arvados-workbench2.git] / src / views-components / login-form / login-form.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { useState, useEffect, useRef } from 'react';
7 import { withStyles, WithStyles, StyleRulesCallback } from '@material-ui/core/styles';
8 import CircularProgress from '@material-ui/core/CircularProgress';
9 import { Button, Card, CardContent, TextField, CardActions } from '@material-ui/core';
10 import { green } from '@material-ui/core/colors';
11 import { AxiosPromise } from 'axios';
12
13 type CssRules = 'root' | 'loginBtn' | 'card' | 'wrapper' | 'progress';
14
15 const styles: StyleRulesCallback<CssRules> = theme => ({
16     root: {
17         display: 'flex',
18         flexWrap: 'wrap',
19         width: '100%',
20         margin: `${theme.spacing.unit} auto`
21     },
22     loginBtn: {
23         marginTop: theme.spacing.unit,
24         flexGrow: 1
25     },
26     card: {
27         marginTop: theme.spacing.unit,
28         width: '100%'
29     },
30     wrapper: {
31         margin: theme.spacing.unit,
32         position: 'relative',
33     },
34     progress: {
35         color: green[500],
36         position: 'absolute',
37         top: '50%',
38         left: '50%',
39         marginTop: -12,
40         marginLeft: -12,
41     },
42 });
43
44 interface LoginFormProps {
45     handleSubmit: (username: string, password: string) => AxiosPromise;
46 }
47
48 export const LoginForm = withStyles(styles)(
49     ({ handleSubmit, classes }: LoginFormProps & WithStyles<CssRules>) => {
50         const userInput = useRef<HTMLInputElement>(null);
51         const [username, setUsername] = useState('');
52         const [password, setPassword] = useState('');
53         const [isButtonDisabled, setIsButtonDisabled] = useState(true);
54         const [isSubmitting, setSubmitting] = useState(false);
55         const [helperText, setHelperText] = useState('');
56         const [error, setError] = useState(false);
57
58         useEffect(() => {
59             setError(false);
60             setHelperText('');
61             if (username.trim() && password.trim()) {
62                 setIsButtonDisabled(false);
63             } else {
64                 setIsButtonDisabled(true);
65             }
66         }, [username, password]);
67
68         // This only run once after render.
69         useEffect(() => {
70             userInput.current!.focus();
71         }, []);
72
73         const handleLogin = () => {
74             setSubmitting(true);
75             handleSubmit(username, password)
76             .then((response) => {
77                 setError(false);
78                 console.log("LOGIN SUCESSFUL: ", response);
79                 setSubmitting(false);
80             })
81             .catch((err) => {
82                 setError(true);
83                 console.log("ERROR: ", err.response);
84                 setHelperText(`${err.response && err.response.data && err.response.data.errors[0] || 'Error logging in: '+err}`);
85                 setSubmitting(false);
86             });
87         };
88
89         const handleKeyPress = (e: any) => {
90             if (e.keyCode === 13 || e.which === 13) {
91                 if (!isButtonDisabled) {
92                     handleLogin();
93                 }
94             }
95         };
96
97         return (
98             <React.Fragment>
99                 <form className={classes.root} noValidate autoComplete="off">
100                     <Card className={classes.card}>
101                     <div className={classes.wrapper}>
102                         <CardContent>
103                             <div>
104                                 <TextField
105                                     inputRef={userInput}
106                                     disabled={isSubmitting}
107                                     error={error} fullWidth id="username" type="email"
108                                     label="Username" margin="normal"
109                                     onChange={(e) => setUsername(e.target.value)}
110                                     onKeyPress={(e) => handleKeyPress(e)}
111                                 />
112                                 <TextField
113                                     disabled={isSubmitting}
114                                     error={error} fullWidth id="password" type="password"
115                                     label="Password" margin="normal"
116                                     helperText={helperText}
117                                     onChange={(e) => setPassword(e.target.value)}
118                                     onKeyPress={(e) => handleKeyPress(e)}
119                                 />
120                             </div>
121                         </CardContent>
122                         <CardActions>
123                             <Button variant="contained" size="large" color="primary"
124                                 className={classes.loginBtn} onClick={() => handleLogin()}
125                                 disabled={isSubmitting || isButtonDisabled}>
126                                 Log in
127                             </Button>
128                         </CardActions>
129                         { isSubmitting && <CircularProgress color='secondary' className={classes.progress} />}
130                     </div>
131                     </Card>
132                 </form>
133             </React.Fragment>
134         );
135     });