aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
Diffstat (limited to 'modern')
-rw-r--r--modern/src/DevicesList.js77
-rw-r--r--modern/src/MainPage.js223
2 files changed, 258 insertions, 42 deletions
diff --git a/modern/src/DevicesList.js b/modern/src/DevicesList.js
index 85b936ce..c774cdfe 100644
--- a/modern/src/DevicesList.js
+++ b/modern/src/DevicesList.js
@@ -3,21 +3,23 @@ import { useDispatch, useSelector } from 'react-redux';
import { makeStyles } from '@material-ui/core/styles';
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 ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
+import Grid from '@material-ui/core/Grid';
import ListItemText from '@material-ui/core/ListItemText';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
+import BatteryFullIcon from '@material-ui/icons/BatteryFull';
+import VpnKeyIcon from '@material-ui/icons/VpnKey';
import { devicesActions } from './store';
import EditCollectionView from './EditCollectionView';
import { useEffectAsync } from './reactHelper';
+import { formatPosition } from './common/formatter';
-const useStyles = makeStyles(() => ({
+const useStyles = makeStyles(theme => ({
list: {
maxHeight: '100%',
},
@@ -26,8 +28,71 @@ const useStyles = makeStyles(() => ({
height: '25px',
filter: 'brightness(0) invert(1)',
},
+ batteryText: {
+ fontSize: '0.75rem',
+ fontWeight: 'normal',
+ lineHeight: '0.875rem'
+ },
+ green: {
+ color: theme.palette.common.green,
+ },
+ red: {
+ color: theme.palette.common.red,
+ },
+ gray: {
+ color: theme.palette.common.gray
+ }
}));
+const OnlineStatus =({ status }) => {
+
+ const classes = useStyles();
+
+ switch (status) {
+ case 'online':
+ return <span className={classes.green}>{status}</span>
+ case 'offline':
+ return <span className={classes.red}>{status}</span>
+ case 'unknown':
+ default:
+ return <span className={classes.gray}>{status}</span>
+ }
+}
+
+const DeviceStatus = ({ deviceId }) => {
+ const classes = useStyles();
+ let batteryClass = '';
+
+ const position = useSelector(state => state.positions.items[deviceId]);
+ if (!position) {
+ return null;
+ }
+ const batteryLevel = position.attributes.hasOwnProperty('batteryLevel') ? position.attributes.batteryLevel : 'undefined';
+
+ if (batteryLevel >= 70) {
+ batteryClass = classes.green;
+ } else if (batteryLevel > 30) {
+ batteryClass = classes.gray;
+ } else {
+ batteryClass = classes.red;
+ }
+ return (
+ <Grid container direction="row" alignItems="center" alignContent="center" spacing={1}>
+ {position.attributes.hasOwnProperty('ignition') && <Grid item>
+ <VpnKeyIcon className={`${position.attributes.ignition ? classes.green : classes.gray}`}/>
+ </Grid>}
+ {batteryLevel !== 'undefined' && <Grid item container xs alignItems="center" alignContent="center">
+ <Grid item>
+ <span className={classes.batteryText}>{formatPosition(batteryLevel, 'batteryLevel')}</span>
+ </Grid>
+ <Grid item>
+ <BatteryFullIcon className={batteryClass} />
+ </Grid>
+ </Grid>}
+ </Grid>
+ );
+}
+
const DeviceRow = ({ data, index, style }) => {
const classes = useStyles();
const dispatch = useDispatch();
@@ -44,11 +109,9 @@ const DeviceRow = ({ data, index, style }) => {
<img className={classes.icon} src={`images/icon/${item.category || 'default'}.svg`} alt="" />
</Avatar>
</ListItemAvatar>
- <ListItemText primary={item.name} secondary={item.uniqueId} />
+ <ListItemText primary={item.name} secondary={<OnlineStatus status={item.status} />} />
<ListItemSecondaryAction>
- <IconButton onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
+ <DeviceStatus deviceId={item.id} />
</ListItemSecondaryAction>
</ListItem>
{index < items.length - 1 ? <Divider /> : null}
diff --git a/modern/src/MainPage.js b/modern/src/MainPage.js
index 88608df7..9740227e 100644
--- a/modern/src/MainPage.js
+++ b/modern/src/MainPage.js
@@ -1,7 +1,19 @@
-import React from 'react';
-import { isWidthUp, makeStyles, withWidth } from '@material-ui/core';
-import Drawer from '@material-ui/core/Drawer';
-import ContainerDimensions from 'react-container-dimensions';
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import classnames from 'classnames';
+import { makeStyles, withWidth, Paper, Toolbar, Grid, TextField, IconButton, Button } from '@material-ui/core';
+
+import { useTheme } from '@material-ui/core/styles';
+import useMediaQuery from '@material-ui/core/useMediaQuery';
+import AddIcon from '@material-ui/icons/Add';
+import CloseIcon from '@material-ui/icons/Close';
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+import ListIcon from '@material-ui/icons/List';
+import ReplayIcon from '@material-ui/icons/Replay';
+import DescriptionIcon from '@material-ui/icons/Description';
+import ShuffleIcon from '@material-ui/icons/Shuffle';
+import PersonIcon from '@material-ui/icons/Person';
+
import DevicesList from './DevicesList';
import MainToolbar from './MainToolbar';
import Map from './map/Map';
@@ -10,22 +22,14 @@ import AccuracyMap from './map/AccuracyMap';
import GeofenceMap from './map/GeofenceMap';
import CurrentPositionsMap from './map/CurrentPositionsMap';
import CurrentLocationMap from './map/CurrentLocationMap';
+import t from './common/localization';
const useStyles = makeStyles((theme) => ({
root: {
- height: '100%',
+ height: '100vh',
display: 'flex',
flexDirection: 'column',
},
- content: {
- flexGrow: 1,
- overflow: 'hidden',
- display: 'flex',
- flexDirection: 'row',
- [theme.breakpoints.down('xs')]: {
- flexDirection: 'column-reverse',
- },
- },
drawerPaper: {
position: 'relative',
[theme.breakpoints.up('sm')]: {
@@ -36,36 +40,185 @@ const useStyles = makeStyles((theme) => ({
},
overflow: 'hidden',
},
- mapContainer: {
- flexGrow: 1,
+ listContainer: {
+ position: 'absolute',
+ left: theme.spacing(1.5),
+ top: theme.spacing(10.5),
+ width: theme.dimensions.drawerWidthDesktop,
+ zIndex: 1301,
+ height: '100%',
+ maxHeight: `calc(100vh - ${theme.spacing(20)}px)`,
+ [theme.breakpoints.down("md")]: {
+ top: theme.spacing(7),
+ left: '0px',
+ width: '100%',
+ maxHeight: `calc(100vh - ${theme.spacing(14)}px)`,
+ }
+ },
+ paper: {
+ borderRadius: '0px',
+ },
+ toolbar: {
+ paddingLeft: theme.spacing(2),
+ paddingRight: theme.spacing(2),
+ },
+ deviceList: {
+ height: '100%',
+ },
+ collapsed: {
+ transform: `translateX(-${360 + 16}px)`,
+ transition: 'transform .5s ease',
+ [theme.breakpoints.down("md")]: {
+ transform: `translateX(-100vw)`,
+ }
+ },
+ uncollapsed: {
+ transform: `translateX(${theme.spacing(1.5)})`,
+ transition: 'transform .5s ease',
+ [theme.breakpoints.down("md")]: {
+ transform: `translateX(0)`,
+ }
+ },
+ deviceButton: {
+ position: 'absolute',
+ left: theme.spacing(1),
+ top: theme.spacing(10.5),
+ borderRadius: '0px',
+ [theme.breakpoints.down("md")]: {
+ top: theme.spacing(7),
+ }
+ },
+ bottomMenuContainer: {
+ position: 'absolute',
+ left: theme.spacing(1.5),
+ bottom: theme.spacing(1.5),
+ width: theme.dimensions.drawerWidthDesktop,
+ zIndex: 1301,
+ [theme.breakpoints.down("md")]: {
+ bottom: theme.spacing(0),
+ left: '0px',
+ width: '100%'
+ }
+ },
+ menuButton: {
+ display: 'flex',
+ flexDirection: 'column',
},
+ iconText: {
+ fontSize: '0.75rem',
+ fontWeight: 'normal',
+ color: '#222222',
+ }
}));
const MainPage = ({ width }) => {
const classes = useStyles();
+ const history = useHistory();
+ const theme = useTheme();
+
+ const [deviceName, setDeviceName] = useState();
+ const [collapsed, setCollapsed] = useState(false);
+
+ const matchesMD = useMediaQuery(theme.breakpoints.down('md'));
+
+ const handleClose = () => {
+ setCollapsed(!collapsed);
+ }
return (
<div className={classes.root}>
<MainToolbar />
- <div className={classes.content}>
- <Drawer
- anchor={isWidthUp('sm', width) ? 'left' : 'bottom'}
- variant="permanent"
- classes={{ paper: classes.drawerPaper }}
- >
- <DevicesList />
- </Drawer>
- <div className={classes.mapContainer}>
- <ContainerDimensions>
- <Map>
- <CurrentLocationMap />
- <GeofenceMap />
- <AccuracyMap />
- <CurrentPositionsMap />
- <SelectedDeviceMap />
- </Map>
- </ContainerDimensions>
- </div>
+ <Map>
+ <CurrentLocationMap />
+ <GeofenceMap />
+ <AccuracyMap />
+ <CurrentPositionsMap />
+ <SelectedDeviceMap />
+ </Map>
+ {collapsed &&
+ <Button
+ variant="contained"
+ color={matchesMD ? "secondary": "default"}
+ className={classes.deviceButton}
+ onClick={handleClose}
+ startIcon={<ListIcon />}>
+ {t('deviceTitle')}
+ </Button>
+ }
+ <div className={`${classes.listContainer} ${collapsed ? classes.collapsed : classes.uncollapsed}`}>
+ <Grid container direction="column" spacing={matchesMD ? 0: 2} style={{height: '100%'}}>
+ <Grid item>
+ <Paper className={classes.paper}>
+ <Toolbar className={classes.toolbar} disableGutters>
+ <Grid container direction="row" alignItems="center" spacing={2}>
+ {matchesMD && <Grid item>
+ <IconButton onClick={handleClose}>
+ <ArrowBackIcon />
+ </IconButton>
+ </Grid>}
+ <Grid item xs>
+ <TextField
+ fullWidth
+ name='deviceName'
+ value={deviceName || ''}
+ autoComplete='deviceName'
+ autoFocus
+ onChange={event => setDeviceName(event.target.value)}
+ placeholder="Search Devices"
+ variant='filled' />
+ </Grid>
+ <Grid item>
+ <IconButton onClick={() => history.push('/device')}>
+ <AddIcon />
+ </IconButton>
+ </Grid>
+ {!matchesMD && <Grid item>
+ <IconButton onClick={handleClose}>
+ <CloseIcon />
+ </IconButton>
+ </Grid>}
+ </Grid>
+ </Toolbar>
+ </Paper>
+ </Grid>
+ <Grid item xs>
+ <Paper className={`${classes.deviceList} ${classes.paper}`}>
+ <DevicesList />
+ </Paper>
+ </Grid>
+ </Grid>
+ </div>
+ <div className={classes.bottomMenuContainer}>
+ <Paper className={classes.paper}>
+ <Toolbar className={classes.toolbar} disableGutters>
+ <Grid container justify="space-around">
+ <Grid item>
+ <IconButton classes={{ label: classes.menuButton }}>
+ <ReplayIcon />
+ <span className={classes.iconText}>{t('reportReplay')}</span>
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton classes={{ label: classes.menuButton }}>
+ <DescriptionIcon />
+ <span className={classes.iconText}>{t('reportTitle')}</span>
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton classes={{ label: classes.menuButton }}>
+ <ShuffleIcon />
+ <span className={classes.iconText}>Options</span>
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton classes={{ label: classes.menuButton }}>
+ <PersonIcon />
+ <span className={classes.iconText}>{t('settingsUser')}</span>
+ </IconButton>
+ </Grid>
+ </Grid>
+ </Toolbar>
+ </Paper>
</div>
</div>
);