aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
Diffstat (limited to 'modern')
-rw-r--r--modern/public/styles.css4
-rw-r--r--modern/src/common/formatter.js22
-rw-r--r--modern/src/map/PositionsMap.js11
-rw-r--r--modern/src/map/StatusView.js177
4 files changed, 161 insertions, 53 deletions
diff --git a/modern/public/styles.css b/modern/public/styles.css
index e7aee866..3d925d15 100644
--- a/modern/public/styles.css
+++ b/modern/public/styles.css
@@ -10,3 +10,7 @@ canvas {
.mapboxgl-ctrl {
margin: 10px;
}
+
+.maplibregl-popup {
+ max-width: 330px!important;
+}
diff --git a/modern/src/common/formatter.js b/modern/src/common/formatter.js
index 3c0341b7..f0bb2e11 100644
--- a/modern/src/common/formatter.js
+++ b/modern/src/common/formatter.js
@@ -98,3 +98,25 @@ export const formatCoordinate = (key, value, unit) => {
return `${value.toFixed(6)}°`;
}
};
+
+export const getStatusColor = (status) => {
+ switch (status) {
+ case 'online':
+ return 'green';
+ case 'offline':
+ return 'red';
+ case 'unknown':
+ default:
+ return 'gray';
+ }
+};
+
+export const getBatteryStatus = (batteryLevel) => {
+ if (batteryLevel >= 70) {
+ return 'green';
+ }
+ if (batteryLevel > 30) {
+ return 'gray';
+ }
+ return 'red';
+};
diff --git a/modern/src/map/PositionsMap.js b/modern/src/map/PositionsMap.js
index 2de01d2c..9719b45b 100644
--- a/modern/src/map/PositionsMap.js
+++ b/modern/src/map/PositionsMap.js
@@ -1,5 +1,6 @@
import React, { useCallback, useEffect } from 'react';
import ReactDOM from 'react-dom';
+import { ThemeProvider } from '@material-ui/core/styles';
import maplibregl from 'maplibre-gl';
import { Provider, useSelector } from 'react-redux';
@@ -7,6 +8,7 @@ import { useHistory } from 'react-router-dom';
import { map } from './Map';
import store from '../store';
import StatusView from './StatusView';
+import theme from '../theme';
const PositionsMap = ({ positions }) => {
const id = 'positions';
@@ -49,7 +51,14 @@ const PositionsMap = ({ positions }) => {
const placeholder = document.createElement('div');
ReactDOM.render(
<Provider store={store}>
- <StatusView deviceId={feature.properties.deviceId} onShowDetails={(positionId) => history.push(`/position/${positionId}`)} />
+ <ThemeProvider theme={theme}>
+ <StatusView
+ deviceId={feature.properties.deviceId}
+ onShowDetails={(positionId) => history.push(`/position/${positionId}`)}
+ onShowHistory={() => history.push('/replay')}
+ onEditClick={(deviceId) => history.push(`/device/${deviceId}`)}
+ />
+ </ThemeProvider>
</Provider>,
placeholder,
);
diff --git a/modern/src/map/StatusView.js b/modern/src/map/StatusView.js
index c0f723d2..735fbab4 100644
--- a/modern/src/map/StatusView.js
+++ b/modern/src/map/StatusView.js
@@ -1,70 +1,143 @@
-import React from 'react';
+import React, { useState } from 'react';
+import {
+ makeStyles, Paper, IconButton, Grid, Button,
+} from '@material-ui/core';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
+import ListItemText from '@material-ui/core/ListItemText';
import { useSelector } from 'react-redux';
+
+import ReplayIcon from '@material-ui/icons/Replay';
+import ExitToAppIcon from '@material-ui/icons/ExitToApp';
+import EditIcon from '@material-ui/icons/Edit';
+import DeleteIcon from '@material-ui/icons/Delete';
+
+import {
+ formatPosition, getStatusColor, getBatteryStatus, formatDistance, formatSpeed,
+} from '../common/formatter';
+import { useAttributePreference } from '../common/preferences';
+import RemoveDialog from '../RemoveDialog';
import t from '../common/localization';
import { formatPosition } from '../common/formatter';
import { getPosition } from '../common/selectors';
-const StatusView = ({ deviceId, onShowDetails }) => {
+const useStyles = makeStyles((theme) => ({
+ paper: {
+ width: '300px',
+ },
+ green: {
+ color: theme.palette.common.green,
+ },
+ red: {
+ color: theme.palette.common.red,
+ },
+ gray: {
+ color: theme.palette.common.gray,
+ },
+ listItemContainer: {
+ maxWidth: '240px',
+ },
+}));
+
+const StatusView = ({
+ deviceId, onShowDetails, onShowHistory, onEditClick,
+}) => {
+ const classes = useStyles();
+
+ const [removeDialogShown, setRemoveDialogShown] = useState(false);
const device = useSelector((state) => state.devices.items[deviceId]);
const position = useSelector(getPosition(deviceId));
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const speedUnit = useAttributePreference('speedUnit');
+
const handleClick = (e) => {
e.preventDefault();
onShowDetails(position.id);
};
+ const handleEditClick = (e) => {
+ e.preventDefault();
+ onEditClick(deviceId);
+ };
+
+ const handleRemove = () => {
+ setRemoveDialogShown(true);
+ };
+
+ const handleRemoveResult = () => {
+ setRemoveDialogShown(false);
+ };
+
return (
<>
- <b>
- {t('deviceStatus')}
- :
- </b>
- {' '}
- {formatPosition(device.status, 'status')}
- <br />
- <b>
- {t('sharedLocation')}
- :
- </b>
- {' '}
- {formatPosition(position, 'latitude')}
- {' '}
- {formatPosition(position, 'longitude')}
- <br />
- <b>
- {t('positionSpeed')}
- :
- </b>
- {' '}
- {formatPosition(position.speed, 'speed')}
- <br />
- <b>
- {t('positionCourse')}
- :
- </b>
- {' '}
- {formatPosition(position.course, 'course')}
- <br />
- <b>
- {t('positionDistance')}
- :
- </b>
- {' '}
- {formatPosition(position.attributes.totalDistance, 'distance')}
- <br />
- {position.attributes.batteryLevel
- && (
- <>
- <b>
- {t('positionBattery')}
- :
- </b>
- {' '}
- {formatPosition(position.attributes.batteryLevel, 'batteryLevel')}
- <br />
- </>
- )}
- <a href="/" onClick={handleClick}>{t('sharedShowDetails')}</a>
+ <Paper className={classes.paper} elevation={0} square>
+ <Grid container direction="column">
+ <Grid item>
+ <List>
+ <ListItem classes={{ container: classes.listItemContainer }}>
+ <ListItemText primary={t('deviceStatus')} />
+ <ListItemSecondaryAction>
+ <span className={classes[getStatusColor(device.status)]}>{device.status}</span>
+ </ListItemSecondaryAction>
+ </ListItem>
+ <ListItem classes={{ container: classes.listItemContainer }}>
+ <ListItemText primary={t('positionSpeed')} />
+ <ListItemSecondaryAction>
+ <span>{formatSpeed(position.speed, speedUnit)}</span>
+ </ListItemSecondaryAction>
+ </ListItem>
+ {position.attributes.batteryLevel && (
+ <ListItem classes={{ container: classes.listItemContainer }}>
+ <ListItemText primary={t('positionBattery')} />
+ <ListItemSecondaryAction>
+ <span className={classes[getBatteryStatus(position.attributes.batteryLevel)]}>{formatPosition(position.attributes.batteryLevel, 'batteryLevel')}</span>
+ </ListItemSecondaryAction>
+ </ListItem>
+ )}
+ <ListItem classes={{ container: classes.listItemContainer }}>
+ <ListItemText primary={t('positionDistance')} />
+ <ListItemSecondaryAction>
+ <span>{formatDistance(position.attributes.totalDistance, distanceUnit)}</span>
+ </ListItemSecondaryAction>
+ </ListItem>
+ <ListItem classes={{ container: classes.listItemContainer }}>
+ <ListItemText primary={t('positionCourse')} />
+ <ListItemSecondaryAction>
+ <span>{formatPosition(position.course, 'course')}</span>
+ </ListItemSecondaryAction>
+ </ListItem>
+ </List>
+ </Grid>
+ <Grid item container>
+ <Grid item>
+ <Button color="secondary" onClick={handleClick}>More Info</Button>
+ </Grid>
+ <Grid item>
+ <IconButton onClick={onShowHistory}>
+ <ReplayIcon />
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton>
+ <ExitToAppIcon />
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton onClick={handleEditClick}>
+ <EditIcon />
+ </IconButton>
+ </Grid>
+ <Grid item>
+ <IconButton onClick={handleRemove} className={classes.red}>
+ <DeleteIcon />
+ </IconButton>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Paper>
+ <RemoveDialog open={removeDialogShown} endpoint="devices" itemId={deviceId} onResult={handleRemoveResult} />
</>
);
};