aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modern/src/common/attributes/useUserAttributes.js8
-rw-r--r--modern/src/map/MapCamera.js26
-rw-r--r--modern/src/map/MapGeofenceEdit.js2
-rw-r--r--modern/src/map/MapRoutePath.js9
-rw-r--r--modern/src/map/main/MapDefaultCamera.js2
-rw-r--r--modern/src/other/ReplayPage.js2
-rw-r--r--modern/src/reports/RouteReportPage.js18
-rw-r--r--modern/src/reports/SummaryReportPage.js7
-rw-r--r--modern/src/reports/TripReportPage.js2
-rw-r--r--modern/src/reports/components/ReportFilter.js4
-rw-r--r--web/l10n/en.json2
11 files changed, 55 insertions, 27 deletions
diff --git a/modern/src/common/attributes/useUserAttributes.js b/modern/src/common/attributes/useUserAttributes.js
index a100539e..81230884 100644
--- a/modern/src/common/attributes/useUserAttributes.js
+++ b/modern/src/common/attributes/useUserAttributes.js
@@ -5,6 +5,14 @@ export default (t) => useMemo(() => ({
name: t('attributeTelegramChatId'),
type: 'string',
},
+ pushoverUserKey: {
+ name: t('attributePushoverUserKey'),
+ type: 'string',
+ },
+ pushoverDeviceNames: {
+ name: t('attributePushoverDeviceNames'),
+ type: 'string',
+ },
'mail.smtp.host': {
name: t('attributeMailSmtpHost'),
type: 'string',
diff --git a/modern/src/map/MapCamera.js b/modern/src/map/MapCamera.js
index 7ebf24fb..63f070f8 100644
--- a/modern/src/map/MapCamera.js
+++ b/modern/src/map/MapCamera.js
@@ -1,16 +1,28 @@
import { useEffect } from 'react';
-
+import maplibregl from 'maplibre-gl';
import { map } from './core/MapView';
const MapCamera = ({
- latitude, longitude,
+ latitude, longitude, positions,
}) => {
useEffect(() => {
- map.jumpTo({
- center: [longitude, latitude],
- zoom: Math.max(map.getZoom(), 10),
- });
- }, [latitude, longitude]);
+ if (positions) {
+ const coordinates = positions.map((item) => [item.longitude, item.latitude]);
+ if (coordinates.length) {
+ const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[1]));
+ const canvas = map.getCanvas();
+ map.fitBounds(bounds, {
+ padding: Math.min(canvas.width, canvas.height) * 0.1,
+ duration: 0,
+ });
+ }
+ } else {
+ map.jumpTo({
+ center: [longitude, latitude],
+ zoom: Math.max(map.getZoom(), 10),
+ });
+ }
+ }, [latitude, longitude, positions]);
return null;
};
diff --git a/modern/src/map/MapGeofenceEdit.js b/modern/src/map/MapGeofenceEdit.js
index c387a086..a7e07483 100644
--- a/modern/src/map/MapGeofenceEdit.js
+++ b/modern/src/map/MapGeofenceEdit.js
@@ -148,7 +148,7 @@ const MapGeofenceEdit = ({ selectedGeofenceId }) => {
}
const bounds = coordinates.reduce(
(bounds, coordinate) => bounds.extend(coordinate),
- new maplibregl.LngLatBounds(coordinates[0], coordinates[0]),
+ new maplibregl.LngLatBounds(coordinates[0], coordinates[1]),
);
const canvas = map.getCanvas();
map.fitBounds(bounds, { padding: Math.min(canvas.width, canvas.height) * 0.1 });
diff --git a/modern/src/map/MapRoutePath.js b/modern/src/map/MapRoutePath.js
index d2d6827c..22f8d335 100644
--- a/modern/src/map/MapRoutePath.js
+++ b/modern/src/map/MapRoutePath.js
@@ -1,5 +1,4 @@
import { useTheme } from '@mui/styles';
-import maplibregl from 'maplibre-gl';
import { useId, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { map } from './core/MapView';
@@ -70,14 +69,6 @@ const MapRoutePath = ({ positions }) => {
color: reportColor,
},
});
- if (coordinates.length) {
- const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[0]));
- const canvas = map.getCanvas();
- map.fitBounds(bounds, {
- padding: Math.min(canvas.width, canvas.height) * 0.1,
- duration: 0,
- });
- }
}, [positions, reportColor]);
return null;
diff --git a/modern/src/map/main/MapDefaultCamera.js b/modern/src/map/main/MapDefaultCamera.js
index bb9ea404..f6b6ed9c 100644
--- a/modern/src/map/main/MapDefaultCamera.js
+++ b/modern/src/map/main/MapDefaultCamera.js
@@ -27,7 +27,7 @@ const MapDefaultCamera = () => {
} else {
const coordinates = Object.values(positions).map((item) => [item.longitude, item.latitude]);
if (coordinates.length > 1) {
- const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[0]));
+ const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[1]));
const canvas = map.getCanvas();
map.fitBounds(bounds, {
duration: 0,
diff --git a/modern/src/other/ReplayPage.js b/modern/src/other/ReplayPage.js
index e49835d6..556f5a87 100644
--- a/modern/src/other/ReplayPage.js
+++ b/modern/src/other/ReplayPage.js
@@ -21,6 +21,7 @@ import { formatTime } from '../common/util/formatter';
import ReportFilter from '../reports/components/ReportFilter';
import { useTranslation } from '../common/components/LocalizationProvider';
import { useCatch } from '../reactHelper';
+import MapCamera from '../map/MapCamera';
const useStyles = makeStyles((theme) => ({
root: {
@@ -155,6 +156,7 @@ const ReplayPage = () => {
<MapPositions positions={[positions[index]]} onClick={onClick} />
)}
</MapView>
+ <MapCamera positions={positions} />
<div className={classes.sidebar}>
<Paper elevation={3} square>
<Toolbar>
diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js
index c901cf66..d60b241c 100644
--- a/modern/src/reports/RouteReportPage.js
+++ b/modern/src/reports/RouteReportPage.js
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
import {
IconButton, Table, TableBody, TableCell, TableHead, TableRow,
} from '@mui/material';
@@ -18,6 +19,7 @@ import MapRoutePath from '../map/MapRoutePath';
import MapPositions from '../map/MapPositions';
import useReportStyles from './common/useReportStyles';
import TableShimmer from '../common/components/TableShimmer';
+import MapCamera from '../map/MapCamera';
const RouteReportPage = () => {
const classes = useReportStyles();
@@ -25,13 +27,16 @@ const RouteReportPage = () => {
const positionAttributes = usePositionAttributes(t);
+ const devices = useSelector((state) => state.devices.items);
+
const [columns, setColumns] = usePersistedState('routeColumns', ['fixTime', 'latitude', 'longitude', 'speed', 'address']);
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
- const handleSubmit = useCatch(async ({ deviceId, from, to, type }) => {
- const query = new URLSearchParams({ deviceId, from, to });
+ const handleSubmit = useCatch(async ({ deviceIds, from, to, type }) => {
+ const query = new URLSearchParams({ from, to });
+ deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
if (type === 'export') {
window.location.assign(`/api/reports/route/xlsx?${query.toString()}`);
} else if (type === 'mail') {
@@ -62,14 +67,17 @@ const RouteReportPage = () => {
{selectedItem && (
<div className={classes.containerMap}>
<MapView>
- <MapRoutePath positions={items} />
+ {[...new Set(items.map((it) => it.deviceId))].map((deviceId) => (
+ <MapRoutePath key={deviceId} positions={items.filter((position) => position.deviceId === deviceId)} />
+ ))}
<MapPositions positions={[selectedItem]} />
</MapView>
+ <MapCamera positions={items} />
</div>
)}
<div className={classes.containerMain}>
<div className={classes.header}>
- <ReportFilter handleSubmit={handleSubmit}>
+ <ReportFilter handleSubmit={handleSubmit} multiDevice>
<ColumnSelect
columns={columns}
setColumns={setColumns}
@@ -81,6 +89,7 @@ const RouteReportPage = () => {
<TableHead>
<TableRow>
<TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedDevice')}</TableCell>
{columns.map((key) => (<TableCell key={key}>{positionAttributes[key].name}</TableCell>))}
</TableRow>
</TableHead>
@@ -98,6 +107,7 @@ const RouteReportPage = () => {
</IconButton>
)}
</TableCell>
+ <TableCell>{devices[item.deviceId].name}</TableCell>
{columns.map((key) => (
<TableCell key={key}>
<PositionValue
diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js
index b8ec9283..77c42cba 100644
--- a/modern/src/reports/SummaryReportPage.js
+++ b/modern/src/reports/SummaryReportPage.js
@@ -18,7 +18,6 @@ import useReportStyles from './common/useReportStyles';
import TableShimmer from '../common/components/TableShimmer';
const columnsArray = [
- ['deviceId', 'sharedDevice'],
['startTime', 'reportStartDate'],
['distance', 'sharedDistance'],
['startOdometer', 'reportStartOdometer'],
@@ -40,7 +39,7 @@ const SummaryReportPage = () => {
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
- const [columns, setColumns] = usePersistedState('summaryColumns', ['deviceId', 'startTime', 'distance', 'averageSpeed']);
+ const [columns, setColumns] = usePersistedState('summaryColumns', ['startTime', 'distance', 'averageSpeed']);
const [daily, setDaily] = useState(false);
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
@@ -98,7 +97,7 @@ const SummaryReportPage = () => {
return (
<PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportSummary']}>
<div className={classes.header}>
- <ReportFilter handleSubmit={handleSubmit} multiDevice>
+ <ReportFilter handleSubmit={handleSubmit} multiDevice includeGroups>
<div className={classes.filterItem}>
<FormControl fullWidth>
<InputLabel>{t('sharedType')}</InputLabel>
@@ -114,12 +113,14 @@ const SummaryReportPage = () => {
<Table>
<TableHead>
<TableRow>
+ <TableCell>{t('sharedDevice')}</TableCell>
{columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
</TableRow>
</TableHead>
<TableBody>
{!loading ? items.map((item) => (
<TableRow key={(`${item.deviceId}_${Date.parse(item.startTime)}`)}>
+ <TableCell>{devices[item.deviceId].name}</TableCell>
{columns.map((key) => (
<TableCell key={key}>
{formatValue(item, key)}
diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js
index 23455404..8aed01be 100644
--- a/modern/src/reports/TripReportPage.js
+++ b/modern/src/reports/TripReportPage.js
@@ -21,6 +21,7 @@ import MapRoutePath from '../map/MapRoutePath';
import AddressValue from '../common/components/AddressValue';
import TableShimmer from '../common/components/TableShimmer';
import MapMarkers from '../map/MapMarkers';
+import MapCamera from '../map/MapCamera';
const columnsArray = [
['startTime', 'reportStartTime'],
@@ -151,6 +152,7 @@ const TripReportPage = () => {
</>
)}
</MapView>
+ <MapCamera positions={route} />
</div>
)}
<div className={classes.containerMain}>
diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js
index 816fde8d..d223b086 100644
--- a/modern/src/reports/components/ReportFilter.js
+++ b/modern/src/reports/components/ReportFilter.js
@@ -7,7 +7,7 @@ import moment from 'moment';
import { useTranslation } from '../../common/components/LocalizationProvider';
import useReportStyles from '../common/useReportStyles';
-const ReportFilter = ({ children, handleSubmit, showOnly, ignoreDevice, multiDevice }) => {
+const ReportFilter = ({ children, handleSubmit, showOnly, ignoreDevice, multiDevice, includeGroups }) => {
const classes = useReportStyles();
const t = useTranslation();
@@ -87,7 +87,7 @@ const ReportFilter = ({ children, handleSubmit, showOnly, ignoreDevice, multiDev
</FormControl>
</div>
)}
- {multiDevice && (
+ {includeGroups && (
<div className={classes.filterItem}>
<FormControl fullWidth>
<InputLabel>{t('settingsGroups')}</InputLabel>
diff --git a/web/l10n/en.json b/web/l10n/en.json
index 528c66c5..712e32ff 100644
--- a/web/l10n/en.json
+++ b/web/l10n/en.json
@@ -110,6 +110,8 @@
"attributeWebSelectZoom": "Web: Zoom On Select",
"attributeWebMaxZoom": "Web: Maximum Zoom",
"attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
"attributeMailSmtpHost": "Mail: SMTP Host",
"attributeMailSmtpPort": "Mail: SMTP Port",
"attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",