diff options
-rw-r--r-- | modern/src/DeviceList.js | 137 | ||||
-rw-r--r-- | modern/src/LoginPage.js | 177 | ||||
-rw-r--r-- | modern/src/MainPage.js | 87 | ||||
-rw-r--r-- | modern/src/MainToolbar.js | 260 |
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; |