aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
authorzirops <zirops@gmail.com>2021-01-26 10:57:25 +0100
committerzirops <zirops@gmail.com>2021-01-26 10:57:25 +0100
commit12184a5ef570593ab492c1499f2d7a23e3be2569 (patch)
treebc2f7ed0d098bbaef247d0e5ab7f5a57f08ab3be /modern
parent2201f5417f41204453e3cd53da003296925282aa (diff)
downloadetbsa-traccar-web-12184a5ef570593ab492c1499f2d7a23e3be2569.tar.gz
etbsa-traccar-web-12184a5ef570593ab492c1499f2d7a23e3be2569.tar.bz2
etbsa-traccar-web-12184a5ef570593ab492c1499f2d7a23e3be2569.zip
user self registration
Diffstat (limited to 'modern')
-rw-r--r--modern/src/LoginPage.js29
-rw-r--r--modern/src/RegisterDialog.js109
2 files changed, 136 insertions, 2 deletions
diff --git a/modern/src/LoginPage.js b/modern/src/LoginPage.js
index 2636c80..cbe50fe 100644
--- a/modern/src/LoginPage.js
+++ b/modern/src/LoginPage.js
@@ -7,6 +7,9 @@ import FormControl from '@material-ui/core/FormControl';
import Paper from '@material-ui/core/Paper';
import { makeStyles } from '@material-ui/core';
import TextField from '@material-ui/core/TextField';
+import RegisterDialog from './RegisterDialog';
+import Snackbar from '@material-ui/core/Snackbar';
+import { useSelector } from 'react-redux';
import t from './common/localization';
@@ -47,10 +50,19 @@ const LoginPage = () => {
const [failed, setFailed] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
+ const [registerDialogShown, setRegisterDialogShown] = useState(false);
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
const classes = useStyles();
const history = useHistory();
+ const server = useSelector(state => state.session.server);
+
+ let registrationPref = false;
+ if (server) {
+ registrationPref = server['registration']
+ }
+
const handleEmailChange = (event) => {
setEmail(event.target.value);
}
@@ -60,7 +72,14 @@ const LoginPage = () => {
}
const handleRegister = () => {
- // TODO: Implement registration
+ setRegisterDialogShown(true);
+ }
+
+ const handleRegisterResult = (resultOk) => {
+ setRegisterDialogShown(false);
+ if (resultOk) {
+ setSnackbarOpen(true);
+ }
}
const handleLogin = async (event) => {
@@ -109,7 +128,7 @@ const LoginPage = () => {
<FormControl fullWidth margin='normal'>
<div className={classes.buttons}>
- <Button type='button' variant='contained' disabled onClick={handleRegister}>
+ <Button type='button' variant='contained' onClick={handleRegister} disabled={!registrationPref}>
{t('loginRegister')}
</Button>
<Button type='submit' variant='contained' color='primary' disabled={!email || !password}>
@@ -118,6 +137,12 @@ const LoginPage = () => {
</div>
</FormControl>
</form>
+ {registerDialogShown && <RegisterDialog open={registerDialogShown} onResult={handleRegisterResult} />}
+ <Snackbar
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center', }}
+ open={snackbarOpen} autoHideDuration={6000}
+ onClose={()=>{setSnackbarOpen(false);}} message={t('loginCreated')}
+ />
</Paper>
</main>
);
diff --git a/modern/src/RegisterDialog.js b/modern/src/RegisterDialog.js
new file mode 100644
index 0000000..ecd07ac
--- /dev/null
+++ b/modern/src/RegisterDialog.js
@@ -0,0 +1,109 @@
+import t from './common/localization'
+import React, { useState } from 'react';
+import Button from '@material-ui/core/Button';
+import Dialog from '@material-ui/core/Dialog';
+import DialogActions from '@material-ui/core/DialogActions';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+import TextField from '@material-ui/core/TextField';
+import Alert from '@material-ui/lab/Alert';
+
+const RegisterDialog = ({ open, onResult }) => {
+ const [formFields, setFormFields] = useState({});
+ const [validationErrors, setValidationErrors] = useState({});
+ const [errorResponse, setErrorResponse] = useState(null);
+
+ const handleChange = (event) => {
+ setFormFields({ ...formFields, [event.target.name]: event.target.value });
+ setValidationErrors({ ...validationErrors, [event.target.name]: false });
+ }
+
+ const handleRegister = async (event) => {
+ event.preventDefault();
+ setErrorResponse(null);
+ let objErrors = {};
+ if (formFields.name.trim() === '') {
+ objErrors.name = true;
+ }
+ if (!
+ /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(formFields.email)) {
+ objErrors.email = true;
+ }
+ if (formFields.password.trim() === '') {
+ objErrors.password = true;
+ }
+ if (Object.keys(objErrors).length !== 0) {
+ setValidationErrors(objErrors);
+ return;
+ }
+
+ const response = await fetch('/api/users', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(formFields)
+ });
+
+ if (response.ok) {
+ onResult(true)
+ } else {
+ setErrorResponse(t('errorGeneral'));
+ }
+ }
+
+ return (
+ <Dialog
+ open={open}
+ onClose={() => { onResult(false) }}>
+ <DialogContent>
+ <DialogContentText>{t('loginRegister')}</DialogContentText>
+
+ {errorResponse && <Alert severity="error">{errorResponse}</Alert>}
+
+ <TextField
+ margin='normal'
+ required
+ fullWidth
+ error={validationErrors.name}
+ label={t('sharedName')}
+ name='name'
+ value={formFields.name || ''}
+ autoComplete='name'
+ autoFocus
+ onChange={handleChange}
+ helperText={validationErrors.name && 'Name is required'} />
+
+ <TextField
+ margin='normal'
+ required
+ fullWidth
+ error={validationErrors.email}
+ label={t('userEmail')}
+ name='email'
+ value={formFields.email || ''}
+ autoComplete='email'
+ onChange={handleChange}
+ helperText={validationErrors.email && 'Invalid e-mail'} />
+
+ <TextField
+ margin='normal'
+ required
+ fullWidth
+ error={validationErrors.password}
+ label={t('userPassword')}
+ name='password'
+ value={formFields.password || ''}
+ type='password'
+ autoComplete='current-password'
+ onChange={handleChange}
+ helperText={validationErrors.password && 'Password is required'} />
+
+ </DialogContent>
+ <DialogActions>
+ <Button color="primary" onClick={handleRegister} disabled={!formFields.name || !formFields.email || !formFields.password}>{t('loginRegister')}</Button>
+ <Button autoFocus onClick={() => onResult(false)}>{t('sharedCancel')}</Button>
+ </DialogActions>
+ </Dialog>
+ );
+};
+
+export default RegisterDialog;