aboutsummaryrefslogtreecommitdiff
path: root/modern/src/reports
diff options
context:
space:
mode:
Diffstat (limited to 'modern/src/reports')
-rw-r--r--modern/src/reports/ChartReportPage.js8
-rw-r--r--modern/src/reports/EventReportPage.js6
-rw-r--r--modern/src/reports/ReplayPage.js220
-rw-r--r--modern/src/reports/RouteReportPage.js6
-rw-r--r--modern/src/reports/StatisticsPage.js145
-rw-r--r--modern/src/reports/StopReportPage.js6
-rw-r--r--modern/src/reports/SummaryReportPage.js6
-rw-r--r--modern/src/reports/TripReportPage.js6
-rw-r--r--modern/src/reports/components/Graph.js (renamed from modern/src/reports/Graph.js)0
-rw-r--r--modern/src/reports/components/ReportFilter.js (renamed from modern/src/reports/ReportFilter.js)2
-rw-r--r--modern/src/reports/components/ReportLayout.js (renamed from modern/src/reports/ReportLayout.js)6
11 files changed, 168 insertions, 243 deletions
diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js
index 8803f2be..70429ec5 100644
--- a/modern/src/reports/ChartReportPage.js
+++ b/modern/src/reports/ChartReportPage.js
@@ -2,13 +2,13 @@ import React, { useState } from 'react';
import {
Grid, FormControl, InputLabel, Select, MenuItem,
} from '@material-ui/core';
-import ReportLayout from './ReportLayout';
-import ReportFilter from './ReportFilter';
-import Graph from './Graph';
+import ReportLayout from './components/ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import Graph from './components/Graph';
import { useAttributePreference } from '../common/util/preferences';
import { formatDate } from '../common/util/formatter';
import { speedFromKnots } from '../common/util/converter';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ children, setItems }) => {
const speedUnit = useAttributePreference('speedUnit');
diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js
index 45bb4c25..bbd92d59 100644
--- a/modern/src/reports/EventReportPage.js
+++ b/modern/src/reports/EventReportPage.js
@@ -6,10 +6,10 @@ import {
import { useTheme } from '@material-ui/core/styles';
import { useSelector } from 'react-redux';
import { formatDate } from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import ReportLayout from './ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import ReportLayout from './components/ReportLayout';
import { prefixString } from '../common/util/stringUtils';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ setItems }) => {
const t = useTranslation();
diff --git a/modern/src/reports/ReplayPage.js b/modern/src/reports/ReplayPage.js
deleted file mode 100644
index a8d4844f..00000000
--- a/modern/src/reports/ReplayPage.js
+++ /dev/null
@@ -1,220 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import {
- Grid, FormControlLabel, Switch, IconButton, TextField, makeStyles, Paper, Slider, Toolbar, Tooltip, Typography,
-} from '@material-ui/core';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import SettingsIcon from '@material-ui/icons/Settings';
-import PlayArrowIcon from '@material-ui/icons/PlayArrow';
-import PauseIcon from '@material-ui/icons/Pause';
-import FastForwardIcon from '@material-ui/icons/FastForward';
-import FastRewindIcon from '@material-ui/icons/FastRewind';
-import { useHistory } from 'react-router-dom';
-import { useSelector } from 'react-redux';
-import Map from '../map/core/Map';
-import ReplayPathMap from '../map/ReplayPathMap';
-import PositionsMap from '../map/PositionsMap';
-import { formatTime } from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import { useTranslation } from '../LocalizationProvider';
-
-const useStyles = makeStyles((theme) => ({
- root: {
- height: '100%',
- },
- sidebar: {
- position: 'absolute',
- left: 0,
- top: 0,
- margin: theme.spacing(1.5),
- width: theme.dimensions.drawerWidthDesktop,
- [theme.breakpoints.down('sm')]: {
- width: '100%',
- margin: 0,
- },
- },
- formControlLabel: {
- height: '100%',
- width: '100%',
- paddingRight: theme.spacing(1),
- justifyContent: 'space-between',
- alignItems: 'center',
- },
- reportFilterContainer: {
- flex: 1,
- padding: theme.spacing(2),
- [theme.breakpoints.down('sm')]: {
- margin: theme.spacing(1),
- },
- },
- sliderContainer: {
- padding: theme.spacing(2),
- },
-}));
-
-const TimeLabel = ({ children, open, value }) => (
- <Tooltip open={open} enterTouchDelay={0} placement="top" title={value}>
- {children}
- </Tooltip>
-);
-
-const ReplayPage = () => {
- const t = useTranslation();
- const classes = useStyles();
- const history = useHistory();
- const timerRef = useRef();
-
- const [positions, setPositions] = useState([]);
- const [index, setIndex] = useState(0);
- const [selectedDeviceId, setSelectedDeviceId] = useState();
- const [playbackSpeed, setPlaybackSpeed] = useState('');
- const [expanded, setExpanded] = useState(true);
- const [isPlaying, setIsPlaying] = useState(false);
-
- const deviceName = useSelector((state) => {
- if (selectedDeviceId) {
- const device = state.devices.items[selectedDeviceId];
- if (device) {
- return device.name;
- }
- }
- return null;
- });
-
- useEffect(() => {
- if (isPlaying && positions.length > 0) {
- timerRef.current = setInterval(() => {
- setIndex((index) => index + 1);
- }, 500);
- } else {
- clearInterval(timerRef.current);
- }
-
- return () => clearInterval(timerRef.current);
- }, [isPlaying, positions]);
-
- useEffect(() => {
- if (index >= positions.length) {
- clearInterval(timerRef.current);
- }
- }, [index, positions]);
-
- const handleSubmit = async (deviceId, from, to, _, headers) => {
- setSelectedDeviceId(deviceId);
- const query = new URLSearchParams({ deviceId, from, to });
- const response = await fetch(`/api/positions?${query.toString()}`, { headers });
- if (response.ok) {
- setIndex(0);
- setPositions(await response.json());
- setExpanded(false);
- }
- };
-
- return (
- <div className={classes.root}>
- <Map>
- <ReplayPathMap positions={positions} />
- {index < positions.length && <PositionsMap positions={[positions[index]]} />}
- </Map>
- <div className={classes.sidebar}>
- <Grid container direction="column" spacing={1}>
- <Grid item>
- <Paper elevation={3} square>
- <Toolbar disableGutters>
- <Grid container alignItems="center">
- <Grid item>
- <IconButton onClick={() => history.push('/')}>
- <ArrowBackIcon />
- </IconButton>
- </Grid>
- <Grid item xs>
- <Typography variant="h6">{t('reportReplay')}</Typography>
- </Grid>
- {!expanded && (
- <Grid item>
- <IconButton onClick={() => setExpanded(true)}>
- <SettingsIcon />
- </IconButton>
- </Grid>
- )}
- </Grid>
- </Toolbar>
- </Paper>
- </Grid>
- <Grid item>
- {!expanded ? (
- <Paper className={classes.sliderContainer}>
- <Grid container direction="column" alignItems="center">
- <Grid item>
- {deviceName}
- </Grid>
- <Grid item style={{ width: '100%' }}>
- <Slider
- max={positions.length - 1}
- step={null}
- marks={positions.map((_, index) => ({ value: index }))}
- value={index}
- onChange={(_, index) => setIndex(index)}
- valueLabelDisplay="auto"
- valueLabelFormat={(i) => (i < positions.length ? formatTime(positions[i]) : '')}
- ValueLabelComponent={TimeLabel}
- />
- </Grid>
- <Grid item container justifyContent="space-between" alignItems="center">
- <Grid item xs={2}>{`${index}/${positions.length}`}</Grid>
- <Grid item xs={2}>
- <IconButton onClick={() => setIndex((index) => index - 1)} disabled={isPlaying}>
- <FastRewindIcon />
- </IconButton>
- </Grid>
- <Grid item xs={2}>
- <IconButton onClick={() => setIsPlaying(!isPlaying)}>
- {isPlaying ? <PauseIcon /> : <PlayArrowIcon /> }
- </IconButton>
- </Grid>
- <Grid item xs={2}>
- <IconButton onClick={() => setIndex((index) => index + 1)} disabled={isPlaying}>
- <FastForwardIcon />
- </IconButton>
- </Grid>
- <Grid item xs>{formatTime(positions[index])}</Grid>
- </Grid>
- </Grid>
- </Paper>
- ) : (
- <Paper elevation={3} className={classes.reportFilterContainer} square>
- <ReportFilter handleSubmit={handleSubmit} fullScreen showOnly>
- <Grid item xs={6}>
- <TextField
- fullWidth
- label={t('reportPlaybackPerMinute')}
- value={playbackSpeed}
- onChange={(e) => setPlaybackSpeed(e.target.value)}
- variant="filled"
- />
- </Grid>
- <Grid item xs={6}>
- <FormControlLabel
- classes={{ root: classes.formControlLabel }}
- control={(
- <Switch
- checked={isPlaying}
- onChange={(e) => setIsPlaying(e.target.checked)}
- color="primary"
- edge="start"
- />
- )}
- label={t('reportAutoPlay')}
- labelPlacement="start"
- />
- </Grid>
- </ReportFilter>
- </Paper>
- )}
- </Grid>
- </Grid>
- </div>
- </div>
- );
-};
-
-export default ReplayPage;
diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js
index a3549924..035f6acd 100644
--- a/modern/src/reports/RouteReportPage.js
+++ b/modern/src/reports/RouteReportPage.js
@@ -5,10 +5,10 @@ import { useTheme } from '@material-ui/core/styles';
import {
formatDistance, formatSpeed, formatBoolean, formatDate, formatCoordinate,
} from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import ReportLayout from './ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import ReportLayout from './components/ReportLayout';
import { useAttributePreference, usePreference } from '../common/util/preferences';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ setItems }) => {
const handleSubmit = async (deviceId, from, to, mail, headers) => {
diff --git a/modern/src/reports/StatisticsPage.js b/modern/src/reports/StatisticsPage.js
new file mode 100644
index 00000000..4b0d9bfe
--- /dev/null
+++ b/modern/src/reports/StatisticsPage.js
@@ -0,0 +1,145 @@
+import React, { useState } from 'react';
+import {
+ FormControl, InputLabel, Select, MenuItem, TextField, Button, TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@material-ui/core';
+import moment from 'moment';
+import { formatDate } from '../common/util/formatter';
+import OptionsLayout from '../settings/components/OptionsLayout';
+import { useTranslation } from '../common/components/LocalizationProvider';
+
+const Filter = ({ setItems }) => {
+ const t = useTranslation();
+
+ const [period, setPeriod] = useState('today');
+ const [from, setFrom] = useState(moment().subtract(1, 'hour'));
+ const [to, setTo] = useState(moment());
+
+ const handleClick = async () => {
+ 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({ from: selectedFrom.toISOString(), to: selectedTo.toISOString() });
+ const response = await fetch(`/api/statistics?${query.toString()}`, { Accept: 'application/json' });
+ if (response.ok) {
+ setItems(await response.json());
+ }
+ };
+
+ return (
+ <>
+ <FormControl variant="filled" margin="normal" fullWidth>
+ <InputLabel>{t('reportPeriod')}</InputLabel>
+ <Select value={period} onChange={(e) => setPeriod(e.target.value)}>
+ <MenuItem value="today">{t('reportToday')}</MenuItem>
+ <MenuItem value="yesterday">{t('reportYesterday')}</MenuItem>
+ <MenuItem value="thisWeek">{t('reportThisWeek')}</MenuItem>
+ <MenuItem value="previousWeek">{t('reportPreviousWeek')}</MenuItem>
+ <MenuItem value="thisMonth">{t('reportThisMonth')}</MenuItem>
+ <MenuItem value="previousMonth">{t('reportPreviousMonth')}</MenuItem>
+ <MenuItem value="custom">{t('reportCustom')}</MenuItem>
+ </Select>
+ </FormControl>
+ {period === 'custom' && (
+ <TextField
+ margin="normal"
+ variant="filled"
+ label={t('reportFrom')}
+ type="datetime-local"
+ value={from.format(moment.HTML5_FMT.DATETIME_LOCAL)}
+ onChange={(e) => setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))}
+ fullWidth
+ />
+ )}
+ {period === 'custom' && (
+ <TextField
+ margin="normal"
+ variant="filled"
+ label={t('reportTo')}
+ type="datetime-local"
+ value={to.format(moment.HTML5_FMT.DATETIME_LOCAL)}
+ onChange={(e) => setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))}
+ fullWidth
+ />
+ )}
+ <Button variant="contained" color="primary" onClick={handleClick} fullWidth>{t('reportShow')}</Button>
+ </>
+ );
+};
+
+const StatisticsPage = () => {
+ const t = useTranslation();
+
+ const [items, setItems] = useState([]);
+
+ return (
+ <OptionsLayout>
+ <Filter setItems={setItems} />
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('statisticsCaptureTime')}</TableCell>
+ <TableCell>{t('statisticsActiveUsers')}</TableCell>
+ <TableCell>{t('statisticsActiveDevices')}</TableCell>
+ <TableCell>{t('statisticsRequests')}</TableCell>
+ <TableCell>{t('statisticsMessagesReceived')}</TableCell>
+ <TableCell>{t('statisticsMessagesStored')}</TableCell>
+ <TableCell>{t('notificatorMail')}</TableCell>
+ <TableCell>{t('notificatorSms')}</TableCell>
+ <TableCell>{t('statisticsGeocoder')}</TableCell>
+ <TableCell>{t('statisticsGeolocation')}</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{formatDate(item.captureTime)}</TableCell>
+ <TableCell>{item.activeUsers}</TableCell>
+ <TableCell>{item.activeDevices}</TableCell>
+ <TableCell>{item.requests}</TableCell>
+ <TableCell>{item.messagesReceived}</TableCell>
+ <TableCell>{item.messagesStored}</TableCell>
+ <TableCell>{item.mailSent}</TableCell>
+ <TableCell>{item.smsSent}</TableCell>
+ <TableCell>{item.geocoderRequests}</TableCell>
+ <TableCell>{item.geolocationRequests}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ </OptionsLayout>
+ );
+};
+
+export default StatisticsPage;
diff --git a/modern/src/reports/StopReportPage.js b/modern/src/reports/StopReportPage.js
index b0c5169f..0dca99b3 100644
--- a/modern/src/reports/StopReportPage.js
+++ b/modern/src/reports/StopReportPage.js
@@ -4,10 +4,10 @@ import { useTheme } from '@material-ui/core/styles';
import {
formatDistance, formatHours, formatDate, formatVolume,
} from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import ReportLayout from './ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import ReportLayout from './components/ReportLayout';
import { useAttributePreference } from '../common/util/preferences';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ setItems }) => {
const handleSubmit = async (deviceId, from, to, mail, headers) => {
diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js
index f86a55fe..fe6f4aa1 100644
--- a/modern/src/reports/SummaryReportPage.js
+++ b/modern/src/reports/SummaryReportPage.js
@@ -5,10 +5,10 @@ import { useTheme } from '@material-ui/core/styles';
import {
formatDistance, formatHours, formatDate, formatSpeed, formatVolume,
} from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import ReportLayout from './ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import ReportLayout from './components/ReportLayout';
import { useAttributePreference } from '../common/util/preferences';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ setItems }) => {
const t = useTranslation();
diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js
index 7a74e673..a87031d3 100644
--- a/modern/src/reports/TripReportPage.js
+++ b/modern/src/reports/TripReportPage.js
@@ -4,10 +4,10 @@ import { useTheme } from '@material-ui/core/styles';
import {
formatDistance, formatSpeed, formatHours, formatDate, formatVolume,
} from '../common/util/formatter';
-import ReportFilter from './ReportFilter';
-import ReportLayout from './ReportLayout';
+import ReportFilter from './components/ReportFilter';
+import ReportLayout from './components/ReportLayout';
import { useAttributePreference } from '../common/util/preferences';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../common/components/LocalizationProvider';
const Filter = ({ setItems }) => {
const handleSubmit = async (deviceId, from, to, mail, headers) => {
diff --git a/modern/src/reports/Graph.js b/modern/src/reports/components/Graph.js
index 63d24eee..63d24eee 100644
--- a/modern/src/reports/Graph.js
+++ b/modern/src/reports/components/Graph.js
diff --git a/modern/src/reports/ReportFilter.js b/modern/src/reports/components/ReportFilter.js
index 827b36fb..bc9c5af6 100644
--- a/modern/src/reports/ReportFilter.js
+++ b/modern/src/reports/components/ReportFilter.js
@@ -4,7 +4,7 @@ import {
} from '@material-ui/core';
import { useSelector } from 'react-redux';
import moment from 'moment';
-import { useTranslation } from '../LocalizationProvider';
+import { useTranslation } from '../../common/components/LocalizationProvider';
const ReportFilter = ({ children, handleSubmit, showOnly }) => {
const t = useTranslation();
diff --git a/modern/src/reports/ReportLayout.js b/modern/src/reports/components/ReportLayout.js
index b2be2ac1..c028530b 100644
--- a/modern/src/reports/ReportLayout.js
+++ b/modern/src/reports/components/ReportLayout.js
@@ -11,9 +11,9 @@ import FormatListBulletedIcon from '@material-ui/icons/FormatListBulleted';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import SideNav from '../common/components/SideNav';
-import NavBar from '../common/components/NavBar';
-import { useTranslation } from '../LocalizationProvider';
+import SideNav from '../../common/components/SideNav';
+import NavBar from '../../common/components/NavBar';
+import { useTranslation } from '../../common/components/LocalizationProvider';
const useStyles = makeStyles((theme) => ({
root: {