diff options
-rw-r--r-- | modern/src/common/attributes/useUserAttributes.js | 8 | ||||
-rw-r--r-- | modern/src/map/MapCamera.js | 26 | ||||
-rw-r--r-- | modern/src/map/MapGeofenceEdit.js | 2 | ||||
-rw-r--r-- | modern/src/map/MapRoutePath.js | 9 | ||||
-rw-r--r-- | modern/src/map/main/MapDefaultCamera.js | 2 | ||||
-rw-r--r-- | modern/src/other/ReplayPage.js | 2 | ||||
-rw-r--r-- | modern/src/reports/RouteReportPage.js | 18 | ||||
-rw-r--r-- | modern/src/reports/SummaryReportPage.js | 7 | ||||
-rw-r--r-- | modern/src/reports/TripReportPage.js | 2 | ||||
-rw-r--r-- | modern/src/reports/components/ReportFilter.js | 4 | ||||
-rw-r--r-- | web/l10n/en.json | 2 |
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", |