diff options
author | Anton Tananaev <anton@traccar.org> | 2022-05-14 16:58:54 -0700 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2022-05-14 16:58:54 -0700 |
commit | 41244e5354fd05ef92d94fda0ceab36b2c23e88d (patch) | |
tree | 9b653faf3d10ca083aa5cc5da962dc8716770246 /modern/src/reports | |
parent | 8091d156d3379784d8d2f023c0a26944274542fd (diff) | |
download | trackermap-web-41244e5354fd05ef92d94fda0ceab36b2c23e88d.tar.gz trackermap-web-41244e5354fd05ef92d94fda0ceab36b2c23e88d.tar.bz2 trackermap-web-41244e5354fd05ef92d94fda0ceab36b2c23e88d.zip |
Improve report filter
Diffstat (limited to 'modern/src/reports')
-rw-r--r-- | modern/src/reports/ChartReportPage.js | 57 | ||||
-rw-r--r-- | modern/src/reports/EventReportPage.js | 9 | ||||
-rw-r--r-- | modern/src/reports/StatisticsPage.js | 102 | ||||
-rw-r--r-- | modern/src/reports/SummaryReportPage.js | 22 | ||||
-rw-r--r-- | modern/src/reports/components/ReportFilter.js | 122 |
5 files changed, 117 insertions, 195 deletions
diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js index 4ddb5f75..57c7c689 100644 --- a/modern/src/reports/ChartReportPage.js +++ b/modern/src/reports/ChartReportPage.js @@ -1,8 +1,8 @@ import React, { useState } from 'react'; import { - Grid, FormControl, InputLabel, Select, MenuItem, + FormControl, InputLabel, Select, MenuItem, } from '@material-ui/core'; -import ReportFilter from './components/ReportFilter'; +import ReportFilter, { useFilterStyles } from './components/ReportFilter'; import Graph from './components/Graph'; import { useAttributePreference } from '../common/util/preferences'; import { formatDate } from '../common/util/formatter'; @@ -11,9 +11,15 @@ import { useTranslation } from '../common/components/LocalizationProvider'; import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; -const Filter = ({ children, setItems }) => { +const ChartReportPage = () => { + const classes = useFilterStyles(); + const t = useTranslation(); + const speedUnit = useAttributePreference('speedUnit'); + const [items, setItems] = useState([]); + const [type, setType] = useState('speed'); + const handleSubmit = async (deviceId, from, to, mail, headers) => { const query = new URLSearchParams({ deviceId, from, to, mail, @@ -30,42 +36,21 @@ const Filter = ({ children, setItems }) => { setItems(formattedPositions); } }; - return ( - <> - <ReportFilter handleSubmit={handleSubmit} showOnly /> - {children} - </> - ); -}; - -const ChartType = ({ type, setType }) => { - const t = useTranslation(); - - return ( - <Grid container spacing={3}> - <Grid item xs={12} sm={6}> - <FormControl variant="filled" margin="normal" fullWidth> - <InputLabel>{t('reportChartType')}</InputLabel> - <Select value={type} onChange={(e) => setType(e.target.value)}> - <MenuItem value="speed">{t('positionSpeed')}</MenuItem> - <MenuItem value="accuracy">{t('positionAccuracy')}</MenuItem> - <MenuItem value="altitude">{t('positionAltitude')}</MenuItem> - </Select> - </FormControl> - </Grid> - </Grid> - ); -}; - -const ChartReportPage = () => { - const [items, setItems] = useState([]); - const [type, setType] = useState('speed'); return ( <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportChart']}> - <Filter setItems={setItems}> - <ChartType type={type} setType={setType} /> - </Filter> + <ReportFilter handleSubmit={handleSubmit} showOnly> + <div className={classes.item}> + <FormControl variant="filled" fullWidth> + <InputLabel>{t('reportChartType')}</InputLabel> + <Select value={type} onChange={(e) => setType(e.target.value)}> + <MenuItem value="speed">{t('positionSpeed')}</MenuItem> + <MenuItem value="accuracy">{t('positionAccuracy')}</MenuItem> + <MenuItem value="altitude">{t('positionAltitude')}</MenuItem> + </Select> + </FormControl> + </div> + </ReportFilter> <Graph items={items} type={type} /> </PageLayout> ); diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js index 71ca08d9..33f656b6 100644 --- a/modern/src/reports/EventReportPage.js +++ b/modern/src/reports/EventReportPage.js @@ -1,18 +1,19 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; import { - Grid, FormControl, InputLabel, Select, MenuItem, + FormControl, InputLabel, Select, MenuItem, } from '@material-ui/core'; import { useTheme } from '@material-ui/core/styles'; import { useSelector } from 'react-redux'; import { formatDate } from '../common/util/formatter'; -import ReportFilter from './components/ReportFilter'; +import ReportFilter, { useFilterStyles } from './components/ReportFilter'; import { prefixString } from '../common/util/stringUtils'; import { useTranslation } from '../common/components/LocalizationProvider'; import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; const Filter = ({ setItems }) => { + const classes = useFilterStyles(); const t = useTranslation(); const [eventTypes, setEventTypes] = useState(['allEvents']); @@ -37,7 +38,7 @@ const Filter = ({ setItems }) => { return ( <ReportFilter handleSubmit={handleSubmit}> - <Grid item xs={12} sm={6}> + <div className={classes.item}> <FormControl variant="filled" fullWidth> <InputLabel>{t('reportEventTypes')}</InputLabel> <Select value={eventTypes} onChange={(e) => setEventTypes(e.target.value)} multiple> @@ -61,7 +62,7 @@ const Filter = ({ setItems }) => { <MenuItem value="driverChanged">{t('eventDriverChanged')}</MenuItem> </Select> </FormControl> - </Grid> + </div> </ReportFilter> ); }; diff --git a/modern/src/reports/StatisticsPage.js b/modern/src/reports/StatisticsPage.js index f04f6e43..fcdf22d6 100644 --- a/modern/src/reports/StatisticsPage.js +++ b/modern/src/reports/StatisticsPage.js @@ -1,62 +1,20 @@ import React, { useState } from 'react'; import { - FormControl, InputLabel, Select, MenuItem, TextField, Button, TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, } from '@material-ui/core'; -import moment from 'moment'; import { formatDate } from '../common/util/formatter'; import { useTranslation } from '../common/components/LocalizationProvider'; import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; +import ReportFilter from './components/ReportFilter'; -const useStyles = makeStyles((theme) => ({ - filter: { - margin: theme.spacing(1), - }, -})); - -const Filter = ({ setItems }) => { - const classes = useStyles(); +const StatisticsPage = () => { 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 [items, setItems] = useState([]); - const query = new URLSearchParams({ from: selectedFrom.toISOString(), to: selectedTo.toISOString() }); + const handleSubmit = async (from, to) => { + const query = new URLSearchParams({ from, to }); const response = await fetch(`/api/statistics?${query.toString()}`, { Accept: 'application/json' }); if (response.ok) { setItems(await response.json()); @@ -64,54 +22,8 @@ const Filter = ({ setItems }) => { }; return ( - <div className={classes.filter}> - <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="outlined" color="secondary" onClick={handleClick} fullWidth>{t('reportShow')}</Button> - </div> - ); -}; - -const StatisticsPage = () => { - const t = useTranslation(); - - const [items, setItems] = useState([]); - - return ( <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'statisticsTitle']}> - <Filter setItems={setItems} /> + <ReportFilter handleSubmit={handleSubmit} showOnly ignoreDevice /> <TableContainer> <Table> <TableHead> diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js index b9f5e5dd..5a8bb34e 100644 --- a/modern/src/reports/SummaryReportPage.js +++ b/modern/src/reports/SummaryReportPage.js @@ -1,17 +1,20 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; -import { Grid, FormControlLabel, Checkbox } from '@material-ui/core'; +import { + FormControl, InputLabel, Select, MenuItem, +} from '@material-ui/core'; import { useTheme } from '@material-ui/core/styles'; import { formatDistance, formatHours, formatDate, formatSpeed, formatVolume, } from '../common/util/formatter'; -import ReportFilter from './components/ReportFilter'; +import ReportFilter, { useFilterStyles } from './components/ReportFilter'; import { useAttributePreference } from '../common/util/preferences'; import { useTranslation } from '../common/components/LocalizationProvider'; import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; const Filter = ({ setItems }) => { + const classes = useFilterStyles(); const t = useTranslation(); const [daily, setDaily] = useState(false); @@ -35,12 +38,15 @@ const Filter = ({ setItems }) => { return ( <ReportFilter handleSubmit={handleSubmit}> - <Grid item xs={12} sm={6}> - <FormControlLabel - control={<Checkbox checked={daily} onChange={(e) => setDaily(e.target.checked)} />} - label={t('reportDaily')} - /> - </Grid> + <div className={classes.item}> + <FormControl variant="filled" fullWidth> + <InputLabel>{t('sharedType')}</InputLabel> + <Select value={daily} onChange={(e) => setDaily(e.target.value)}> + <MenuItem value={false}>{t('reportSummary')}</MenuItem> + <MenuItem value>{t('reportDaily')}</MenuItem> + </Select> + </FormControl> + </div> </ReportFilter> ); }; diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js index 739d5e47..30a49c81 100644 --- a/modern/src/reports/components/ReportFilter.js +++ b/modern/src/reports/components/ReportFilter.js @@ -1,23 +1,42 @@ import React, { useState } from 'react'; import { - FormControl, InputLabel, Select, MenuItem, Button, TextField, Grid, Typography, makeStyles, + FormControl, InputLabel, Select, MenuItem, Button, TextField, Typography, makeStyles, } from '@material-ui/core'; import { useSelector } from 'react-redux'; import moment from 'moment'; import { useTranslation } from '../../common/components/LocalizationProvider'; +import dimensions from '../../common/theme/dimensions'; -const useStyles = makeStyles((theme) => ({ +export const useFilterStyles = makeStyles((theme) => ({ filter: { + display: 'flex', + flexWrap: 'wrap', + gap: theme.spacing(1), padding: theme.spacing(2), }, + item: { + flex: `1 1 ${dimensions.filterFormWidth}`, + }, + buttons: { + display: 'flex', + gap: theme.spacing(1), + flex: `1 1 ${dimensions.filterFormWidth}`, + }, + button: { + flexGrow: 1, + }, })); -const ReportFilter = ({ children, handleSubmit, showOnly }) => { - const classes = useStyles(); +const ReportFilter = ({ + children, handleSubmit, showOnly, ignoreDevice, +}) => { + const classes = useFilterStyles(); const t = useTranslation(); const devices = useSelector((state) => state.devices.items); - const [deviceId, setDeviceId] = useState(); + const selectedDeviceId = useSelector((state) => state.devices.selectedId); + + const [deviceId, setDeviceId] = useState(selectedDeviceId); const [period, setPeriod] = useState('today'); const [from, setFrom] = useState(moment().subtract(1, 'hour')); const [to, setTo] = useState(moment()); @@ -67,18 +86,20 @@ const ReportFilter = ({ children, handleSubmit, showOnly }) => { }; return ( - <Grid container className={classes.filter} spacing={2} justifyContent="flex-end"> - <Grid item xs={12} sm={period === 'custom' ? 3 : 6}> - <FormControl variant="filled" fullWidth> - <InputLabel>{t('reportDevice')}</InputLabel> - <Select value={deviceId} onChange={(e) => setDeviceId(e.target.value)}> - {Object.values(devices).map((device) => ( - <MenuItem key={device.id} value={device.id}>{device.name}</MenuItem> - ))} - </Select> - </FormControl> - </Grid> - <Grid item xs={12} sm={period === 'custom' ? 3 : 6}> + <div className={classes.filter}> + {!ignoreDevice && ( + <div className={classes.item}> + <FormControl variant="filled" fullWidth> + <InputLabel>{t('reportDevice')}</InputLabel> + <Select value={deviceId} onChange={(e) => setDeviceId(e.target.value)}> + {Object.values(devices).map((device) => ( + <MenuItem key={device.id} value={device.id}>{device.name}</MenuItem> + ))} + </Select> + </FormControl> + </div> + )} + <div className={classes.item}> <FormControl variant="filled" fullWidth> <InputLabel>{t('reportPeriod')}</InputLabel> <Select value={period} onChange={(e) => setPeriod(e.target.value)}> @@ -91,69 +112,66 @@ const ReportFilter = ({ children, handleSubmit, showOnly }) => { <MenuItem value="custom">{t('reportCustom')}</MenuItem> </Select> </FormControl> - </Grid> + </div> {period === 'custom' && ( - <Grid item xs={12} sm={3}> - <TextField - 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 - /> - </Grid> + <div className={classes.item}> + <TextField + 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 + /> + </div> )} {period === 'custom' && ( - <Grid item xs={12} sm={3}> - <TextField - 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 - /> - </Grid> + <div className={classes.item}> + <TextField + 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 + /> + </div> )} {children} - <Grid item xs={!showOnly ? 4 : 12} sm={!showOnly ? 2 : 6}> + <div className={classes.buttons}> <Button onClick={() => handleClick(false, true)} variant="outlined" color="secondary" - fullWidth + className={classes.button} + disabled={!ignoreDevice && !deviceId} > {t('reportShow')} </Button> - </Grid> - {!showOnly - && ( - <Grid item xs={4} sm={2}> + {!showOnly && ( <Button onClick={() => handleClick(false, false)} variant="outlined" color="secondary" - fullWidth + className={classes.button} + disabled={!ignoreDevice && !deviceId} > {t('reportExport')} </Button> - </Grid> )} - {!showOnly - && ( - <Grid item xs={4} sm={2}> + {!showOnly && ( <Button onClick={() => handleClick(true, false)} variant="outlined" color="secondary" - fullWidth + className={classes.button} + disabled={!ignoreDevice && !deviceId} > <Typography variant="button" noWrap>{t('reportEmail')}</Typography> </Button> - </Grid> )} - </Grid> + </div> + </div> ); }; |