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/components | |
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/components')
-rw-r--r-- | modern/src/reports/components/Graph.js | 33 | ||||
-rw-r--r-- | modern/src/reports/components/ReportFilter.js | 153 | ||||
-rw-r--r-- | modern/src/reports/components/ReportLayout.js | 124 |
3 files changed, 310 insertions, 0 deletions
diff --git a/modern/src/reports/components/Graph.js b/modern/src/reports/components/Graph.js new file mode 100644 index 00000000..63d24eee --- /dev/null +++ b/modern/src/reports/components/Graph.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { withWidth } from '@material-ui/core'; +import { + LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, +} from 'recharts'; + +const CustomizedAxisTick = ({ x, y, payload }) => { + if (!payload.value) { + return payload.value; + } + const parts = payload.value.split(' '); + return ( + <g transform={`translate(${x},${y})`}> + <text x={0} y={0} dy={16} textAnchor="end" fill="#666" transform="rotate(-35)">{parts[0]}</text> + <text x={0} y={16} dy={16} textAnchor="end" fill="#666" transform="rotate(-35)">{parts[1]}</text> + </g> + ); +}; + +const Graph = ({ type, items }) => ( + <ResponsiveContainer height={400} width="100%" debounce={1}> + <LineChart data={items}> + <XAxis dataKey="fixTime" tick={<CustomizedAxisTick />} height={60} /> + <YAxis /> + <CartesianGrid strokeDasharray="3 3" /> + <Tooltip /> + <Legend /> + <Line type="natural" dataKey={type} /> + </LineChart> + </ResponsiveContainer> +); + +export default withWidth()(Graph); diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js new file mode 100644 index 00000000..bc9c5af6 --- /dev/null +++ b/modern/src/reports/components/ReportFilter.js @@ -0,0 +1,153 @@ +import React, { useState } from 'react'; +import { + FormControl, InputLabel, Select, MenuItem, Button, TextField, Grid, Typography, +} from '@material-ui/core'; +import { useSelector } from 'react-redux'; +import moment from 'moment'; +import { useTranslation } from '../../common/components/LocalizationProvider'; + +const ReportFilter = ({ children, handleSubmit, showOnly }) => { + const t = useTranslation(); + + const devices = useSelector((state) => state.devices.items); + const [deviceId, setDeviceId] = useState(); + const [period, setPeriod] = useState('today'); + const [from, setFrom] = useState(moment().subtract(1, 'hour')); + const [to, setTo] = useState(moment()); + + const handleClick = (mail, json) => { + 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 accept = json ? 'application/json' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; + handleSubmit( + deviceId, + selectedFrom.toISOString(), + selectedTo.toISOString(), + mail, + { Accept: accept }, + ); + }; + + return ( + <Grid container 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}> + <FormControl variant="filled" 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> + </Grid> + {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> + )} + {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> + )} + {children} + <Grid item xs={!showOnly ? 4 : 12} sm={!showOnly ? 2 : 6}> + <Button + onClick={() => handleClick(false, true)} + variant="outlined" + color="secondary" + fullWidth + > + {t('reportShow')} + </Button> + </Grid> + {!showOnly + && ( + <Grid item xs={4} sm={2}> + <Button + onClick={() => handleClick(false, false)} + variant="outlined" + color="secondary" + fullWidth + > + {t('reportExport')} + </Button> + </Grid> + )} + {!showOnly + && ( + <Grid item xs={4} sm={2}> + <Button + onClick={() => handleClick(true, false)} + variant="outlined" + color="secondary" + fullWidth + > + <Typography variant="button" noWrap>{t('reportEmail')}</Typography> + </Button> + </Grid> + )} + </Grid> + ); +}; + +export default ReportFilter; diff --git a/modern/src/reports/components/ReportLayout.js b/modern/src/reports/components/ReportLayout.js new file mode 100644 index 00000000..c028530b --- /dev/null +++ b/modern/src/reports/components/ReportLayout.js @@ -0,0 +1,124 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; +import { + Grid, Typography, Divider, Drawer, makeStyles, IconButton, Hidden, +} from '@material-ui/core'; +import TimelineIcon from '@material-ui/icons/Timeline'; +import PauseCircleFilledIcon from '@material-ui/icons/PauseCircleFilled'; +import PlayCircleFilledIcon from '@material-ui/icons/PlayCircleFilled'; +import NotificationsActiveIcon from '@material-ui/icons/NotificationsActive'; +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 '../../common/components/LocalizationProvider'; + +const useStyles = makeStyles((theme) => ({ + root: { + display: 'flex', + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + height: '100%', + }, + drawerContainer: { + width: theme.dimensions.drawerWidthDesktop, + }, + drawer: { + width: theme.dimensions.drawerWidthDesktop, + [theme.breakpoints.down('sm')]: { + width: theme.dimensions.drawerWidthTablet, + }, + }, + content: { + flex: 1, + padding: theme.spacing(3, 3, 3, 3), + }, + drawerHeader: { + ...theme.mixins.toolbar, + display: 'flex', + alignItems: 'center', + padding: theme.spacing(0, 1), + }, + backArrowIconContainer: { + '&:hover': { + backgroundColor: 'transparent', + }, + }, +})); + +const ReportLayout = ({ children, filter }) => { + const classes = useStyles(); + const history = useHistory(); + const location = useLocation(); + const t = useTranslation(); + + const [openDrawer, setOpenDrawer] = useState(false); + const [reportTitle, setReportTitle] = useState(); + + const routes = useMemo(() => [ + { name: t('reportRoute'), href: '/reports/route', icon: <TimelineIcon /> }, + { name: t('reportEvents'), href: '/reports/event', icon: <NotificationsActiveIcon /> }, + { name: t('reportTrips'), href: '/reports/trip', icon: <PlayCircleFilledIcon /> }, + { name: t('reportStops'), href: '/reports/stop', icon: <PauseCircleFilledIcon /> }, + { name: t('reportSummary'), href: '/reports/summary', icon: <FormatListBulletedIcon /> }, + { name: t('reportChart'), href: '/reports/chart', icon: <TrendingUpIcon /> }, + ], [t]); + + useEffect(() => { + routes.forEach((route) => { + switch (location.pathname) { + case `${route.href}`: + setReportTitle(route.name); + break; + default: + break; + } + }); + }, [routes, location]); + + const pageTitle = `${t('reportTitle')} / ${reportTitle}`; + + return ( + <div className={classes.root}> + <Hidden mdUp> + <NavBar setOpenDrawer={setOpenDrawer} title={pageTitle} /> + <Drawer + variant="temporary" + open={openDrawer} + onClose={() => setOpenDrawer(!openDrawer)} + classes={{ paper: classes.drawer }} + > + <SideNav routes={routes} /> + </Drawer> + </Hidden> + <Hidden smDown> + <Drawer + variant="permanent" + classes={{ root: classes.drawerContainer, paper: classes.drawer }} + > + <div className={classes.drawerHeader}> + <IconButton onClick={() => history.push('/')}> + <ArrowBackIcon /> + </IconButton> + <Typography variant="h6" color="inherit" noWrap> + {t('reportTitle')} + </Typography> + </div> + <Divider /> + <SideNav routes={routes} /> + </Drawer> + </Hidden> + <div className={classes.content}> + <Grid container direction="column" spacing={2}> + <Grid item>{filter}</Grid> + <Grid item>{children}</Grid> + </Grid> + </div> + </div> + ); +}; + +export default ReportLayout; |