From d69bb2b2c3053c2c61e4e5d7029751debcfb0dd9 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 25 Jul 2020 12:36:19 -0700 Subject: Implement simple route report --- modern/src/App.js | 4 +- modern/src/DeviceList.js | 4 +- modern/src/DevicePage.js | 14 +-- modern/src/LoginPage.js | 22 ++--- modern/src/MainPage.js | 49 +++------- modern/src/MainToolbar.js | 11 ++- modern/src/RouteReportPage.js | 19 ---- modern/src/SocketController.js | 35 ++++--- modern/src/common/formatter.js | 23 +++++ modern/src/reports/RouteReportPage.js | 168 ++++++++++++++++++++++++++++++++++ 10 files changed, 260 insertions(+), 89 deletions(-) delete mode 100644 modern/src/RouteReportPage.js create mode 100644 modern/src/common/formatter.js create mode 100644 modern/src/reports/RouteReportPage.js (limited to 'modern/src') diff --git a/modern/src/App.js b/modern/src/App.js index 7921a2c..f45c64d 100644 --- a/modern/src/App.js +++ b/modern/src/App.js @@ -3,13 +3,15 @@ import { Switch, Route } from 'react-router-dom' import CssBaseline from '@material-ui/core/CssBaseline'; import MainPage from './MainPage'; import LoginPage from './LoginPage'; -import RouteReportPage from './RouteReportPage'; +import RouteReportPage from './reports/RouteReportPage'; import DevicePage from './DevicePage'; +import SocketController from './SocketController'; const App = () => { return ( <> + diff --git a/modern/src/DeviceList.js b/modern/src/DeviceList.js index 5e09041..66b9406 100644 --- a/modern/src/DeviceList.js +++ b/modern/src/DeviceList.js @@ -23,8 +23,8 @@ import RemoveDialog from './RemoveDialog'; const useStyles = makeStyles(theme => ({ list: { - maxHeight: '100%', - overflow: 'auto', + maxHeight: '100%', + overflow: 'auto', }, fab: { position: 'absolute', diff --git a/modern/src/DevicePage.js b/modern/src/DevicePage.js index c9362de..ee52549 100644 --- a/modern/src/DevicePage.js +++ b/modern/src/DevicePage.js @@ -72,28 +72,28 @@ const DevicePage = () => {
{(!id || device) && setName(event.target.value)} label={t('sharedName')} - variant="filled" /> + variant='filled' /> } {(!id || device) && setUniqueId(event.target.value)} label={t('deviceIdentifier')} - variant="filled" /> + variant='filled' /> } - +
- -
diff --git a/modern/src/LoginPage.js b/modern/src/LoginPage.js index 68ac31f..429a6e6 100644 --- a/modern/src/LoginPage.js +++ b/modern/src/LoginPage.js @@ -79,40 +79,40 @@ const LoginPage = () => { return (
- Traccar + Traccar - +
- -
diff --git a/modern/src/MainPage.js b/modern/src/MainPage.js index bc79247..1ab151f 100644 --- a/modern/src/MainPage.js +++ b/modern/src/MainPage.js @@ -1,72 +1,53 @@ -import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { sessionActions } from './store'; -import { useHistory } from 'react-router-dom'; +import React from 'react'; +import { useSelector } from 'react-redux'; import { isWidthUp, makeStyles, withWidth } from '@material-ui/core'; import Drawer from '@material-ui/core/Drawer'; import ContainerDimensions from 'react-container-dimensions'; import LinearProgress from '@material-ui/core/LinearProgress'; - import DeviceList from './DeviceList'; import MainMap from './MainMap'; import MainToobar from './MainToolbar'; -import SocketController from './SocketController'; const useStyles = makeStyles(theme => ({ root: { - height: "100vh", - display: "flex", - flexDirection: "column" + height: '100%', + display: 'flex', + flexDirection: 'column', }, content: { flexGrow: 1, - overflow: "hidden", - display: "flex", - flexDirection: "row", + overflow: 'hidden', + display: 'flex', + flexDirection: 'row', [theme.breakpoints.down('xs')]: { - flexDirection: "column-reverse" + flexDirection: 'column-reverse', } }, drawerPaper: { position: 'relative', [theme.breakpoints.up('sm')]: { - width: 350 + width: 350, }, [theme.breakpoints.down('xs')]: { - height: 250 + height: 250, } }, mapContainer: { - flexGrow: 1 - } + flexGrow: 1, + }, })); const MainPage = ({ width }) => { - const dispatch = useDispatch(); const authenticated = useSelector(state => state.session.authenticated); const classes = useStyles(); - const history = useHistory(); - - useEffect(() => { - if (!authenticated) { - fetch('/api/session').then(response => { - if (response.ok) { - dispatch(sessionActions.authenticated(true)); - } else { - history.push('/login'); - } - }); - } - }, [authenticated]); return !authenticated ? () : (
-
diff --git a/modern/src/MainToolbar.js b/modern/src/MainToolbar.js index f77868d..5994a38 100644 --- a/modern/src/MainToolbar.js +++ b/modern/src/MainToolbar.js @@ -87,10 +87,13 @@ const MainToolbar = () => { - - {t('reportTitle')} - }> - { history.push('/reports/route') }}> + + {t('reportTitle')} + + }> + { history.push('/reports/route') }}> diff --git a/modern/src/RouteReportPage.js b/modern/src/RouteReportPage.js deleted file mode 100644 index 6bbf01e..0000000 --- a/modern/src/RouteReportPage.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import MainToobar from './MainToolbar'; -import withStyles from '@material-ui/core/styles/withStyles'; -import withWidth from '@material-ui/core/withWidth'; -import { useHistory } from 'react-router-dom'; - -const styles = theme => ({}); - -const RouteReportPage = () => { - const history = useHistory(); - - return ( -
- -
- ); -} - -export default withWidth()(withStyles(styles)(RouteReportPage)); diff --git a/modern/src/SocketController.js b/modern/src/SocketController.js index 13ff0a9..94be52a 100644 --- a/modern/src/SocketController.js +++ b/modern/src/SocketController.js @@ -1,7 +1,8 @@ import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { connect } from 'react-redux'; -import { positionsActions, devicesActions } from './store'; +import { positionsActions, devicesActions, sessionActions } from './store'; +import { useHistory } from 'react-router-dom'; const displayNotifications = events => { if ("Notification" in window) { @@ -22,6 +23,8 @@ const displayNotifications = events => { const SocketController = () => { const dispatch = useDispatch(); + const history = useHistory(); + const authenticated = useSelector(state => state.session.authenticated); const connectSocket = () => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -46,15 +49,25 @@ const SocketController = () => { } useEffect(() => { - fetch('/api/devices').then(response => { - if (response.ok) { - response.json().then(devices => { - dispatch(devicesActions.update(devices)); - }); - } - connectSocket(); - }); - }, []); + if (authenticated) { + fetch('/api/devices').then(response => { + if (response.ok) { + response.json().then(devices => { + dispatch(devicesActions.update(devices)); + }); + } + connectSocket(); + }); + } else { + fetch('/api/session').then(response => { + if (response.ok) { + dispatch(sessionActions.authenticated(true)); + } else { + history.push('/login'); + } + }); + } + }, [authenticated]); return null; } diff --git a/modern/src/common/formatter.js b/modern/src/common/formatter.js new file mode 100644 index 0000000..ce46c6c --- /dev/null +++ b/modern/src/common/formatter.js @@ -0,0 +1,23 @@ +import moment from 'moment'; + +const formatValue = (key, value) => { + switch (key) { + case 'fixTime': + return moment(value).format('LLL'); + case 'latitude': + case 'longitude': + return value.toFixed(5); + case 'speed': + return value.toFixed(1); + default: + return value; + } +} + +export default (object, key) => { + if (object != null && typeof object == 'object') { + return formatValue(key, object[key]); + } else { + return formatValue(key, object); + } +}; diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js new file mode 100644 index 0000000..eb0ccc5 --- /dev/null +++ b/modern/src/reports/RouteReportPage.js @@ -0,0 +1,168 @@ +import React, { useState } from 'react'; +import MainToobar from '../MainToolbar'; +import { useHistory } from 'react-router-dom'; +import { Grid, TableContainer, Table, TableRow, TableCell, TableHead, TableBody, Paper, makeStyles, FormControl, InputLabel, Select, MenuItem, Button, TextField } from '@material-ui/core'; +import t from '../common/localization'; +import { useSelector } from 'react-redux'; +import moment from 'moment'; +import formatter from '../common/formatter'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%', + display: 'flex', + flexDirection: 'column', + }, + content: { + flex: 1, + overflow: 'auto', + padding: theme.spacing(2), + }, + form: { + padding: theme.spacing(1, 2, 2), + }, +})); + +const RouteReportPage = () => { + const history = useHistory(); + const classes = useStyles(); + const devices = useSelector(state => Object.values(state.devices.items)); + const [deviceId, setDeviceId] = useState(); + const [period, setPeriod] = useState('today'); + const [from, setFrom] = useState(moment().subtract(1, 'hour')); + const [to, setTo] = useState(moment()); + const [data, setData] = useState([]); + + const handleShow = () => { + let selectedFrom; + let selectedTo; + switch (period) { + case 'today': + selectedFrom = moment().startOf('day'); + selectedTo = moment().endOf('day'); + break; + case 'yesterday': + selectedFrom = moment().subtract(1, 'day').startOf('day'); + selectedTo = moment().subtract(1, 'day').endOf('day'); + break; + case 'thisWeek': + selectedFrom = moment().startOf('week'); + selectedTo = moment().endOf('week'); + break; + case 'previousWeek': + selectedFrom = moment().subtract(1, 'week').startOf('week'); + selectedTo = moment().subtract(1, 'week').endOf('week'); + break; + case 'thisMonth': + selectedFrom = moment().startOf('month'); + selectedTo = moment().endOf('month'); + break; + case 'previousMonth': + selectedFrom = moment().subtract(1, 'month').startOf('month'); + selectedTo = moment().subtract(1, 'month').endOf('month'); + break; + default: + selectedFrom = from; + selectedTo = to; + break; + } + const query = new URLSearchParams({ + deviceId, + from: selectedFrom.toISOString(), + to: selectedTo.toISOString(), + }); + fetch(`/api/reports/route?${query.toString()}`, { headers: { 'Accept': 'application/json' } }) + .then(response => { + if (response.ok) { + response.json().then(setData); + } + }); + } + + return ( +
+ +
+ + + + + {t('reportDevice')} + + + + {t('reportPeriod')} + + + {period === 'custom' && + setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth /> + } + {period === 'custom' && + setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth /> + } + + + + + + + + + + + {t('positionFixTime')} + {t('positionLatitude')} + {t('positionLongitude')} + {t('positionSpeed')} + {t('positionAddress')} + + + + {data.map((item) => ( + + {formatter(item, 'fixTime')} + {formatter(item, 'latitude')} + {formatter(item, 'longitude')} + {formatter(item, 'speed')} + {formatter(item, 'address')} + + ))} + +
+
+
+
+
+
+ ); +} + +export default RouteReportPage; -- cgit v1.2.3