aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modern/src/DeviceList.js137
-rw-r--r--modern/src/LoginPage.js177
-rw-r--r--modern/src/MainPage.js87
-rw-r--r--modern/src/MainToolbar.js260
4 files changed, 297 insertions, 364 deletions
diff --git a/modern/src/DeviceList.js b/modern/src/DeviceList.js
index a97e4fe3..64f95a27 100644
--- a/modern/src/DeviceList.js
+++ b/modern/src/DeviceList.js
@@ -1,96 +1,79 @@
-import t from './common/localization'
-import React, { Component, Fragment } from 'react';
-import { connect } from 'react-redux';
+import React, { Fragment, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useHistory } from 'react-router-dom';
+import Avatar from '@material-ui/core/Avatar';
+import Divider from '@material-ui/core/Divider';
+import IconButton from '@material-ui/core/IconButton';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
+import Menu from '@material-ui/core/Menu';
+import MenuItem from '@material-ui/core/MenuItem';
+import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
-import Avatar from '@material-ui/core/Avatar';
import LocationOnIcon from '@material-ui/icons/LocationOn';
-import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
-import IconButton from '@material-ui/core/IconButton';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import Divider from '@material-ui/core/Divider';
-import Menu from '@material-ui/core/Menu';
-import MenuItem from '@material-ui/core/MenuItem';
-import RemoveDialog from './RemoveDialog'
-import { devicesActions } from './store';
-const mapStateToProps = state => ({
- devices: Object.values(state.devices.items)
-});
-
-class DeviceList extends Component {
- constructor(props) {
- super(props);
- this.state = {
- menuAnchorEl: null,
- removeDialogOpen: false
- };
- }
+import { devicesActions } from './store';
+import t from './common/localization';
+import RemoveDialog from './RemoveDialog';
- handleItemClick(device) {
- this.props.dispatch(devicesActions.select(device));
- }
+const DeviceList = () => {
+ const [menuAnchorEl, setMenuAnchorEl] = useState(null);
+ const [removeDialogOpen, setRemoveDialogOpen] = useState(false);
+ const devices = useSelector(state => Object.values(state.devices.items));
+ const dispatch = useDispatch();
+ const history = useHistory();
- handleMenuClick(event) {
- this.setState({ menuAnchorEl: event.currentTarget });
+ const handleMenuClick = (event) => {
+ setMenuAnchorEl(event.currentTarget);
}
- handleMenuClose() {
- this.setState({ menuAnchorEl: null });
+ const handleMenuClose = () => {
+ setMenuAnchorEl(null);
}
- handleMenuEdit() {
- this.props.history.push('/device');
- this.handleMenuClose();
+ const handleMenuEdit = () => {
+ history.push('/device');
+ handleMenuClose();
}
- handleMenuRemove() {
- this.setState({ removeDialogOpen: true });
- this.handleMenuClose();
+ const handleMenuRemove = () => {
+ setRemoveDialogOpen(true);
+ handleMenuClose();
}
- render() {
- const devices = this.props.devices.map((device, index, list) =>
- <Fragment key={device.id.toString()}>
- <ListItem button onClick={() => this.handleItemClick(device)}>
- <ListItemAvatar>
- <Avatar>
- <LocationOnIcon />
- </Avatar>
- </ListItemAvatar>
- <ListItemText primary={device.name} secondary={device.uniqueId} />
- <ListItemSecondaryAction>
- <IconButton onClick={(event) => this.handleMenuClick(event)}>
- <MoreVertIcon />
- </IconButton>
- </ListItemSecondaryAction>
- </ListItem>
- {index < list.length - 1 ? <Divider /> : null}
- </Fragment>
- );
-
- return (
- <Fragment>
- <List>
- {devices}
- </List>
- <Menu
- id="device-menu"
- anchorEl={this.state.menuAnchorEl}
- keepMounted
- open={Boolean(this.state.menuAnchorEl)}
- onClose={() => this.handleMenuClose()}>
- <MenuItem onClick={() => this.handleMenuEdit()}>{t('sharedEdit')}</MenuItem>
- <MenuItem onClick={() => this.handleMenuRemove()}>{t('sharedRemove')}</MenuItem>
- </Menu>
- <RemoveDialog
- open={this.state.removeDialogOpen}
- onClose={() => { this.setState({ removeDialogOpen: false }) }} />
- </Fragment>
- );
- }
+ return (
+ <>
+ <List>
+ {devices.map((device, index, list) => (
+ <Fragment key={device.id}>
+ <ListItem button key={device.id} onClick={() => dispatch(devicesActions.select(device))}>
+ <ListItemAvatar>
+ <Avatar>
+ <LocationOnIcon />
+ </Avatar>
+ </ListItemAvatar>
+ <ListItemText primary={device.name} secondary={device.uniqueId} />
+ <ListItemSecondaryAction>
+ <IconButton onClick={handleMenuClick}>
+ <MoreVertIcon />
+ </IconButton>
+ </ListItemSecondaryAction>
+ </ListItem>
+ {index < list.length - 1 ? <Divider /> : null}
+ </Fragment>
+ ))
+ }
+ </List>
+ <Menu id="device-menu" anchorEl={menuAnchorEl} keepMounted open={Boolean(menuAnchorEl)} onClose={handleMenuClose}>
+ <MenuItem onClick={handleMenuEdit}>{t('sharedEdit')}</MenuItem>
+ <MenuItem onClick={handleMenuRemove}>{t('sharedRemove')}</MenuItem>
+ </Menu>
+ <RemoveDialog open={removeDialogOpen} onClose={() => { setRemoveDialogOpen(false) }} />
+ </>
+ );
}
-export default connect(mapStateToProps)(DeviceList);
+export default DeviceList;
+
diff --git a/modern/src/LoginPage.js b/modern/src/LoginPage.js
index 8104aa34..1da33663 100644
--- a/modern/src/LoginPage.js
+++ b/modern/src/LoginPage.js
@@ -1,14 +1,16 @@
-import t from './common/localization'
-import React, { Component } from 'react';
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
import Button from '@material-ui/core/Button';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import Paper from '@material-ui/core/Paper';
-import withStyles from '@material-ui/core/styles/withStyles';
+import { makeStyles } from '@material-ui/core';
-const styles = theme => ({
+import t from './common/localization';
+
+const useStyles = makeStyles(theme => ({
root: {
width: 'auto',
display: 'block', // Fix IE11 issue.
@@ -39,111 +41,94 @@ const styles = theme => ({
flex: '1 1 0',
margin: `${theme.spacing(3)}px ${theme.spacing(1)}px 0`
},
-});
+}));
+
+const LoginPage = () => {
+ const [filled, setFilled] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [failed, setFailed] = useState(false);
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
-class LoginPage extends Component {
- constructor(props) {
- super(props);
- this.state = {
- filled: false,
- loading: false,
- failed: false,
- email: "",
- password: ""
- };
- this.handleChange = this.handleChange.bind(this);
- this.handleRegister = this.handleRegister.bind(this);
- this.handleLogin = this.handleLogin.bind(this);
+ const classes = useStyles();
+ const history = useHistory();
+
+ const handleEmailChange = (event) => {
+ setEmail(event.target.value);
}
- handleChange(event) {
- this.setState({
- [event.target.id]: event.target.value
- });
+ const handlePasswordChange = (event) => {
+ setPassword(event.target.value);
}
- handleRegister() {
- // TODO implement registration
+ const handleRegister = () => {
+ // TODO: Implement registration
}
- handleLogin(event) {
+ const handleLogin = (event) => {
event.preventDefault();
- const { email, password } = this.state;
- fetch("/api/session", {
- method: "POST",
- body: new URLSearchParams(`email=${email}&password=${password}`)
- }).then(response => {
+ fetch('/api/session', { method: 'POST', body: new URLSearchParams(`email=${email}&password=${password}`) }).then(response => {
if (response.ok) {
- this.props.history.push('/'); // TODO avoid calling sessions twice
+ history.push('/'); // TODO: Avoid calling sessions twice
} else {
- this.setState({
- failed: true,
- password: ""
- });
+ setFailed(true);
+ setPassword('');
}
});
}
- render() {
- const { classes } = this.props;
- const { failed, email, password } = this.state;
- return (
- <main className={classes.root}>
- <Paper className={classes.paper}>
-
- <img className={classes.logo} src="/logo.svg" alt="Traccar" />
-
- <form onSubmit={this.handleLogin}>
-
- <FormControl margin="normal" required fullWidth error={failed}>
- <InputLabel htmlFor="email">{t('userEmail')}</InputLabel>
- <Input
- id="email"
- value={email}
- autoComplete="email"
- autoFocus
- onChange={this.handleChange} />
- { failed && <FormHelperText>Invalid username or password</FormHelperText> }
- </FormControl>
-
- <FormControl margin="normal" required fullWidth>
- <InputLabel htmlFor="password">{t('userPassword')}</InputLabel>
- <Input
- id="password"
- type="password"
- value={password}
- autoComplete="current-password"
- onChange={this.handleChange} />
- </FormControl>
-
- <div className={classes.buttons}>
-
- <Button
- type="button"
- variant="contained"
- disabled
- className={classes.button}
- onClick={this.handleRegister}>
- {t('loginRegister')}
- </Button>
-
- <Button
- type="submit"
- variant="contained"
- color="primary"
- disabled={!email || !password}
- className={classes.button}>
- {t('loginLogin')}
- </Button>
-
- </div>
-
- </form>
-
- </Paper>
- </main>
- );
- }
+ return (
+ <main className={classes.root}>
+ <Paper className={classes.paper}>
+ <img className={classes.logo} src="/logo.svg" alt="Traccar" />
+ <form onSubmit={handleLogin}>
+ <FormControl margin="normal" required fullWidth error={failed}>
+ <InputLabel htmlFor="email">{t('userEmail')}</InputLabel>
+ <Input
+ id="email"
+ name="email"
+ value={email}
+ autoComplete="email"
+ autoFocus
+ onChange={handleEmailChange} />
+ {failed && <FormHelperText>Invalid username or password</FormHelperText>}
+ </FormControl>
+
+ <FormControl margin="normal" required fullWidth>
+ <InputLabel htmlFor="password">{t('userPassword')}</InputLabel>
+ <Input
+ id="password"
+ name="password"
+ type="password"
+ value={password}
+ autoComplete="current-password"
+ onChange={handlePasswordChange} />
+ </FormControl>
+
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ variant="contained"
+ disabled
+ className={classes.button}
+ onClick={handleRegister}>
+ {t('loginRegister')}
+ </Button>
+
+ <Button
+ type="submit"
+ variant="contained"
+ color="primary"
+ disabled={!email || !password}
+ className={classes.button}>
+ {t('loginLogin')}
+ </Button>
+
+ </div>
+ </form>
+ </Paper>
+ </main>
+ );
}
-export default withStyles(styles)(LoginPage);
+export default LoginPage;
diff --git a/modern/src/MainPage.js b/modern/src/MainPage.js
index e0b4da2c..c70e8169 100644
--- a/modern/src/MainPage.js
+++ b/modern/src/MainPage.js
@@ -1,14 +1,16 @@
-import React, { Component } from 'react';
+import React, { useEffect, useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import { isWidthUp, makeStyles, withWidth } from '@material-ui/core';
+import Drawer from '@material-ui/core/Drawer';
import ContainerDimensions from 'react-container-dimensions';
-import MainToobar from './MainToolbar';
+
+import DeviceList from './DeviceList';
import MainMap from './MainMap';
-import Drawer from '@material-ui/core/Drawer';
-import withStyles from '@material-ui/core/styles/withStyles';
+import MainToobar from './MainToolbar';
import SocketController from './SocketController';
-import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
-import DeviceList from './DeviceList';
-const styles = theme => ({
+
+const useStyles = makeStyles(theme => ({
root: {
height: "100vh",
display: "flex",
@@ -35,57 +37,42 @@ const styles = theme => ({
mapContainer: {
flexGrow: 1
}
-});
+}));
-class MainPage extends Component {
- constructor(props) {
- super(props);
- this.state = {
- loading: true
- };
- }
+const MainPage = ({ width }) => {
+ const [loading, setLoading] = useState(true);
+ const classes = useStyles();
+ const history = useHistory();
- componentDidMount() {
+ useEffect(() => {
fetch('/api/session').then(response => {
if (response.ok) {
- this.setState({
- loading: false
- });
+ setLoading(false);
} else {
- this.props.history.push('/login');
+ history.push('/login');
}
});
- }
+ }, [history]);
- render() {
- const { classes } = this.props;
- const { loading } = this.state;
- if (loading) {
- return (
- <div>Loading...</div>
- );
- } else {
- return (
- <div className={classes.root}>
- <SocketController />
- <MainToobar history={this.props.history} />
- <div className={classes.content}>
- <Drawer
- anchor={isWidthUp('sm', this.props.width) ? "left" : "bottom"}
- variant="permanent"
- classes={{ paper: classes.drawerPaper }}>
- <DeviceList history={this.props.history} />
- </Drawer>
- <div className={classes.mapContainer}>
- <ContainerDimensions>
- <MainMap/>
- </ContainerDimensions>
- </div>
- </div>
+ return loading ? (<div>Loading...</div>) : (
+ <div className={classes.root}>
+ <SocketController />
+ <MainToobar />
+ <div className={classes.content}>
+ <Drawer
+ anchor={isWidthUp('sm', width) ? "left" : "bottom"}
+ variant="permanent"
+ classes={{ paper: classes.drawerPaper }}>
+ <DeviceList />
+ </Drawer>
+ <div className={classes.mapContainer}>
+ <ContainerDimensions>
+ <MainMap />
+ </ContainerDimensions>
</div>
- );
- }
- }
+ </div>
+ </div>
+ );
}
-export default withWidth()(withStyles(styles)(MainPage));
+export default withWidth()(MainPage);
diff --git a/modern/src/MainToolbar.js b/modern/src/MainToolbar.js
index fc3281cd..bf4aa402 100644
--- a/modern/src/MainToolbar.js
+++ b/modern/src/MainToolbar.js
@@ -1,6 +1,6 @@
-import t from './common/localization'
-import React, { Component, Fragment } from 'react';
-import { withStyles } from '@material-ui/core/styles';
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
@@ -17,8 +17,9 @@ import ListItemText from '@material-ui/core/ListItemText';
import DashboardIcon from '@material-ui/icons/Dashboard';
import BarChartIcon from '@material-ui/icons/BarChart';
import SettingsIcon from '@material-ui/icons/Settings';
+import t from './common/localization';
-const styles = theme => ({
+const useStyles = makeStyles(theme => ({
flex: {
flexGrow: 1
},
@@ -32,149 +33,126 @@ const styles = theme => ({
marginLeft: -12,
marginRight: 20,
}
-});
+}));
-class MainToobar extends Component {
- constructor(props) {
- super(props);
- this.state = {
- drawer: false
- };
- this.openDrawer = this.openDrawer.bind(this);
- this.closeDrawer = this.closeDrawer.bind(this);
- this.handleLogout = this.handleLogout.bind(this);
- }
-
- openDrawer() {
- this.setState({
- drawer: true
- });
- };
+const MainToolbar = () => {
+ const [drawer, setDrawer] = useState(false);
+ const classes = useStyles();
+ const history = useHistory();
- closeDrawer() {
- this.setState({
- drawer: false
- });
- };
+ const openDrawer = () => { setDrawer(true) }
+ const closeDrawer = () => { setDrawer(false) }
- handleLogout() {
- fetch("/api/session", {
- method: "DELETE"
- }).then(response => {
+ const handleLogout = () => {
+ fetch('/api/session', { method: 'DELETE' }).then(response => {
if (response.ok) {
- this.props.history.push('/login');
+ history.push('/login');
}
- });
+ })
}
- render() {
- const { classes } = this.props;
- return (
- <Fragment>
- <AppBar position="static" className={classes.appBar}>
- <Toolbar>
- <IconButton
- className={classes.menuButton}
- color="inherit"
- onClick={this.openDrawer}>
- <MenuIcon />
- </IconButton>
- <Typography variant="h6" color="inherit" className={classes.flex}>
- Traccar
- </Typography>
- <Button color="inherit" onClick={this.handleLogout}>{t('loginLogout')}</Button>
- </Toolbar>
- </AppBar>
- <Drawer open={this.state.drawer} onClose={this.closeDrawer}>
- <div
- tabIndex={0}
- className={classes.list}
- role="button"
- onClick={this.closeDrawer}
- onKeyDown={this.closeDrawer}>
- <List>
- <ListItem button onClick={() => this.props.history.push('/')}>
- <ListItemIcon>
- <DashboardIcon />
- </ListItemIcon>
- <ListItemText primary={t('mapTitle')} />
- </ListItem>
- </List>
- <Divider />
- <List
- subheader={
- <ListSubheader>
- {t('reportTitle')}
- </ListSubheader>
- }>
- <ListItem button onClick={() => this.props.history.push('/reports/route')}>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportRoute')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportEvents')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportTrips')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportStops')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportSummary')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('reportChart')} />
- </ListItem>
- </List>
- <Divider />
- <List
- subheader={
- <ListSubheader>
- {t('settingsTitle')}
- </ListSubheader>
- }>
- <ListItem button disabled>
- <ListItemIcon>
- <SettingsIcon />
- </ListItemIcon>
- <ListItemText primary={t('settingsUser')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <SettingsIcon />
- </ListItemIcon>
- <ListItemText primary={t('settingsServer')} />
- </ListItem>
- <ListItem button disabled>
- <ListItemIcon>
- <SettingsIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedNotifications')} />
- </ListItem>
- </List>
- </div>
- </Drawer>
- </Fragment>
- );
- }
+ return (
+ <>
+ <AppBar position="static" className={classes.appBar}>
+ <Toolbar>
+ <IconButton
+ className={classes.menuButton}
+ color="inherit"
+ onClick={openDrawer}>
+ <MenuIcon />
+ </IconButton>
+ <Typography variant="h6" color="inherit" className={classes.flex}>
+ Traccar
+ </Typography>
+ <Button color="inherit" onClick={handleLogout}>{t('loginLogout')}</Button>
+ </Toolbar>
+ </AppBar>
+ <Drawer open={drawer} onClose={closeDrawer}>
+ <div
+ tabIndex={0}
+ className={classes.list}
+ role="button"
+ onClick={closeDrawer}
+ onKeyDown={closeDrawer}>
+ <List>
+ <ListItem button onClick={() => history.push('/')}>
+ <ListItemIcon>
+ <DashboardIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('mapTitle')} />
+ </ListItem>
+ </List>
+ <Divider />
+ <List subheader={<ListSubheader>
+ {t('reportTitle')}
+ </ListSubheader>}>
+ <ListItem button onClick={() => { history.push('/reports/route') }}>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportRoute')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportEvents')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportTrips')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportStops')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportSummary')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <BarChartIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('reportChart')} />
+ </ListItem>
+ </List>
+ <Divider />
+ <List
+ subheader={
+ <ListSubheader>
+ {t('settingsTitle')}
+ </ListSubheader>
+ }>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <SettingsIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('settingsUser')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <SettingsIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('settingsServer')} />
+ </ListItem>
+ <ListItem button disabled>
+ <ListItemIcon>
+ <SettingsIcon />
+ </ListItemIcon>
+ <ListItemText primary={t('sharedNotifications')} />
+ </ListItem>
+ </List>
+ </div>
+ </Drawer>
+ </>
+ );
}
-export default withStyles(styles)(MainToobar);
+export default MainToolbar;