diff options
Diffstat (limited to 'modern/src/reports')
-rw-r--r-- | modern/src/reports/CombinedReportPage.js | 91 | ||||
-rw-r--r-- | modern/src/reports/components/ReportsMenu.js | 7 |
2 files changed, 98 insertions, 0 deletions
diff --git a/modern/src/reports/CombinedReportPage.js b/modern/src/reports/CombinedReportPage.js new file mode 100644 index 00000000..a5d994fe --- /dev/null +++ b/modern/src/reports/CombinedReportPage.js @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { + Table, TableBody, TableCell, TableHead, TableRow, +} from '@mui/material'; +import ReportFilter from './components/ReportFilter'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import PageLayout from '../common/components/PageLayout'; +import ReportsMenu from './components/ReportsMenu'; +import { useCatch } from '../reactHelper'; +import MapView from '../map/core/MapView'; +import MapRoutePath from '../map/MapRoutePath'; +import useReportStyles from './common/useReportStyles'; +import TableShimmer from '../common/components/TableShimmer'; +import MapCamera from '../map/MapCamera'; +import MapGeofence from '../map/MapGeofence'; +import { formatTime } from '../common/util/formatter'; +import { usePreference } from '../common/util/preferences'; +import { prefixString } from '../common/util/stringUtils'; + +const CombinedReportPage = () => { + const classes = useReportStyles(); + const t = useTranslation(); + + const devices = useSelector((state) => state.devices.items); + + const hours12 = usePreference('twelveHourFormat'); + + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(false); + + const handleSubmit = useCatch(async ({ deviceIds, groupIds, from, to }) => { + const query = new URLSearchParams({ from, to }); + deviceIds.forEach((deviceId) => query.append('deviceId', deviceId)); + groupIds.forEach((groupId) => query.append('groupId', groupId)); + setLoading(true); + try { + const response = await fetch(`/api/reports/combined?${query.toString()}`); + if (response.ok) { + setItems(await response.json()); + } else { + throw Error(await response.text()); + } + } finally { + setLoading(false); + } + }); + + return ( + <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportRoute']}> + <div className={classes.container}> + {Boolean(items.length) && ( + <div className={classes.containerMap}> + <MapView> + <MapGeofence /> + {items.map((item) => ( + <MapRoutePath key={item.deviceId} coordinates={item.route} /> + ))} + </MapView> + <MapCamera coordinates={items.flatMap((item) => item.route)} /> + </div> + )} + <div className={classes.containerMain}> + <div className={classes.header}> + <ReportFilter handleSubmit={handleSubmit} showOnly multiDevice includeGroups /> + </div> + <Table> + <TableHead> + <TableRow> + <TableCell>{t('sharedDevice')}</TableCell> + <TableCell>{t('positionFixTime')}</TableCell> + <TableCell>{t('sharedType')}</TableCell> + </TableRow> + </TableHead> + <TableBody> + {!loading ? items.flatMap((item) => item.events.map((event, index) => ( + <TableRow key={event.id}> + <TableCell>{index ? '' : devices[item.deviceId].name}</TableCell> + <TableCell>{formatTime(event.eventTime, 'seconds', hours12)}</TableCell> + <TableCell>{t(prefixString('event', event.type))}</TableCell> + </TableRow> + ))) : (<TableShimmer columns={3} />)} + </TableBody> + </Table> + </div> + </div> + </PageLayout> + ); +}; + +export default CombinedReportPage; diff --git a/modern/src/reports/components/ReportsMenu.js b/modern/src/reports/components/ReportsMenu.js index a31c3afe..c65a4c12 100644 --- a/modern/src/reports/components/ReportsMenu.js +++ b/modern/src/reports/components/ReportsMenu.js @@ -11,6 +11,7 @@ import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import BarChartIcon from '@mui/icons-material/BarChart'; import RouteIcon from '@mui/icons-material/Route'; import EventRepeatIcon from '@mui/icons-material/EventRepeat'; +import StarIcon from '@mui/icons-material/Star'; import { Link, useLocation } from 'react-router-dom'; import { useTranslation } from '../../common/components/LocalizationProvider'; import { useAdministrator, useRestriction } from '../../common/util/permissions'; @@ -35,6 +36,12 @@ const ReportsMenu = () => { <> <List> <MenuItem + title={t('reportCombined')} + link="/reports/combined" + icon={<StarIcon />} + selected={location.pathname === '/reports/combined'} + /> + <MenuItem title={t('reportRoute')} link="/reports/route" icon={<TimelineIcon />} |