diff options
author | Anton Tananaev <anton@traccar.org> | 2022-05-08 13:16:57 -0700 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2022-05-08 13:16:57 -0700 |
commit | 2cd374bb9fa941d7e2a6fd8aa5079893a158c98f (patch) | |
tree | f4ee48130592fed5de25dce7af4ac0cbeb017680 /modern/src/reports | |
parent | 2352071211b61c10fa5bf5736baaff7809d18bf0 (diff) | |
download | trackermap-web-2cd374bb9fa941d7e2a6fd8aa5079893a158c98f.tar.gz trackermap-web-2cd374bb9fa941d7e2a6fd8aa5079893a158c98f.tar.bz2 trackermap-web-2cd374bb9fa941d7e2a6fd8aa5079893a158c98f.zip |
Reorganize remaining files
Diffstat (limited to 'modern/src/reports')
-rw-r--r-- | modern/src/reports/ChartReportPage.js | 8 | ||||
-rw-r--r-- | modern/src/reports/EventReportPage.js | 6 | ||||
-rw-r--r-- | modern/src/reports/ReplayPage.js | 220 | ||||
-rw-r--r-- | modern/src/reports/RouteReportPage.js | 6 | ||||
-rw-r--r-- | modern/src/reports/StatisticsPage.js | 145 | ||||
-rw-r--r-- | modern/src/reports/StopReportPage.js | 6 | ||||
-rw-r--r-- | modern/src/reports/SummaryReportPage.js | 6 | ||||
-rw-r--r-- | modern/src/reports/TripReportPage.js | 6 | ||||
-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: { |