aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-05-28 11:11:11 -0700
committerAnton Tananaev <anton@traccar.org>2022-05-28 11:11:11 -0700
commitb0ff8b904d1f48087da4c7c86849829e899a6281 (patch)
tree845e2e929006a9fbe75e199ebbf8f7e87e4ebdd5 /modern
parent1f9806dc45dbeea3904a679bf38b92ca20a92d4a (diff)
downloadtrackermap-web-b0ff8b904d1f48087da4c7c86849829e899a6281.tar.gz
trackermap-web-b0ff8b904d1f48087da4c7c86849829e899a6281.tar.bz2
trackermap-web-b0ff8b904d1f48087da4c7c86849829e899a6281.zip
Multidevice summary report
Diffstat (limited to 'modern')
-rw-r--r--modern/.eslintrc.js6
-rw-r--r--modern/src/other/ReplayPage.js2
-rw-r--r--modern/src/reports/ChartReportPage.js6
-rw-r--r--modern/src/reports/EventReportPage.js6
-rw-r--r--modern/src/reports/StatisticsPage.js2
-rw-r--r--modern/src/reports/StopReportPage.js6
-rw-r--r--modern/src/reports/SummaryReportPage.js18
-rw-r--r--modern/src/reports/TripReportPage.js6
-rw-r--r--modern/src/reports/components/ReportFilter.js53
9 files changed, 68 insertions, 37 deletions
diff --git a/modern/.eslintrc.js b/modern/.eslintrc.js
index 6cef63d8..df0dcd15 100644
--- a/modern/.eslintrc.js
+++ b/modern/.eslintrc.js
@@ -16,6 +16,12 @@ module.exports = {
'no-nested-ternary': [0],
'operator-linebreak': [0],
'import/no-unresolved': [0],
+ 'object-curly-newline': [1, {
+ ObjectExpression: { minProperties: 8, multiline: true, consistent: true },
+ ObjectPattern: { minProperties: 8, multiline: true, consistent: true },
+ ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
+ ExportDeclaration: { minProperties: 4, multiline: true, consistent: true },
+ }],
'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
'react/function-component-definition': [1, { namedComponents: 'arrow-function', unnamedComponents: 'arrow-function' }],
'react/prop-types': [0],
diff --git a/modern/src/other/ReplayPage.js b/modern/src/other/ReplayPage.js
index 4893dda9..fb6e00a5 100644
--- a/modern/src/other/ReplayPage.js
+++ b/modern/src/other/ReplayPage.js
@@ -123,7 +123,7 @@ const ReplayPage = () => {
}
}, [index, positions]);
- const handleSubmit = useCatch(async (deviceId, from, to, _, headers) => {
+ const handleSubmit = useCatch(async ({ deviceId, from, to, headers }) => {
setSelectedDeviceId(deviceId);
const query = new URLSearchParams({ deviceId, from, to });
const response = await fetch(`/api/positions?${query.toString()}`, { headers });
diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js
index ce50819b..ceb2361b 100644
--- a/modern/src/reports/ChartReportPage.js
+++ b/modern/src/reports/ChartReportPage.js
@@ -34,10 +34,8 @@ const ChartReportPage = () => {
const maxValue = Math.max(...values);
const valueRange = maxValue - minValue;
- const handleSubmit = useCatch(async (deviceId, from, to, mail, headers) => {
- const query = new URLSearchParams({
- deviceId, from, to, mail,
- });
+ const handleSubmit = useCatch(async ({ deviceId, from, to, mail, headers }) => {
+ const query = new URLSearchParams({ deviceId, from, to, mail });
const response = await fetch(`/api/reports/route?${query.toString()}`, { headers });
if (response.ok) {
const positions = await response.json();
diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js
index 447adaef..61b55fcc 100644
--- a/modern/src/reports/EventReportPage.js
+++ b/modern/src/reports/EventReportPage.js
@@ -54,10 +54,8 @@ const EventReportPage = () => {
const [eventTypes, setEventTypes] = useState(['allEvents']);
const [items, setItems] = useState([]);
- const handleSubmit = useCatch(async (deviceId, from, to, mail, headers) => {
- const query = new URLSearchParams({
- deviceId, from, to, mail,
- });
+ const handleSubmit = useCatch(async ({ deviceId, from, to, mail, headers }) => {
+ const query = new URLSearchParams({ deviceId, from, to, mail });
eventTypes.forEach((it) => query.append('type', it));
const response = await fetch(`/api/reports/events?${query.toString()}`, { headers });
if (response.ok) {
diff --git a/modern/src/reports/StatisticsPage.js b/modern/src/reports/StatisticsPage.js
index 5da5b44e..41b8925d 100644
--- a/modern/src/reports/StatisticsPage.js
+++ b/modern/src/reports/StatisticsPage.js
@@ -33,7 +33,7 @@ const StatisticsPage = () => {
const [columns, setColumns] = usePersistedState('statisticsColumns', ['captureTime', 'activeUsers', 'activeDevices', 'messagesStored']);
const [items, setItems] = useState([]);
- const handleSubmit = useCatch(async (_, from, to) => {
+ const handleSubmit = useCatch(async ({ from, to }) => {
const query = new URLSearchParams({ from, to });
const response = await fetch(`/api/statistics?${query.toString()}`, { Accept: 'application/json' });
if (response.ok) {
diff --git a/modern/src/reports/StopReportPage.js b/modern/src/reports/StopReportPage.js
index d44e2046..00c8a41b 100644
--- a/modern/src/reports/StopReportPage.js
+++ b/modern/src/reports/StopReportPage.js
@@ -43,10 +43,8 @@ const StopReportPage = () => {
const [items, setItems] = useState([]);
const [selectedItem, setSelectedItem] = useState(null);
- const handleSubmit = useCatch(async (deviceId, from, to, mail, headers) => {
- const query = new URLSearchParams({
- deviceId, from, to, mail,
- });
+ const handleSubmit = useCatch(async ({ deviceId, from, to, mail, headers }) => {
+ const query = new URLSearchParams({ deviceId, from, to, mail });
const response = await fetch(`/api/reports/stops?${query.toString()}`, { headers });
if (response.ok) {
const contentType = response.headers.get('content-type');
diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js
index a66d1c61..ca18b828 100644
--- a/modern/src/reports/SummaryReportPage.js
+++ b/modern/src/reports/SummaryReportPage.js
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
import {
FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableBody, TableCell,
} from '@mui/material';
@@ -16,6 +17,7 @@ import { useCatch } from '../reactHelper';
import useReportStyles from './common/useReportStyles';
const columnsArray = [
+ ['deviceId', 'sharedDevice'],
['startTime', 'reportStartDate'],
['distance', 'sharedDistance'],
['startOdometer', 'reportStartOdometer'],
@@ -31,18 +33,20 @@ const SummaryReportPage = () => {
const classes = useReportStyles();
const t = useTranslation();
+ const devices = useSelector((state) => state.devices.items);
+
const distanceUnit = useAttributePreference('distanceUnit');
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
- const [columns, setColumns] = usePersistedState('summaryColumns', ['startTime', 'startOdometer', 'distance', 'averageSpeed']);
+ const [columns, setColumns] = usePersistedState('summaryColumns', ['deviceId', 'startTime', 'distance', 'averageSpeed']);
const [daily, setDaily] = useState(false);
const [items, setItems] = useState([]);
- const handleSubmit = useCatch(async (deviceId, from, to, mail, headers) => {
- const query = new URLSearchParams({
- deviceId, from, to, daily, mail,
- });
+ const handleSubmit = useCatch(async ({ deviceIds, groupIds, from, to, mail, headers }) => {
+ const query = new URLSearchParams({ from, to, daily, mail });
+ deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
+ groupIds.forEach((groupId) => query.append('groupId', groupId));
const response = await fetch(`/api/reports/summary?${query.toString()}`, { headers });
if (response.ok) {
const contentType = response.headers.get('content-type');
@@ -60,6 +64,8 @@ const SummaryReportPage = () => {
const formatValue = (item, key) => {
switch (key) {
+ case 'deviceId':
+ return devices[item[key]].name;
case 'startTime':
return item[key] ? formatDate(item[key], 'YYYY-MM-DD') : null;
case 'startOdometer':
@@ -81,7 +87,7 @@ const SummaryReportPage = () => {
return (
<PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportSummary']}>
<div className={classes.header}>
- <ReportFilter handleSubmit={handleSubmit}>
+ <ReportFilter handleSubmit={handleSubmit} multiDevice>
<div className={classes.filterItem}>
<FormControl fullWidth>
<InputLabel>{t('sharedType')}</InputLabel>
diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js
index 47f91ee3..28ec8cc9 100644
--- a/modern/src/reports/TripReportPage.js
+++ b/modern/src/reports/TripReportPage.js
@@ -68,10 +68,8 @@ const TripReportPage = () => {
}
}, [selectedItem]);
- const handleSubmit = useCatch(async (deviceId, from, to, mail, headers) => {
- const query = new URLSearchParams({
- deviceId, from, to, mail,
- });
+ const handleSubmit = useCatch(async ({ deviceId, from, to, mail, headers }) => {
+ const query = new URLSearchParams({ deviceId, from, to, mail });
const response = await fetch(`/api/reports/trips?${query.toString()}`, { headers });
if (response.ok) {
const contentType = response.headers.get('content-type');
diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js
index 29d53dd0..100dab90 100644
--- a/modern/src/reports/components/ReportFilter.js
+++ b/modern/src/reports/components/ReportFilter.js
@@ -7,20 +7,23 @@ import moment from 'moment';
import { useTranslation } from '../../common/components/LocalizationProvider';
import useReportStyles from '../common/useReportStyles';
-const ReportFilter = ({
- children, handleSubmit, showOnly, ignoreDevice,
-}) => {
+const ReportFilter = ({ children, handleSubmit, showOnly, ignoreDevice, multiDevice }) => {
const classes = useReportStyles();
const t = useTranslation();
const devices = useSelector((state) => state.devices.items);
+ const groups = useSelector((state) => state.groups.items);
const selectedDeviceId = useSelector((state) => state.devices.selectedId);
const [deviceId, setDeviceId] = useState(selectedDeviceId);
+ const [deviceIds, setDeviceIds] = useState(selectedDeviceId ? [selectedDeviceId] : []);
+ const [groupIds, setGroupIds] = useState([]);
const [period, setPeriod] = useState('today');
const [from, setFrom] = useState(moment().subtract(1, 'hour'));
const [to, setTo] = useState(moment());
+ const disabled = !ignoreDevice && !deviceId && !deviceIds.length && !groupIds.length;
+
const handleClick = (mail, json) => {
let selectedFrom;
let selectedTo;
@@ -56,13 +59,15 @@ const ReportFilter = ({
}
const accept = json ? 'application/json' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
- handleSubmit(
+ handleSubmit({
deviceId,
- selectedFrom.toISOString(),
- selectedTo.toISOString(),
+ deviceIds,
+ groupIds,
+ from: selectedFrom.toISOString(),
+ to: selectedTo.toISOString(),
mail,
- { Accept: accept },
- );
+ headers: { Accept: accept },
+ });
};
return (
@@ -70,8 +75,13 @@ const ReportFilter = ({
{!ignoreDevice && (
<div className={classes.filterItem}>
<FormControl fullWidth>
- <InputLabel>{t('reportDevice')}</InputLabel>
- <Select label={t('reportDevice')} value={deviceId || ''} onChange={(e) => setDeviceId(e.target.value)}>
+ <InputLabel>{t(multiDevice ? 'deviceTitle' : 'reportDevice')}</InputLabel>
+ <Select
+ label={t(multiDevice ? 'deviceTitle' : 'reportDevice')}
+ value={multiDevice ? deviceIds : deviceId || ''}
+ onChange={(e) => (multiDevice ? setDeviceIds(e.target.value) : setDeviceId(e.target.value))}
+ multiple={multiDevice}
+ >
{Object.values(devices).map((device) => (
<MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
))}
@@ -79,6 +89,23 @@ const ReportFilter = ({
</FormControl>
</div>
)}
+ {multiDevice && (
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('settingsGroups')}</InputLabel>
+ <Select
+ label={t('settingsGroups')}
+ value={groupIds}
+ onChange={(e) => setGroupIds(e.target.value)}
+ multiple
+ >
+ {Object.values(groups).map((group) => (
+ <MenuItem key={group.id} value={group.id}>{group.name}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ </div>
+ )}
<div className={classes.filterItem}>
<FormControl fullWidth>
<InputLabel>{t('reportPeriod')}</InputLabel>
@@ -122,7 +149,7 @@ const ReportFilter = ({
variant="outlined"
color="secondary"
className={classes.filterButton}
- disabled={!ignoreDevice && !deviceId}
+ disabled={disabled}
>
{t('reportShow')}
</Button>
@@ -132,7 +159,7 @@ const ReportFilter = ({
variant="outlined"
color="secondary"
className={classes.filterButton}
- disabled={!ignoreDevice && !deviceId}
+ disabled={disabled}
>
{t('reportExport')}
</Button>
@@ -143,7 +170,7 @@ const ReportFilter = ({
variant="outlined"
color="secondary"
className={classes.filterButton}
- disabled={!ignoreDevice && !deviceId}
+ disabled={disabled}
>
<Typography variant="button" noWrap>{t('reportEmail')}</Typography>
</Button>