aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modern/.eslintrc.js35
-rw-r--r--modern/package.json2
-rw-r--r--modern/src/EditItemView.js7
-rw-r--r--modern/src/GeofencesPage.js24
-rw-r--r--modern/src/MainToolbar.js97
-rw-r--r--modern/src/StartPage.js2
-rw-r--r--modern/src/admin/ServerPage.js12
-rw-r--r--modern/src/admin/StatisticsPage.js7
-rw-r--r--modern/src/admin/UsersPage.js7
-rw-r--r--modern/src/common/selectors.js3
-rw-r--r--modern/src/components/NavBar.js (renamed from modern/src/components/reports/ReportNavbar.js)9
-rw-r--r--modern/src/components/SideNav.js34
-rw-r--r--modern/src/components/reports/ReportSidebar.js31
-rw-r--r--modern/src/reports/ChartReportPage.js6
-rw-r--r--modern/src/reports/EventReportPage.js6
-rw-r--r--modern/src/reports/ReportFilter.js2
-rw-r--r--modern/src/reports/ReportLayout.js (renamed from modern/src/reports/ReportLayoutPage.js)52
-rw-r--r--modern/src/reports/RouteReportPage.js6
-rw-r--r--modern/src/reports/StopReportPage.js6
-rw-r--r--modern/src/reports/SummaryReportPage.js6
-rw-r--r--modern/src/reports/TripReportPage.js6
-rw-r--r--modern/src/settings/ComputedAttributesPage.js8
-rw-r--r--modern/src/settings/DriversPage.js7
-rw-r--r--modern/src/settings/GroupsPage.js7
-rw-r--r--modern/src/settings/MaintenancesPage.js7
-rw-r--r--modern/src/settings/NotificationsPage.js7
-rw-r--r--modern/src/settings/OptionsLayout/index.js106
-rw-r--r--modern/src/settings/OptionsLayout/useRoutes.js86
28 files changed, 357 insertions, 231 deletions
diff --git a/modern/.eslintrc.js b/modern/.eslintrc.js
index a1089804..a4cf3053 100644
--- a/modern/.eslintrc.js
+++ b/modern/.eslintrc.js
@@ -1,18 +1,21 @@
module.exports = {
- "extends": "airbnb",
- "plugins": [
- "react"
- ],
- "ignorePatterns": ["serviceWorker.js", "localization.js", "switcher.js"],
- "rules": {
- "max-len": [0],
- "no-shadow": [0],
- "no-return-assign": [0],
- "no-param-reassign": [0],
- "no-prototype-builtins": [0],
- "react/jsx-filename-extension": [1, { "extensions": [".js"] }],
- "react/prop-types": [0],
- "react/jsx-props-no-spreading": [0],
- "jsx-a11y/anchor-is-valid": [0]
- }
+ extends: 'airbnb',
+ parserOptions: {
+ ecmaVersion: 2020,
+ },
+ plugins: [
+ 'react',
+ ],
+ ignorePatterns: ['serviceWorker.js', 'localization.js', 'switcher.js'],
+ rules: {
+ 'max-len': [0],
+ 'no-shadow': [0],
+ 'no-return-assign': [0],
+ 'no-param-reassign': [0],
+ 'no-prototype-builtins': [0],
+ 'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
+ 'react/prop-types': [0],
+ 'react/jsx-props-no-spreading': [0],
+ 'jsx-a11y/anchor-is-valid': [0],
+ },
};
diff --git a/modern/package.json b/modern/package.json
index 365d9865..2ae229c6 100644
--- a/modern/package.json
+++ b/modern/package.json
@@ -49,7 +49,7 @@
]
},
"devDependencies": {
- "eslint": "^6.8.0",
+ "eslint": "^7.30.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-react": "^7.24.0",
diff --git a/modern/src/EditItemView.js b/modern/src/EditItemView.js
index 9bf91a6a..e067a39f 100644
--- a/modern/src/EditItemView.js
+++ b/modern/src/EditItemView.js
@@ -4,10 +4,10 @@ import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
-import MainToolbar from './MainToolbar';
import t from './common/localization';
import { useEffectAsync } from './reactHelper';
+import OptionsLayout from './settings/OptionsLayout';
const useStyles = makeStyles((theme) => ({
container: {
@@ -58,8 +58,7 @@ const EditItemView = ({
};
return (
- <>
- <MainToolbar />
+ <OptionsLayout>
<Container maxWidth="xs" className={classes.container}>
{children}
<FormControl fullWidth margin="normal">
@@ -73,7 +72,7 @@ const EditItemView = ({
</div>
</FormControl>
</Container>
- </>
+ </OptionsLayout>
);
};
diff --git a/modern/src/GeofencesPage.js b/modern/src/GeofencesPage.js
index 95c7151e..71219c16 100644
--- a/modern/src/GeofencesPage.js
+++ b/modern/src/GeofencesPage.js
@@ -1,13 +1,17 @@
import React from 'react';
-import { isWidthUp, makeStyles, withWidth } from '@material-ui/core';
+import {
+ Divider, isWidthUp, makeStyles, withWidth, Typography, IconButton,
+} from '@material-ui/core';
import Drawer from '@material-ui/core/Drawer';
import ContainerDimensions from 'react-container-dimensions';
-import MainToolbar from './MainToolbar';
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import Map from './map/Map';
import CurrentLocationMap from './map/CurrentLocationMap';
import GeofenceEditMap from './map/GeofenceEditMap';
import GeofencesList from './GeofencesList';
+import t from './common/localization';
+
const useStyles = makeStyles((theme) => ({
root: {
height: '100%',
@@ -32,6 +36,12 @@ const useStyles = makeStyles((theme) => ({
height: 250,
},
},
+ drawerHeader: {
+ ...theme.mixins.toolbar,
+ display: 'flex',
+ alignItems: 'center',
+ padding: theme.spacing(0, 1),
+ },
mapContainer: {
flexGrow: 1,
},
@@ -42,13 +52,21 @@ const GeofencesPage = ({ width }) => {
return (
<div className={classes.root}>
- <MainToolbar />
<div className={classes.content}>
<Drawer
anchor={isWidthUp('sm', width) ? 'left' : 'bottom'}
variant="permanent"
classes={{ paper: classes.drawerPaper }}
>
+ <div className={classes.drawerHeader}>
+ <IconButton component="a" href="/">
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6" color="inherit" noWrap>
+ {t('sharedGeofences')}
+ </Typography>
+ </div>
+ <Divider />
<GeofencesList />
</Drawer>
<div className={classes.mapContainer}>
diff --git a/modern/src/MainToolbar.js b/modern/src/MainToolbar.js
index da7c42c5..59c2eca8 100644
--- a/modern/src/MainToolbar.js
+++ b/modern/src/MainToolbar.js
@@ -10,24 +10,16 @@ import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Drawer from '@material-ui/core/Drawer';
import List from '@material-ui/core/List';
-import ListSubheader from '@material-ui/core/ListSubheader';
-import Divider from '@material-ui/core/Divider';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import MapIcon from '@material-ui/icons/Map';
-import BarChartIcon from '@material-ui/icons/BarChart';
-import PeopleIcon from '@material-ui/icons/People';
-import StorageIcon from '@material-ui/icons/Storage';
import PersonIcon from '@material-ui/icons/Person';
-import NotificationsIcon from '@material-ui/icons/Notifications';
import DescriptionIcon from '@material-ui/icons/Description';
-import FolderIcon from '@material-ui/icons/Folder';
-import CreateIcon from '@material-ui/icons/Create';
import ReplayIcon from '@material-ui/icons/Replay';
-import BuildIcon from '@material-ui/icons/Build';
import { sessionActions } from './store';
import t from './common/localization';
+import * as selectors from './common/selectors';
const useStyles = makeStyles((theme) => ({
flex: {
@@ -50,8 +42,7 @@ const MainToolbar = () => {
const [drawer, setDrawer] = useState(false);
const classes = useStyles();
const history = useHistory();
- const adminEnabled = useSelector((state) => state.session.user && state.session.user.administrator);
- const userId = useSelector((state) => state.session.user && state.session.user.id);
+ const userId = useSelector(selectors.getUserId);
const openDrawer = () => { setDrawer(true); };
const closeDrawer = () => { setDrawer(false); };
@@ -108,89 +99,17 @@ const MainToolbar = () => {
</ListItemIcon>
<ListItemText primary={t('reportTitle')} />
</ListItem>
- </List>
- <Divider />
- <List
- subheader={(
- <ListSubheader>
- {t('settingsTitle')}
- </ListSubheader>
- )}
- >
- <ListItem button disabled={!userId} onClick={() => history.push(`/user/${userId}`)}>
+ <ListItem
+ button
+ disabled={!userId}
+ onClick={() => history.push('/settings/notifications')}
+ >
<ListItemIcon>
<PersonIcon />
</ListItemIcon>
- <ListItemText primary={t('settingsUser')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/geofences')}>
- <ListItemIcon>
- <CreateIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedGeofences')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/settings/notifications')}>
- <ListItemIcon>
- <NotificationsIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedNotifications')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/settings/groups')}>
- <ListItemIcon>
- <FolderIcon />
- </ListItemIcon>
- <ListItemText primary={t('settingsGroups')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/settings/drivers')}>
- <ListItemIcon>
- <PersonIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedDrivers')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/settings/attributes')}>
- <ListItemIcon>
- <StorageIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedComputedAttributes')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/settings/maintenances')}>
- <ListItemIcon>
- <BuildIcon />
- </ListItemIcon>
- <ListItemText primary={t('sharedMaintenance')} />
+ <ListItemText primary={t('settingsTitle')} />
</ListItem>
</List>
- {adminEnabled && (
- <>
- <Divider />
- <List
- subheader={(
- <ListSubheader>
- {t('userAdmin')}
- </ListSubheader>
- )}
- >
- <ListItem button onClick={() => history.push('/admin/server')}>
- <ListItemIcon>
- <StorageIcon />
- </ListItemIcon>
- <ListItemText primary={t('settingsServer')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/admin/users')}>
- <ListItemIcon>
- <PeopleIcon />
- </ListItemIcon>
- <ListItemText primary={t('settingsUsers')} />
- </ListItem>
- <ListItem button onClick={() => history.push('/admin/statistics')}>
- <ListItemIcon>
- <BarChartIcon />
- </ListItemIcon>
- <ListItemText primary={t('statisticsTitle')} />
- </ListItem>
- </List>
- </>
- )}
</div>
</Drawer>
</>
diff --git a/modern/src/StartPage.js b/modern/src/StartPage.js
index 3edc5b31..1472f501 100644
--- a/modern/src/StartPage.js
+++ b/modern/src/StartPage.js
@@ -16,7 +16,7 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(5),
width: theme.dimensions.sidebarWidth,
[theme.breakpoints.down('md')]: {
- width: theme.dimensions.tabletSidebarWidth,
+ width: theme.dimensions.sidebarWidthTablet,
},
[theme.breakpoints.down('xs')]: {
width: '0px',
diff --git a/modern/src/admin/ServerPage.js b/modern/src/admin/ServerPage.js
index 7decac1a..ec0034f5 100644
--- a/modern/src/admin/ServerPage.js
+++ b/modern/src/admin/ServerPage.js
@@ -8,11 +8,11 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import t from '../common/localization';
-import MainToolbar from '../MainToolbar';
import { sessionActions } from '../store';
import EditAttributesView from '../attributes/EditAttributesView';
import deviceAttributes from '../attributes/deviceAttributes';
import userAttributes from '../attributes/userAttributes';
+import OptionsLayout from '../settings/OptionsLayout';
const useStyles = makeStyles((theme) => ({
container: {
@@ -51,11 +51,9 @@ const ServerPage = () => {
};
return (
- <>
- <MainToolbar />
+ <OptionsLayout>
<Container maxWidth="xs" className={classes.container}>
- {item
- && (
+ {item && (
<>
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
@@ -113,7 +111,7 @@ const ServerPage = () => {
</AccordionDetails>
</Accordion>
</>
- )}
+ )}
<FormControl fullWidth margin="normal">
<div className={classes.buttons}>
<Button type="button" color="primary" variant="outlined" onClick={() => history.goBack()}>
@@ -125,7 +123,7 @@ const ServerPage = () => {
</div>
</FormControl>
</Container>
- </>
+ </OptionsLayout>
);
};
diff --git a/modern/src/admin/StatisticsPage.js b/modern/src/admin/StatisticsPage.js
index 131ab7e4..1f32c3d9 100644
--- a/modern/src/admin/StatisticsPage.js
+++ b/modern/src/admin/StatisticsPage.js
@@ -5,7 +5,7 @@ import {
import moment from 'moment';
import t from '../common/localization';
import { formatDate } from '../common/formatter';
-import ReportLayoutPage from '../reports/ReportLayoutPage';
+import OptionsLayout from '../settings/OptionsLayout';
const Filter = ({ setItems }) => {
const [period, setPeriod] = useState('today');
@@ -98,7 +98,8 @@ const StatisticsPage = () => {
const [items, setItems] = useState([]);
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <OptionsLayout>
+ <Filter setItems={setItems} />
<TableContainer component={Paper}>
<Table>
<TableHead>
@@ -133,7 +134,7 @@ const StatisticsPage = () => {
</TableBody>
</Table>
</TableContainer>
- </ReportLayoutPage>
+ </OptionsLayout>
);
};
diff --git a/modern/src/admin/UsersPage.js b/modern/src/admin/UsersPage.js
index fc6e6736..a8b3c849 100644
--- a/modern/src/admin/UsersPage.js
+++ b/modern/src/admin/UsersPage.js
@@ -3,11 +3,11 @@ import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
import { formatBoolean } from '../common/formatter';
+import OptionsLayout from '../settings/OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -61,10 +61,9 @@ const UsersView = ({ updateTimestamp, onMenuClick }) => {
};
const UsersPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
<EditCollectionView content={UsersView} editPath="/user" endpoint="users" />
- </>
+ </OptionsLayout>
);
export default UsersPage;
diff --git a/modern/src/common/selectors.js b/modern/src/common/selectors.js
new file mode 100644
index 00000000..44a0c547
--- /dev/null
+++ b/modern/src/common/selectors.js
@@ -0,0 +1,3 @@
+export const getIsAdmin = (state) => state.session.user?.administrator;
+
+export const getUserId = (state) => state.session.user?.id;
diff --git a/modern/src/components/reports/ReportNavbar.js b/modern/src/components/NavBar.js
index 16807e89..93e79bbb 100644
--- a/modern/src/components/reports/ReportNavbar.js
+++ b/modern/src/components/NavBar.js
@@ -3,9 +3,8 @@ import {
AppBar, Toolbar, Typography, IconButton,
} from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';
-import t from '../../common/localization';
-const ReportNavbar = ({ setOpenDrawer, reportTitle }) => (
+const Navbar = ({ setOpenDrawer, title }) => (
<AppBar position="fixed" color="inherit">
<Toolbar>
<IconButton
@@ -17,12 +16,10 @@ const ReportNavbar = ({ setOpenDrawer, reportTitle }) => (
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
- {t('reportTitle')}
- {' '}
- {` / ${reportTitle}`}
+ {title}
</Typography>
</Toolbar>
</AppBar>
);
-export default ReportNavbar;
+export default Navbar;
diff --git a/modern/src/components/SideNav.js b/modern/src/components/SideNav.js
new file mode 100644
index 00000000..bcf8ecd5
--- /dev/null
+++ b/modern/src/components/SideNav.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import {
+ List, ListItem, ListItemText, ListItemIcon, Divider, ListSubheader,
+} from '@material-ui/core';
+import { Link, useLocation } from 'react-router-dom';
+
+const SideNav = ({ routes }) => {
+ const location = useLocation();
+
+ return (
+ <List disablePadding style={{ paddingTop: '16px' }}>
+ {routes.map((route) => (route.subheader ? (
+ <>
+ <Divider />
+ <ListSubheader>{route.subheader}</ListSubheader>
+ </>
+ ) : (
+ <ListItem
+ disableRipple
+ component={Link}
+ key={route.href || route.subheader}
+ button
+ to={route.href}
+ selected={location.pathname.match(route.match || route.href)}
+ >
+ <ListItemIcon>{route.icon}</ListItemIcon>
+ <ListItemText primary={route.name} />
+ </ListItem>
+ )))}
+ </List>
+ );
+};
+
+export default SideNav;
diff --git a/modern/src/components/reports/ReportSidebar.js b/modern/src/components/reports/ReportSidebar.js
deleted file mode 100644
index 686fc040..00000000
--- a/modern/src/components/reports/ReportSidebar.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import {
- List, ListItem, ListItemText, ListItemIcon,
-} from '@material-ui/core';
-import { Link, useLocation } from 'react-router-dom';
-
-const ReportSidebar = ({ routes }) => {
- const location = useLocation();
-
- return (
- <List disablePadding style={{ paddingTop: '16px' }}>
- {routes.map((route) => (
- <ListItem
- disableRipple
- component={Link}
- key={route}
- button
- to={route.href}
- selected={route.href === location.pathname}
- >
- <ListItemIcon>
- {route.icon}
- </ListItemIcon>
- <ListItemText primary={route.name} />
- </ListItem>
- ))}
- </List>
- );
-};
-
-export default ReportSidebar;
diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js
index 70ce780f..c55ed6d5 100644
--- a/modern/src/reports/ChartReportPage.js
+++ b/modern/src/reports/ChartReportPage.js
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import {
Grid, FormControl, InputLabel, Select, MenuItem,
} from '@material-ui/core';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import ReportFilter from './ReportFilter';
import Graph from './Graph';
import { useAttributePreference } from '../common/preferences';
@@ -57,14 +57,14 @@ const ChartReportPage = () => {
const [type, setType] = useState('speed');
return (
- <ReportLayoutPage filter={(
+ <ReportLayout filter={(
<Filter setItems={setItems}>
<ChartType type={type} setType={setType} />
</Filter>
)}
>
<Graph items={items} type={type} />
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js
index 89efcf76..b5c3daeb 100644
--- a/modern/src/reports/EventReportPage.js
+++ b/modern/src/reports/EventReportPage.js
@@ -7,7 +7,7 @@ import { useTheme } from '@material-ui/core/styles';
import { useSelector } from 'react-redux';
import { formatDate } from '../common/formatter';
import ReportFilter from './ReportFilter';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import { prefixString } from '../common/stringUtils';
import t from '../common/localization';
@@ -101,14 +101,14 @@ const EventReportPage = () => {
}];
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <ReportLayout filter={<Filter setItems={setItems} />}>
<DataGrid
rows={items}
columns={columns}
hideFooter
autoHeight
/>
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/reports/ReportFilter.js b/modern/src/reports/ReportFilter.js
index 1e2eb887..dec8d018 100644
--- a/modern/src/reports/ReportFilter.js
+++ b/modern/src/reports/ReportFilter.js
@@ -58,7 +58,7 @@ const ReportFilter = ({ children, handleSubmit, showOnly }) => {
};
return (
- <Grid container spacing={2}>
+ <Grid container spacing={2} justify="flex-end">
<Grid item xs={12} sm={period === 'custom' ? 3 : 6}>
<FormControl variant="filled" fullWidth>
<InputLabel>{t('reportDevice')}</InputLabel>
diff --git a/modern/src/reports/ReportLayoutPage.js b/modern/src/reports/ReportLayout.js
index d25a8876..c231cd80 100644
--- a/modern/src/reports/ReportLayoutPage.js
+++ b/modern/src/reports/ReportLayout.js
@@ -11,8 +11,8 @@ import FormatListBulletedIcon from '@material-ui/icons/FormatListBulleted';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import ReportSidebar from '../components/reports/ReportSidebar';
-import ReportNavbar from '../components/reports/ReportNavbar';
+import SideNav from '../components/SideNav';
+import NavBar from '../components/NavBar';
import t from '../common/localization';
const useStyles = makeStyles((theme) => ({
@@ -60,7 +60,7 @@ const routes = [
{ name: t('reportChart'), href: '/reports/chart', icon: <TrendingUpIcon /> },
];
-const ReportLayoutPage = ({ children, filter }) => {
+const ReportLayout = ({ children, filter }) => {
const classes = useStyles();
const history = useHistory();
const location = useLocation();
@@ -79,41 +79,39 @@ const ReportLayoutPage = ({ children, filter }) => {
});
}, [location]);
+ const pageTitle = `${t('reportTitle')} / ${reportTitle}`;
+
return (
<div className={classes.root}>
<Hidden only={['lg', 'xl']}>
- <ReportNavbar setOpenDrawer={setOpenDrawer} reportTitle={reportTitle} />
+ <NavBar setOpenDrawer={setOpenDrawer} title={pageTitle} />
<Drawer
variant="temporary"
open={openDrawer}
onClose={() => setOpenDrawer(!openDrawer)}
classes={{ paper: classes.drawer }}
>
- <ReportSidebar routes={routes} />
+ <SideNav routes={routes} />
</Drawer>
</Hidden>
<Hidden only={['xs', 'sm', 'md']}>
- <div className={classes.drawerContainer}>
- <Drawer
- variant="permanent"
- classes={{ paper: classes.drawer }}
- >
- <div className={classes.drawerHeader}>
- <IconButton
- onClick={() => history.push('/')}
- className={classes.backArrowIconContainer}
- disableRipple
- >
- <ArrowBackIcon />
- </IconButton>
- <Typography variant="h6" color="inherit" noWrap>
- {t('reportTitle')}
- </Typography>
- </div>
- <Divider />
- <ReportSidebar routes={routes} />
- </Drawer>
- </div>
+ <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}>
<div className={classes.toolbar} />
@@ -126,4 +124,4 @@ const ReportLayoutPage = ({ children, filter }) => {
);
};
-export default ReportLayoutPage;
+export default ReportLayout;
diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js
index dce08cb1..fffcdcb5 100644
--- a/modern/src/reports/RouteReportPage.js
+++ b/modern/src/reports/RouteReportPage.js
@@ -6,7 +6,7 @@ import {
formatDistance, formatSpeed, formatBoolean, formatDate, formatCoordinate,
} from '../common/formatter';
import ReportFilter from './ReportFilter';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import { useAttributePreference, usePreference } from '../common/preferences';
import t from '../common/localization';
@@ -86,7 +86,7 @@ const RouteReportPage = () => {
const [items, setItems] = useState([]);
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <ReportLayout filter={<Filter setItems={setItems} />}>
<Paper>
<DataGrid
rows={items}
@@ -95,7 +95,7 @@ const RouteReportPage = () => {
autoHeight
/>
</Paper>
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/reports/StopReportPage.js b/modern/src/reports/StopReportPage.js
index 078a5e5e..d2e7e7ed 100644
--- a/modern/src/reports/StopReportPage.js
+++ b/modern/src/reports/StopReportPage.js
@@ -5,7 +5,7 @@ import {
formatDistance, formatHours, formatDate, formatVolume,
} from '../common/formatter';
import ReportFilter from './ReportFilter';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import { useAttributePreference } from '../common/preferences';
import t from '../common/localization';
@@ -84,7 +84,7 @@ const StopReportPage = () => {
}];
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <ReportLayout filter={<Filter setItems={setItems} />}>
<DataGrid
rows={items}
columns={columns}
@@ -92,7 +92,7 @@ const StopReportPage = () => {
autoHeight
getRowId={() => Math.random()}
/>
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js
index 0e88705f..4523e652 100644
--- a/modern/src/reports/SummaryReportPage.js
+++ b/modern/src/reports/SummaryReportPage.js
@@ -6,7 +6,7 @@ import {
formatDistance, formatHours, formatDate, formatSpeed, formatVolume,
} from '../common/formatter';
import ReportFilter from './ReportFilter';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import { useAttributePreference } from '../common/preferences';
import t from '../common/localization';
@@ -103,7 +103,7 @@ const SummaryReportPage = () => {
}];
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <ReportLayout filter={<Filter setItems={setItems} />}>
<DataGrid
rows={items}
columns={columns}
@@ -111,7 +111,7 @@ const SummaryReportPage = () => {
autoHeight
getRowId={() => Math.random()}
/>
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js
index c10e6b1a..f611dde1 100644
--- a/modern/src/reports/TripReportPage.js
+++ b/modern/src/reports/TripReportPage.js
@@ -5,7 +5,7 @@ import {
formatDistance, formatSpeed, formatHours, formatDate, formatVolume,
} from '../common/formatter';
import ReportFilter from './ReportFilter';
-import ReportLayoutPage from './ReportLayoutPage';
+import ReportLayout from './ReportLayout';
import { useAttributePreference } from '../common/preferences';
import t from '../common/localization';
@@ -115,7 +115,7 @@ const TripReportPage = () => {
}];
return (
- <ReportLayoutPage filter={<Filter setItems={setItems} />}>
+ <ReportLayout filter={<Filter setItems={setItems} />}>
<DataGrid
rows={items}
columns={columns}
@@ -123,7 +123,7 @@ const TripReportPage = () => {
autoHeight
getRowId={() => Math.random()}
/>
- </ReportLayoutPage>
+ </ReportLayout>
);
};
diff --git a/modern/src/settings/ComputedAttributesPage.js b/modern/src/settings/ComputedAttributesPage.js
index f555259b..9f131d33 100644
--- a/modern/src/settings/ComputedAttributesPage.js
+++ b/modern/src/settings/ComputedAttributesPage.js
@@ -4,10 +4,10 @@ import {
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useSelector } from 'react-redux';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
+import OptionsLayout from './OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -65,10 +65,10 @@ const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => {
};
const ComputedAttributesPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
+
<EditCollectionView content={ComputedAttributeView} editPath="/settings/attribute" endpoint="attributes/computed" />
- </>
+ </OptionsLayout>
);
export default ComputedAttributesPage;
diff --git a/modern/src/settings/DriversPage.js b/modern/src/settings/DriversPage.js
index 36fc12d6..03bf49c5 100644
--- a/modern/src/settings/DriversPage.js
+++ b/modern/src/settings/DriversPage.js
@@ -3,10 +3,10 @@ import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
+import OptionsLayout from './OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -56,10 +56,9 @@ const DriversView = ({ updateTimestamp, onMenuClick }) => {
};
const DriversPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
<EditCollectionView content={DriversView} editPath="/settings/driver" endpoint="drivers" />
- </>
+ </OptionsLayout>
);
export default DriversPage;
diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js
index d5921844..ebebd40a 100644
--- a/modern/src/settings/GroupsPage.js
+++ b/modern/src/settings/GroupsPage.js
@@ -3,10 +3,10 @@ import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
+import OptionsLayout from './OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -54,10 +54,9 @@ const GroupsView = ({ updateTimestamp, onMenuClick }) => {
};
const GroupsPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
<EditCollectionView content={GroupsView} editPath="/settings/group" endpoint="groups" />
- </>
+ </OptionsLayout>
);
export default GroupsPage;
diff --git a/modern/src/settings/MaintenancesPage.js b/modern/src/settings/MaintenancesPage.js
index e598bc90..357d0791 100644
--- a/modern/src/settings/MaintenancesPage.js
+++ b/modern/src/settings/MaintenancesPage.js
@@ -3,7 +3,6 @@ import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
@@ -11,6 +10,7 @@ import EditCollectionView from '../EditCollectionView';
import positionAttributes from '../attributes/positionAttributes';
import { formatDistance, formatSpeed } from '../common/formatter';
import { useAttributePreference } from '../common/preferences';
+import OptionsLayout from './OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -82,10 +82,9 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => {
};
const MaintenacesPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
<EditCollectionView content={MaintenancesView} editPath="/settings/maintenance" endpoint="maintenance" />
- </>
+ </OptionsLayout>
);
export default MaintenacesPage;
diff --git a/modern/src/settings/NotificationsPage.js b/modern/src/settings/NotificationsPage.js
index 4e052927..2f1ee8b7 100644
--- a/modern/src/settings/NotificationsPage.js
+++ b/modern/src/settings/NotificationsPage.js
@@ -3,12 +3,12 @@ import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
-import MainToolbar from '../MainToolbar';
import t from '../common/localization';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from '../EditCollectionView';
import { prefixString } from '../common/stringUtils';
import { formatBoolean } from '../common/formatter';
+import OptionsLayout from './OptionsLayout';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -73,10 +73,9 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => {
};
const NotificationsPage = () => (
- <>
- <MainToolbar />
+ <OptionsLayout>
<EditCollectionView content={NotificationsView} editPath="/settings/notification" endpoint="notifications" />
- </>
+ </OptionsLayout>
);
export default NotificationsPage;
diff --git a/modern/src/settings/OptionsLayout/index.js b/modern/src/settings/OptionsLayout/index.js
new file mode 100644
index 00000000..4a42e583
--- /dev/null
+++ b/modern/src/settings/OptionsLayout/index.js
@@ -0,0 +1,106 @@
+import React, { useState, useEffect } from 'react';
+import { useLocation } from 'react-router-dom';
+import {
+ Typography,
+ Divider,
+ Drawer,
+ makeStyles,
+ IconButton,
+ Hidden,
+} from '@material-ui/core';
+
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+
+import SideNav from '../../components/SideNav';
+import NavBar from '../../components/NavBar';
+import t from '../../common/localization';
+import useRoutes from './useRoutes';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ display: 'flex',
+ height: '100%',
+ },
+ drawerContainer: {
+ width: theme.dimensions.drawerWidthDesktop,
+ },
+ drawer: {
+ width: theme.dimensions.drawerWidthDesktop,
+ [theme.breakpoints.down('md')]: {
+ width: theme.dimensions.drawerWidthTablet,
+ },
+ },
+ content: {
+ flex: 1,
+ padding: theme.spacing(5, 3, 3, 3),
+ [theme.breakpoints.down('md')]: {
+ paddingTop: theme.spacing(10),
+ },
+ },
+ drawerHeader: {
+ ...theme.mixins.toolbar,
+ display: 'flex',
+ alignItems: 'center',
+ padding: theme.spacing(0, 1),
+ },
+ toolbar: {
+ [theme.breakpoints.down('md')]: {
+ ...theme.mixins.toolbar,
+ },
+ },
+}));
+
+const OptionsLayout = ({ children }) => {
+ const classes = useStyles();
+ const location = useLocation();
+ const [openDrawer, setOpenDrawer] = useState(false);
+ const [optionTitle, setOptionTitle] = useState();
+ const routes = useRoutes();
+
+ useEffect(() => {
+ const activeRoute = routes.find(
+ (route) => route.href && location.pathname.match(route.match || route.href),
+ );
+ setOptionTitle(activeRoute?.name);
+ }, [location, routes]);
+
+ const title = `${t('settingsTitle')} / ${optionTitle}`;
+
+ return (
+ <div className={classes.root}>
+ <Hidden lgUp>
+ <NavBar setOpenDrawer={setOpenDrawer} title={title} />
+ <Drawer
+ variant="temporary"
+ open={openDrawer}
+ onClose={() => setOpenDrawer(!openDrawer)}
+ classes={{ paper: classes.drawer }}
+ >
+ <SideNav routes={routes} />
+ </Drawer>
+ </Hidden>
+
+ <Hidden mdDown>
+ <Drawer
+ variant="permanent"
+ classes={{ root: classes.drawerContainer, paper: classes.drawer }}
+ >
+ <div className={classes.drawerHeader}>
+ <IconButton component="a" href="/">
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6" color="inherit" noWrap>
+ {t('settingsTitle')}
+ </Typography>
+ </div>
+ <Divider />
+ <SideNav routes={routes} />
+ </Drawer>
+ </Hidden>
+
+ <div className={classes.content}>{children}</div>
+ </div>
+ );
+};
+
+export default OptionsLayout;
diff --git a/modern/src/settings/OptionsLayout/useRoutes.js b/modern/src/settings/OptionsLayout/useRoutes.js
new file mode 100644
index 00000000..18320813
--- /dev/null
+++ b/modern/src/settings/OptionsLayout/useRoutes.js
@@ -0,0 +1,86 @@
+import React, { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import CreateIcon from '@material-ui/icons/Create';
+import NotificationsIcon from '@material-ui/icons/Notifications';
+import FolderIcon from '@material-ui/icons/Folder';
+import PersonIcon from '@material-ui/icons/Person';
+import StorageIcon from '@material-ui/icons/Storage';
+import BuildIcon from '@material-ui/icons/Build';
+import PeopleIcon from '@material-ui/icons/People';
+import BarChartIcon from '@material-ui/icons/BarChart';
+import { getIsAdmin, getUserId } from '../../common/selectors';
+import t from '../../common/localization';
+
+const accountRoute = {
+ name: t('settingsUser'),
+ icon: <PersonIcon />,
+};
+
+const adminRoutes = [
+ { subheader: t('userAdmin') },
+ {
+ name: t('settingsServer'),
+ href: '/admin/server',
+ icon: <StorageIcon />,
+ },
+ {
+ name: t('settingsUsers'),
+ href: '/admin/users',
+ icon: <PeopleIcon />,
+ },
+ {
+ name: t('statisticsTitle'),
+ href: '/admin/statistics',
+ icon: <BarChartIcon />,
+ },
+];
+
+const mainRoutes = [
+ accountRoute,
+ {
+ match: 'geofence',
+ name: t('sharedGeofences'),
+ href: '/geofences',
+ icon: <CreateIcon />,
+ },
+ {
+ match: 'notification',
+ name: t('sharedNotifications'),
+ href: '/settings/notifications',
+ icon: <NotificationsIcon />,
+ },
+ {
+ match: 'group',
+ name: t('settingsGroups'),
+ href: '/settings/groups',
+ icon: <FolderIcon />,
+ },
+ {
+ match: 'driver',
+ name: t('sharedDrivers'),
+ href: '/settings/drivers',
+ icon: <PersonIcon />,
+ },
+ {
+ match: 'attribute',
+ name: t('sharedComputedAttributes'),
+ href: '/settings/attributes',
+ icon: <StorageIcon />,
+ },
+ {
+ match: 'maintenance',
+ name: t('sharedMaintenance'),
+ href: '/settings/maintenances',
+ icon: <BuildIcon />,
+ },
+];
+
+export default () => {
+ const isAdmin = useSelector(getIsAdmin);
+ const userId = useSelector(getUserId);
+ accountRoute.href = `/user/${userId}`;
+
+ return useMemo(() => [...mainRoutes, ...(isAdmin ? adminRoutes : [])], [
+ isAdmin,
+ ]);
+};