aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modern/public/logo.svg4
-rw-r--r--modern/src/LoginPage.js118
-rw-r--r--modern/src/components/LoginForm.js125
-rw-r--r--modern/src/components/RegisterForm.js122
-rw-r--r--modern/src/components/ResetPasswordForm.js12
-rw-r--r--modern/src/theme/palette.js2
6 files changed, 267 insertions, 116 deletions
diff --git a/modern/public/logo.svg b/modern/public/logo.svg
index 6fbe7b0..008b46d 100644
--- a/modern/public/logo.svg
+++ b/modern/public/logo.svg
@@ -12,8 +12,8 @@
</metadata>
<g id="layer1">
<rect id="rect3778" height="64" width="236.1" y="0" x="0" fill="none"/>
- <ellipse id="path3038" rx="28.995" ry="28.995" transform="rotate(-30)" cy="43.713" cx="11.713" stroke-width="10.699" fill="none"/>
- <g fill="#fff">
+ <ellipse id="path3038" rx="28.995" ry="28.995" transform="rotate(-30)" cy="43.713" cx="11.713" stroke-width="10.699" fill="#fff"/>
+ <g fill="#336">
<circle id="path2993" stroke-width="1.3262" transform="rotate(-30)" cy="43.713" cx="9.4364" r="2.2765"/>
<path id="path3004" d="m37.012 24.177-2.8428 3.6128c0.66345 0.52205 1.3255 1.1576 1.7734 1.9333 0.4479 0.77578 0.66726 1.6669 0.78764 2.5025l4.5502-0.65558c-0.193-1.42-0.633-2.804-1.394-4.123s-1.74-2.391-2.874-3.27z" stroke-width="1.0095"/>
<path id="path3014" d="m42.504 16.9-2.8428 3.6128c1.607 1.2355 3.0914 2.7935 4.1679 4.6581s1.6835 3.9291 1.95 5.9386l4.5502-0.65558c-0.33967-2.5954-1.1669-5.1513-2.5573-7.5594-1.3903-2.4081-3.1901-4.4025-5.268-5.9944z" stroke-width="1.0095"/>
diff --git a/modern/src/LoginPage.js b/modern/src/LoginPage.js
index 30275e5..3d4b17f 100644
--- a/modern/src/LoginPage.js
+++ b/modern/src/LoginPage.js
@@ -1,14 +1,8 @@
import React, { useState } from 'react';
-import { useDispatch } from 'react-redux';
-import { useHistory } from 'react-router-dom';
-import { sessionActions } from './store';
-import { Grid, useMediaQuery, makeStyles, InputLabel, Select, MenuItem, FormControl, Button, TextField, Paper, Link } from '@material-ui/core';
+import { useMediaQuery, makeStyles, Paper } from '@material-ui/core';
import { useTheme } from '@material-ui/core/styles';
-import RegisterDialog from './RegisterDialog';
-import { useSelector } from 'react-redux';
-import t from './common/localization';
-import Logo from './Logo';
+import LoginForm from './components/LoginForm';
const useStyles = makeStyles(theme => ({
root: {
@@ -45,123 +39,23 @@ const useStyles = makeStyles(theme => ({
padding: theme.spacing(5),
width: "100%",
},
- logo: {
- textAlign: 'center',
- },
}));
const LoginPage = () => {
const classes = useStyles();
const theme = useTheme();
- const dispatch = useDispatch();
- const history = useHistory();
-
- const [failed, setFailed] = useState(false);
- const [email, setEmail] = useState('');
- const [password, setPassword] = useState('');
- const [registerDialogShown, setRegisterDialogShown] = useState(false);
- const registrationEnabled = useSelector(state => state.session.server ? state.session.server['registration'] : false);
+ const [CurrentForm, setCurrentForm] = useState(() => LoginForm);
const matches = useMediaQuery(theme.breakpoints.down('md'));
- const handleEmailChange = (event) => {
- setEmail(event.target.value);
- }
-
- const handlePasswordChange = (event) => {
- setPassword(event.target.value);
- }
-
- const handleRegister = () => {
- setRegisterDialogShown(true);
- }
-
- const handleRegisterResult = () => {
- setRegisterDialogShown(false);
- }
-
- const handleLogin = async (event) => {
- event.preventDefault();
- const response = await fetch('/api/session', { method: 'POST', body: new URLSearchParams(`email=${email}&password=${password}`) });
- if (response.ok) {
- const user = await response.json();
- dispatch(sessionActions.updateUser(user));
- history.push('/');
- } else {
- setFailed(true);
- setPassword('');
- }
- }
-
return (
<main className={classes.root}>
<div className={classes.sidebar}>
- {!matches && <Logo fill='#fff'/> }
+ {!matches && <img src='/logo.svg' alt='Traccar' /> }
</div>
<Paper className={classes.paper}>
- <form className={classes.form} onSubmit={handleLogin}>
- <Grid container direction='column' spacing={3}>
- <Grid item className={classes.logo}>
- {matches && <Logo fill='#333366'/>}
- </Grid>
- <Grid item>
- <TextField
- required
- fullWidth
- error={failed}
- label={t('userEmail')}
- name='email'
- value={email}
- autoComplete='email'
- autoFocus
- onChange={handleEmailChange}
- helperText={failed && 'Invalid username or password'}
- variant='filled' />
- </Grid>
- <Grid item>
- <TextField
- required
- fullWidth
- error={failed}
- label={t('userPassword')}
- name='password'
- value={password}
- type='password'
- autoComplete='current-password'
- onChange={handlePasswordChange}
- variant='filled' />
- </Grid>
- <Grid item>
- <Button
- type='submit'
- variant='contained'
- color='secondary'
- disabled={!email || !password}
- fullWidth>
- {t('loginLogin')}
- </Button>
- </Grid>
- <Grid item container>
- <Grid item>
- <Button onClick={handleRegister} disabled={!registrationEnabled} color="secondary">
- {t('loginRegister')}
- </Button>
- </Grid>
- <Grid item xs>
- <FormControl variant="filled" fullWidth>
- <InputLabel>{t('loginLanguage')}</InputLabel>
- <Select>
- <MenuItem value="en">English</MenuItem>
- </Select>
- </FormControl>
- </Grid>
- </Grid>
- <Grid item container justify="flex-end">
- <Grid item>
- <Link underline="none">{t('loginReset')}</Link>
- </Grid>
- </Grid>
- </Grid>
+ <form className={classes.form}>
+ <CurrentForm setCurrentForm={setCurrentForm} />
</form>
</Paper>
</main>
diff --git a/modern/src/components/LoginForm.js b/modern/src/components/LoginForm.js
new file mode 100644
index 0000000..d52a51d
--- /dev/null
+++ b/modern/src/components/LoginForm.js
@@ -0,0 +1,125 @@
+import React, { useState } from 'react';
+import { Grid, useMediaQuery, makeStyles, InputLabel, Select, MenuItem, FormControl, Button, TextField, Link } from '@material-ui/core';
+import { useTheme } from '@material-ui/core/styles';
+import { useDispatch, useSelector } from 'react-redux';
+import { useHistory } from 'react-router-dom';
+import { sessionActions } from './../store';
+import t from './../common/localization';
+import RegisterForm from './RegisterForm';
+import ResetPasswordForm from './ResetPasswordForm';
+
+const useStyles = makeStyles(theme => ({
+ logoContainer: {
+ textAlign: 'center',
+ },
+ resetPassword: {
+ cursor: 'pointer',
+ }
+}));
+
+const forms = {
+ register: () => RegisterForm,
+ resetPassword: () => ResetPasswordForm,
+};
+
+const LoginForm = ({ setCurrentForm }) => {
+
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const history = useHistory();
+ const theme = useTheme();
+ const matches = useMediaQuery(theme.breakpoints.down('md'));
+
+ const [failed, setFailed] = useState(false);
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const registrationEnabled = useSelector(state => state.session.server ? state.session.server['registration'] : false);
+
+ const handleEmailChange = (event) => {
+ setEmail(event.target.value);
+ }
+
+ const handlePasswordChange = (event) => {
+ setPassword(event.target.value);
+ }
+
+ const handleLogin = async (event) => {
+ event.preventDefault();
+ const response = await fetch('/api/session', { method: 'POST', body: new URLSearchParams(`email=${email}&password=${password}`) });
+ if (response.ok) {
+ const user = await response.json();
+ dispatch(sessionActions.updateUser(user));
+ history.push('/');
+ } else {
+ setFailed(true);
+ setPassword('');
+ }
+ }
+
+ return (
+ <Grid container direction='column' spacing={3}>
+ <Grid item className={classes.logoContainer}>
+ {matches && <img src='/logo.svg' alt='Traccar' />}
+ </Grid>
+ <Grid item>
+ <TextField
+ required
+ fullWidth
+ error={failed}
+ label={t('userEmail')}
+ name='email'
+ value={email}
+ autoComplete='email'
+ autoFocus
+ onChange={handleEmailChange}
+ helperText={failed && 'Invalid username or password'}
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <TextField
+ required
+ fullWidth
+ error={failed}
+ label={t('userPassword')}
+ name='password'
+ value={password}
+ type='password'
+ autoComplete='current-password'
+ onChange={handlePasswordChange}
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <Button
+ onClick={handleLogin}
+ variant='contained'
+ color='secondary'
+ disabled={!email || !password}
+ fullWidth>
+ {t('loginLogin')}
+ </Button>
+ </Grid>
+ <Grid item container>
+ <Grid item>
+ <Button onClick={() => setCurrentForm(forms.register)} disabled={!registrationEnabled} color="secondary">
+ {t('loginRegister')}
+ </Button>
+ </Grid>
+ <Grid item xs>
+ <FormControl variant="filled" fullWidth>
+ <InputLabel>{t('loginLanguage')}</InputLabel>
+ <Select>
+ <MenuItem value="en">English</MenuItem>
+ </Select>
+ </FormControl>
+ </Grid>
+ </Grid>
+ <Grid item container justify="flex-end">
+ <Grid item>
+ <Link onClick={() => setCurrentForm(forms.resetPassword)} className={classes.resetPassword} underline="none">{t('loginReset')}</Link>
+ </Grid>
+ </Grid>
+ </Grid>
+ )
+}
+
+export default LoginForm;
diff --git a/modern/src/components/RegisterForm.js b/modern/src/components/RegisterForm.js
new file mode 100644
index 0000000..6d013f7
--- /dev/null
+++ b/modern/src/components/RegisterForm.js
@@ -0,0 +1,122 @@
+import React, { useState } from 'react';
+import { Grid, Button, TextField, Typography, Link, makeStyles, Snackbar } from '@material-ui/core';
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+import LoginForm from './LoginForm';
+import t from './../common/localization';
+
+const useStyles = makeStyles(theme => ({
+ register: {
+ fontSize: theme.spacing(3),
+ fontWeight: 500
+ },
+ link: {
+ fontSize: theme.spacing(3),
+ fontWeight: 500,
+ marginTop: theme.spacing(0.5),
+ cursor: 'pointer'
+ }
+}));
+
+const forms = {
+ login: () => LoginForm,
+};
+
+const RegisterForm = ({ setCurrentForm }) => {
+
+ const classes = useStyles();
+ const [name, setName] = useState('');
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
+
+ const submitDisabled = () => {
+ return !name || !/(.+)@(.+)\.(.{2,})/.test(email) || !password;
+ }
+
+ const handleRegister = async () => {
+ const response = await fetch('/api/users', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({name, email, password})
+ });
+
+ if (response.ok) {
+ setSnackbarOpen(true);
+ }
+ }
+
+ return (
+ <>
+ <Snackbar
+ anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
+ open={snackbarOpen}
+ onClose={() => setCurrentForm(forms.login)}
+ autoHideDuration={6000}
+ message={t('loginCreated')} />
+ <Grid container direction='column' spacing={3}>
+ <Grid container item>
+ <Grid item>
+ <Typography className={classes.link} color='primary'>
+ <Link onClick={() => setCurrentForm(forms.login)}>
+ <ArrowBackIcon />
+ </Link>
+ </Typography>
+ </Grid>
+ <Grid item xs>
+ <Typography className={classes.register} color='primary'>
+ {t('loginRegister')}
+ </Typography>
+ </Grid>
+ </Grid>
+ <Grid item>
+ <TextField
+ required
+ fullWidth
+ label={t('sharedName')}
+ name='name'
+ value={name || ''}
+ autoComplete='name'
+ autoFocus
+ onChange={event => setName(event.target.value)}
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <TextField
+ required
+ fullWidth
+ type='email'
+ label={t('userEmail')}
+ name='email'
+ value={email || ''}
+ autoComplete='email'
+ onChange={event => setEmail(event.target.value)}
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <TextField
+ required
+ fullWidth
+ label={t('userPassword')}
+ name='password'
+ value={password || ''}
+ type='password'
+ autoComplete='current-password'
+ onChange={event => setPassword(event.target.value)}
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <Button
+ variant='contained'
+ color="secondary"
+ onClick={handleRegister}
+ disabled={submitDisabled()}
+ fullWidth>
+ {t('loginRegister')}
+ </Button>
+ </Grid>
+ </Grid>
+ </>
+ )
+}
+
+export default RegisterForm;
diff --git a/modern/src/components/ResetPasswordForm.js b/modern/src/components/ResetPasswordForm.js
new file mode 100644
index 0000000..c268f80
--- /dev/null
+++ b/modern/src/components/ResetPasswordForm.js
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const ResetPasswordForm = () => {
+
+ return (
+ <>
+ <div>Reset Password Comming Soon!!!</div>
+ </>
+ )
+}
+
+export default ResetPasswordForm;
diff --git a/modern/src/theme/palette.js b/modern/src/theme/palette.js
index 5a54b77..5c93cfd 100644
--- a/modern/src/theme/palette.js
+++ b/modern/src/theme/palette.js
@@ -1,5 +1,3 @@
-import { deepPurple, green } from '@material-ui/core/colors';
-
const traccarPurple = '#333366';
const traccarGreen = '#4CAF50';
const traccarWhite = '#FFF';