aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-05-08 17:33:58 -0700
committerAnton Tananaev <anton@traccar.org>2022-05-08 17:33:58 -0700
commitbf95e1bb48379bc1c3482d3f201017250991a832 (patch)
tree044446e36f395d3bcc0f48f8d95e25bc23ba2009 /modern
parented99455abb6c73ded056ae3239cfce01a63ab76f (diff)
downloadtrackermap-web-bf95e1bb48379bc1c3482d3f201017250991a832.tar.gz
trackermap-web-bf95e1bb48379bc1c3482d3f201017250991a832.tar.bz2
trackermap-web-bf95e1bb48379bc1c3482d3f201017250991a832.zip
Implement new page layout
Diffstat (limited to 'modern')
-rw-r--r--modern/src/common/components/PageLayout.js110
-rw-r--r--modern/src/settings/AccumulatorsPage.js7
-rw-r--r--modern/src/settings/CalendarPage.js10
-rw-r--r--modern/src/settings/CalendarsPage.js7
-rw-r--r--modern/src/settings/CommandPage.js10
-rw-r--r--modern/src/settings/CommandSendPage.js7
-rw-r--r--modern/src/settings/CommandsPage.js7
-rw-r--r--modern/src/settings/ComputedAttributePage.js10
-rw-r--r--modern/src/settings/ComputedAttributesPage.js7
-rw-r--r--modern/src/settings/DevicePage.js10
-rw-r--r--modern/src/settings/DriverPage.js10
-rw-r--r--modern/src/settings/DriversPage.js7
-rw-r--r--modern/src/settings/GeofencePage.js10
-rw-r--r--modern/src/settings/GroupPage.js10
-rw-r--r--modern/src/settings/GroupsPage.js7
-rw-r--r--modern/src/settings/MaintenancePage.js10
-rw-r--r--modern/src/settings/MaintenancesPage.js7
-rw-r--r--modern/src/settings/NotificationPage.js10
-rw-r--r--modern/src/settings/NotificationsPage.js7
-rw-r--r--modern/src/settings/PreferencesPage.js7
-rw-r--r--modern/src/settings/ServerPage.js7
-rw-r--r--modern/src/settings/UserPage.js11
-rw-r--r--modern/src/settings/UsersPage.js7
-rw-r--r--modern/src/settings/components/EditItemView.js8
-rw-r--r--modern/src/settings/components/SettingsMenu.js128
25 files changed, 381 insertions, 50 deletions
diff --git a/modern/src/common/components/PageLayout.js b/modern/src/common/components/PageLayout.js
new file mode 100644
index 00000000..e37ae4dc
--- /dev/null
+++ b/modern/src/common/components/PageLayout.js
@@ -0,0 +1,110 @@
+import React, { useState } from 'react';
+import {
+ AppBar, Breadcrumbs, Divider, Drawer, Hidden, IconButton, makeStyles, Toolbar, Typography, useMediaQuery, useTheme,
+} from '@material-ui/core';
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+import MenuIcon from '@material-ui/icons/Menu';
+import { useHistory } from 'react-router-dom';
+import { useTranslation } from './LocalizationProvider';
+
+const useStyles = makeStyles((theme) => ({
+ desktopRoot: {
+ height: '100%',
+ display: 'flex',
+ },
+ mobileRoot: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ desktopDrawer: {
+ width: theme.dimensions.drawerWidthDesktop,
+ },
+ mobileDrawer: {
+ width: theme.dimensions.drawerWidthTablet,
+ },
+ toolbar: theme.mixins.toolbar,
+ content: {
+ flexGrow: 1,
+ alignItems: 'stretch',
+ overflow: 'auto',
+ },
+}));
+
+const PageTitle = ({ breadcrumbs }) => {
+ const theme = useTheme();
+ const t = useTranslation();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ if (desktop) {
+ return (
+ <Typography variant="h6" noWrap>{t(breadcrumbs.at(0))}</Typography>
+ );
+ }
+ return (
+ <Breadcrumbs>
+ {breadcrumbs.slice(0, -1).map((breadcrumb) => (
+ <Typography variant="h6" color="inherit" key={breadcrumb}>{t(breadcrumb)}</Typography>
+ ))}
+ <Typography variant="h6" color="textPrimary">{t(breadcrumbs.at(-1))}</Typography>
+ </Breadcrumbs>
+ );
+};
+
+const PageLayout = ({ menu, breadcrumbs, children }) => {
+ const classes = useStyles();
+ const history = useHistory();
+
+ const [openDrawer, setOpenDrawer] = useState(false);
+
+ return (
+ <>
+ <Hidden smDown>
+ <div className={classes.desktopRoot}>
+ <Drawer
+ variant="permanent"
+ className={classes.desktopDrawer}
+ classes={{ paper: classes.desktopDrawer }}
+ >
+ <div className={classes.toolbar}>
+ <Toolbar>
+ <IconButton color="inherit" edge="start" onClick={() => history.push('/')}>
+ <ArrowBackIcon />
+ </IconButton>
+ <PageTitle breadcrumbs={breadcrumbs} />
+ </Toolbar>
+ </div>
+ <Divider />
+ {menu}
+ </Drawer>
+ <div className={classes.content}>{children}</div>
+ </div>
+ </Hidden>
+
+ <Hidden mdUp>
+ <div className={classes.mobileRoot}>
+ <Drawer
+ variant="temporary"
+ open={openDrawer}
+ onClose={() => setOpenDrawer(false)}
+ classes={{ paper: classes.mobileDrawer }}
+ >
+ {menu}
+ </Drawer>
+ <AppBar position="static" color="inherit">
+ <Toolbar>
+ <IconButton color="inherit" edge="start" onClick={() => setOpenDrawer(true)}>
+ <MenuIcon />
+ </IconButton>
+ <PageTitle breadcrumbs={breadcrumbs} />
+ </Toolbar>
+ </AppBar>
+ <div className={classes.content}>{children}</div>
+ </div>
+ </Hidden>
+ </>
+ );
+};
+
+export default PageLayout;
diff --git a/modern/src/settings/AccumulatorsPage.js b/modern/src/settings/AccumulatorsPage.js
index f8895bbe..0fcde8d1 100644
--- a/modern/src/settings/AccumulatorsPage.js
+++ b/modern/src/settings/AccumulatorsPage.js
@@ -6,7 +6,8 @@ import {
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { useTranslation } from '../common/components/LocalizationProvider';
-import OptionsLayout from './components/OptionsLayout';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
container: {
@@ -57,7 +58,7 @@ const AccumulatorsPage = () => {
};
return (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['sharedDeviceAccumulators']}>
{item && (
<Container maxWidth="xs" className={classes.container}>
<Accordion defaultExpanded>
@@ -107,7 +108,7 @@ const AccumulatorsPage = () => {
</FormControl>
</Container>
)}
- </OptionsLayout>
+ </PageLayout>
);
};
diff --git a/modern/src/settings/CalendarPage.js b/modern/src/settings/CalendarPage.js
index ec793196..154b3f11 100644
--- a/modern/src/settings/CalendarPage.js
+++ b/modern/src/settings/CalendarPage.js
@@ -8,6 +8,7 @@ import { DropzoneArea } from 'material-ui-dropzone';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -37,7 +38,14 @@ const CalendarPage = () => {
const validate = () => item && item.name && item.data;
return (
- <EditItemView endpoint="calendars" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="calendars"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedCalendar']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/CalendarsPage.js b/modern/src/settings/CalendarsPage.js
index db4dc770..875530a8 100644
--- a/modern/src/settings/CalendarsPage.js
+++ b/modern/src/settings/CalendarsPage.js
@@ -5,8 +5,9 @@ import {
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -55,9 +56,9 @@ const CalendarsView = ({ updateTimestamp, onMenuClick }) => {
};
const CalendarsPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedCalendars']}>
<EditCollectionView content={CalendarsView} editPath="/settings/calendar" endpoint="calendars" />
- </OptionsLayout>
+ </PageLayout>
);
export default CalendarsPage;
diff --git a/modern/src/settings/CommandPage.js b/modern/src/settings/CommandPage.js
index 99bb21f2..4785021b 100644
--- a/modern/src/settings/CommandPage.js
+++ b/modern/src/settings/CommandPage.js
@@ -6,6 +6,7 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import EditItemView from './components/EditItemView';
import { useTranslation } from '../common/components/LocalizationProvider';
import BaseCommandView from './components/BaseCommandView';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -22,7 +23,14 @@ const CommandPage = () => {
const validate = () => item && item.type;
return (
- <EditItemView endpoint="commands" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="commands"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedSavedCommand']}
+ >
{item && (
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
diff --git a/modern/src/settings/CommandSendPage.js b/modern/src/settings/CommandSendPage.js
index 79de1b84..f7e62b24 100644
--- a/modern/src/settings/CommandSendPage.js
+++ b/modern/src/settings/CommandSendPage.js
@@ -5,9 +5,10 @@ import {
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { useTranslation } from '../common/components/LocalizationProvider';
-import OptionsLayout from './components/OptionsLayout';
import BaseCommandView from './components/BaseCommandView';
import SelectField from '../common/components/SelectField';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
container: {
@@ -62,7 +63,7 @@ const CommandSendPage = () => {
const validate = () => savedId || (item && item.type);
return (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'deviceCommand']}>
<Container maxWidth="xs" className={classes.container}>
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
@@ -108,7 +109,7 @@ const CommandSendPage = () => {
</div>
</FormControl>
</Container>
- </OptionsLayout>
+ </PageLayout>
);
};
diff --git a/modern/src/settings/CommandsPage.js b/modern/src/settings/CommandsPage.js
index 6081b48c..20b792b8 100644
--- a/modern/src/settings/CommandsPage.js
+++ b/modern/src/settings/CommandsPage.js
@@ -5,10 +5,11 @@ import {
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
import { formatBoolean } from '../common/util/formatter';
import { prefixString } from '../common/util/stringUtils';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -61,9 +62,9 @@ const CommandsView = ({ updateTimestamp, onMenuClick }) => {
};
const CommandsPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedSavedCommands']}>
<EditCollectionView content={CommandsView} editPath="/settings/command" endpoint="commands" />
- </OptionsLayout>
+ </PageLayout>
);
export default CommandsPage;
diff --git a/modern/src/settings/ComputedAttributePage.js b/modern/src/settings/ComputedAttributePage.js
index 52a583dc..984339b8 100644
--- a/modern/src/settings/ComputedAttributePage.js
+++ b/modern/src/settings/ComputedAttributePage.js
@@ -6,6 +6,7 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import EditItemView from './components/EditItemView';
import { useTranslation } from '../common/components/LocalizationProvider';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -42,7 +43,14 @@ const ComputedAttributePage = () => {
const validate = () => item && item.description && item.expression;
return (
- <EditItemView endpoint="attributes/computed" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="attributes/computed"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedComputedAttribute']}
+ >
{item && (
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
diff --git a/modern/src/settings/ComputedAttributesPage.js b/modern/src/settings/ComputedAttributesPage.js
index 3b60419d..451b47a7 100644
--- a/modern/src/settings/ComputedAttributesPage.js
+++ b/modern/src/settings/ComputedAttributesPage.js
@@ -5,9 +5,10 @@ import {
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
import { useAdministrator } from '../common/util/permissions';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -65,9 +66,9 @@ const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => {
};
const ComputedAttributesPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedComputedAttributes']}>
<EditCollectionView content={ComputedAttributeView} editPath="/settings/attribute" endpoint="attributes/computed" />
- </OptionsLayout>
+ </PageLayout>
);
export default ComputedAttributesPage;
diff --git a/modern/src/settings/DevicePage.js b/modern/src/settings/DevicePage.js
index e40649a5..491ba60f 100644
--- a/modern/src/settings/DevicePage.js
+++ b/modern/src/settings/DevicePage.js
@@ -14,6 +14,7 @@ import { prefixString } from '../common/util/stringUtils';
import { useTranslation } from '../common/components/LocalizationProvider';
import useDeviceAttributes from '../common/attributes/useDeviceAttributes';
import { useAdministrator } from '../common/util/permissions';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -34,7 +35,14 @@ const DevicePage = () => {
const validate = () => item && item.name && item.uniqueId;
return (
- <EditItemView endpoint="devices" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="devices"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['sharedDevice']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/DriverPage.js b/modern/src/settings/DriverPage.js
index dee11d37..93d09c8e 100644
--- a/modern/src/settings/DriverPage.js
+++ b/modern/src/settings/DriverPage.js
@@ -7,6 +7,7 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -23,7 +24,14 @@ const DriverPage = () => {
const validate = () => item && item.name && item.uniqueId;
return (
- <EditItemView endpoint="drivers" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="drivers"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedDriver']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/DriversPage.js b/modern/src/settings/DriversPage.js
index 5b01bdfa..f5908381 100644
--- a/modern/src/settings/DriversPage.js
+++ b/modern/src/settings/DriversPage.js
@@ -5,8 +5,9 @@ import {
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -57,9 +58,9 @@ const DriversView = ({ updateTimestamp, onMenuClick }) => {
};
const DriversPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}>
<EditCollectionView content={DriversView} editPath="/settings/driver" endpoint="drivers" />
- </OptionsLayout>
+ </PageLayout>
);
export default DriversPage;
diff --git a/modern/src/settings/GeofencePage.js b/modern/src/settings/GeofencePage.js
index 0fa986d2..e23d49fb 100644
--- a/modern/src/settings/GeofencePage.js
+++ b/modern/src/settings/GeofencePage.js
@@ -9,6 +9,7 @@ import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import { useTranslation } from '../common/components/LocalizationProvider';
import useGeofenceAttributes from '../common/attributes/useGeofenceAttributes';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -27,7 +28,14 @@ const GeofencePage = () => {
const validate = () => item && item.name;
return (
- <EditItemView endpoint="geofences" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="geofences"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedGeofence']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/GroupPage.js b/modern/src/settings/GroupPage.js
index 750d6ebc..f56a2fd3 100644
--- a/modern/src/settings/GroupPage.js
+++ b/modern/src/settings/GroupPage.js
@@ -10,6 +10,7 @@ import EditAttributesView from './components/EditAttributesView';
import useDeviceAttributes from '../common/attributes/useDeviceAttributes';
import SelectField from '../common/components/SelectField';
import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -28,7 +29,14 @@ const GroupPage = () => {
const validate = () => item && item.name;
return (
- <EditItemView endpoint="groups" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="groups"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'groupDialog']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js
index 3642c02f..5d23c2d9 100644
--- a/modern/src/settings/GroupsPage.js
+++ b/modern/src/settings/GroupsPage.js
@@ -5,8 +5,9 @@ import {
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -55,9 +56,9 @@ const GroupsView = ({ updateTimestamp, onMenuClick }) => {
};
const GroupsPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsGroups']}>
<EditCollectionView content={GroupsView} editPath="/settings/group" endpoint="groups" />
- </OptionsLayout>
+ </PageLayout>
);
export default GroupsPage;
diff --git a/modern/src/settings/MaintenancePage.js b/modern/src/settings/MaintenancePage.js
index b55ce35b..9e53aca1 100644
--- a/modern/src/settings/MaintenancePage.js
+++ b/modern/src/settings/MaintenancePage.js
@@ -13,6 +13,7 @@ import {
} from '../common/util/converter';
import { useTranslation } from '../common/components/LocalizationProvider';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -97,7 +98,14 @@ const MaintenancePage = () => {
const validate = () => item && item.name && item.type && item.start && item.period;
return (
- <EditItemView endpoint="maintenance" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="maintenance"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedMaintenance']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/MaintenancesPage.js b/modern/src/settings/MaintenancesPage.js
index 038e5dff..3801f010 100644
--- a/modern/src/settings/MaintenancesPage.js
+++ b/modern/src/settings/MaintenancesPage.js
@@ -9,8 +9,9 @@ import EditCollectionView from './components/EditCollectionView';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
import { formatDistance, formatSpeed } from '../common/util/formatter';
import { useAttributePreference } from '../common/util/preferences';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -85,9 +86,9 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => {
};
const MaintenacesPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedMaintenance']}>
<EditCollectionView content={MaintenancesView} editPath="/settings/maintenance" endpoint="maintenance" />
- </OptionsLayout>
+ </PageLayout>
);
export default MaintenacesPage;
diff --git a/modern/src/settings/NotificationPage.js b/modern/src/settings/NotificationPage.js
index ed0b3e5e..38ba19e5 100644
--- a/modern/src/settings/NotificationPage.js
+++ b/modern/src/settings/NotificationPage.js
@@ -8,6 +8,7 @@ import { useTranslation, useTranslationKeys } from '../common/components/Localiz
import EditItemView from './components/EditItemView';
import { prefixString, unprefixString } from '../common/util/stringUtils';
import SelectField from '../common/components/SelectField';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -29,7 +30,14 @@ const NotificationPage = () => {
const validate = () => item && item.type && item.notificators;
return (
- <EditItemView endpoint="notifications" item={item} setItem={setItem} validate={validate}>
+ <EditItemView
+ endpoint="notifications"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedNotification']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/NotificationsPage.js b/modern/src/settings/NotificationsPage.js
index d96bc3da..7ad5186b 100644
--- a/modern/src/settings/NotificationsPage.js
+++ b/modern/src/settings/NotificationsPage.js
@@ -7,8 +7,9 @@ import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
import { prefixString } from '../common/util/stringUtils';
import { formatBoolean } from '../common/util/formatter';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -74,9 +75,9 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => {
};
const NotificationsPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedNotifications']}>
<EditCollectionView content={NotificationsView} editPath="/settings/notification" endpoint="notifications" />
- </OptionsLayout>
+ </PageLayout>
);
export default NotificationsPage;
diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js
index 8b259bf2..1256a53a 100644
--- a/modern/src/settings/PreferencesPage.js
+++ b/modern/src/settings/PreferencesPage.js
@@ -4,8 +4,9 @@ import {
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { useLocalization, useTranslation } from '../common/components/LocalizationProvider';
-import OptionsLayout from './components/OptionsLayout';
import usePersistedState from '../common/util/usePersistedState';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
container: {
@@ -28,7 +29,7 @@ const PreferencesPage = () => {
const [mapCluster, setMapCluster] = usePersistedState('mapCluster', true);
return (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedPreferences']}>
<Container maxWidth="xs" className={classes.container}>
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
@@ -67,7 +68,7 @@ const PreferencesPage = () => {
</AccordionDetails>
</Accordion>
</Container>
- </OptionsLayout>
+ </PageLayout>
);
};
diff --git a/modern/src/settings/ServerPage.js b/modern/src/settings/ServerPage.js
index f98bc691..8943252e 100644
--- a/modern/src/settings/ServerPage.js
+++ b/modern/src/settings/ServerPage.js
@@ -11,9 +11,10 @@ import { sessionActions } from '../store';
import EditAttributesView from './components/EditAttributesView';
import useDeviceAttributes from '../common/attributes/useDeviceAttributes';
import useUserAttributes from '../common/attributes/useUserAttributes';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
import SelectField from '../common/components/SelectField';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
container: {
@@ -57,7 +58,7 @@ const ServerPage = () => {
};
return (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsServer']}>
<Container maxWidth="xs" className={classes.container}>
{item && (
<>
@@ -234,7 +235,7 @@ const ServerPage = () => {
</div>
</FormControl>
</Container>
- </OptionsLayout>
+ </PageLayout>
);
};
diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js
index 2b5adcca..7200329a 100644
--- a/modern/src/settings/UserPage.js
+++ b/modern/src/settings/UserPage.js
@@ -13,6 +13,7 @@ import { useTranslation } from '../common/components/LocalizationProvider';
import useUserAttributes from '../common/attributes/useUserAttributes';
import { sessionActions } from '../store';
import SelectField from '../common/components/SelectField';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles(() => ({
details: {
@@ -40,7 +41,15 @@ const UserPage = () => {
const validate = () => item && item.name && item.email && (item.id || item.password);
return (
- <EditItemView endpoint="users" item={item} setItem={setItem} validate={validate} onItemSaved={onItemSaved}>
+ <EditItemView
+ endpoint="users"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ onItemSaved={onItemSaved}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'settingsUser']}
+ >
{item && (
<>
<Accordion defaultExpanded>
diff --git a/modern/src/settings/UsersPage.js b/modern/src/settings/UsersPage.js
index bdadf845..2d8d1199 100644
--- a/modern/src/settings/UsersPage.js
+++ b/modern/src/settings/UsersPage.js
@@ -6,8 +6,9 @@ import MoreVertIcon from '@material-ui/icons/MoreVert';
import { useEffectAsync } from '../reactHelper';
import EditCollectionView from './components/EditCollectionView';
import { formatBoolean } from '../common/util/formatter';
-import OptionsLayout from './components/OptionsLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -62,9 +63,9 @@ const UsersView = ({ updateTimestamp, onMenuClick }) => {
};
const UsersPage = () => (
- <OptionsLayout>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsUsers']}>
<EditCollectionView content={UsersView} editPath="/settings/user" endpoint="users" />
- </OptionsLayout>
+ </PageLayout>
);
export default UsersPage;
diff --git a/modern/src/settings/components/EditItemView.js b/modern/src/settings/components/EditItemView.js
index 90e5294a..2283ebda 100644
--- a/modern/src/settings/components/EditItemView.js
+++ b/modern/src/settings/components/EditItemView.js
@@ -6,8 +6,8 @@ import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import { useEffectAsync } from '../../reactHelper';
-import OptionsLayout from './OptionsLayout';
import { useTranslation } from '../../common/components/LocalizationProvider';
+import PageLayout from '../../common/components/PageLayout';
const useStyles = makeStyles((theme) => ({
container: {
@@ -23,7 +23,7 @@ const useStyles = makeStyles((theme) => ({
}));
const EditItemView = ({
- children, endpoint, item, setItem, validate, onItemSaved,
+ children, endpoint, item, setItem, validate, onItemSaved, menu, breadcrumbs,
}) => {
const history = useHistory();
const classes = useStyles();
@@ -63,7 +63,7 @@ const EditItemView = ({
};
return (
- <OptionsLayout>
+ <PageLayout menu={menu} breadcrumbs={breadcrumbs}>
<Container maxWidth="xs" className={classes.container}>
{children}
<FormControl fullWidth margin="normal">
@@ -88,7 +88,7 @@ const EditItemView = ({
</div>
</FormControl>
</Container>
- </OptionsLayout>
+ </PageLayout>
);
};
diff --git a/modern/src/settings/components/SettingsMenu.js b/modern/src/settings/components/SettingsMenu.js
new file mode 100644
index 00000000..036f4101
--- /dev/null
+++ b/modern/src/settings/components/SettingsMenu.js
@@ -0,0 +1,128 @@
+import React from 'react';
+import {
+ Divider, List, ListItem, ListItemIcon, ListItemText,
+} from '@material-ui/core';
+import SettingsIcon from '@material-ui/icons/Settings';
+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 TodayIcon from '@material-ui/icons/Today';
+import PublishIcon from '@material-ui/icons/Publish';
+import { Link, useLocation } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import { useAdministrator, useReadonly } from '../../common/util/permissions';
+
+const MenuItem = ({
+ title, link, icon, selected,
+}) => (
+ <ListItem button key={link} component={Link} to={link} selected={selected}>
+ <ListItemIcon>{icon}</ListItemIcon>
+ <ListItemText primary={title} />
+ </ListItem>
+);
+
+const SettingsMenu = () => {
+ const t = useTranslation();
+ const location = useLocation();
+
+ const readonly = useReadonly();
+ const admin = useAdministrator();
+ const userId = useSelector((state) => state.session.user?.id);
+
+ return (
+ <>
+ <List>
+ <MenuItem
+ title={t('sharedPreferences')}
+ link="/settings/preferences"
+ icon={<SettingsIcon />}
+ selected={location.pathname === '/settings/preferences'}
+ />
+ {!readonly && (
+ <>
+ <MenuItem
+ title={t('sharedNotifications')}
+ link="/settings/notifications"
+ icon={<NotificationsIcon />}
+ selected={location.pathname.startsWith('/settings/notification')}
+ />
+ <MenuItem
+ title={t('settingsUser')}
+ link={`/settings/user/${userId}`}
+ icon={<PersonIcon />}
+ selected={location.pathname === `/settings/user/${userId}`}
+ />
+ <MenuItem
+ title={t('sharedGeofences')}
+ link="/geofences"
+ icon={<CreateIcon />}
+ selected={location.pathname.startsWith('/settings/geofence')}
+ />
+ <MenuItem
+ title={t('settingsGroups')}
+ link="/settings/groups"
+ icon={<FolderIcon />}
+ selected={location.pathname.startsWith('/settings/group')}
+ />
+ <MenuItem
+ title={t('sharedDrivers')}
+ link="/settings/drivers"
+ icon={<PersonIcon />}
+ selected={location.pathname.startsWith('/settings/driver')}
+ />
+ <MenuItem
+ title={t('sharedCalendars')}
+ link="/settings/calendars"
+ icon={<TodayIcon />}
+ selected={location.pathname.startsWith('/settings/calendar')}
+ />
+ <MenuItem
+ title={t('sharedComputedAttributes')}
+ link="/settings/attributes"
+ icon={<StorageIcon />}
+ selected={location.pathname.startsWith('/settings/attribute')}
+ />
+ <MenuItem
+ title={t('sharedMaintenance')}
+ link="/settings/maintenances"
+ icon={<BuildIcon />}
+ selected={location.pathname.startsWith('/settings/maintenance')}
+ />
+ <MenuItem
+ title={t('sharedSavedCommands')}
+ link="/settings/commands"
+ icon={<PublishIcon />}
+ selected={location.pathname.startsWith('/settings/command')}
+ />
+ </>
+ )}
+ </List>
+ {admin && (
+ <>
+ <Divider />
+ <List>
+ <MenuItem
+ title={t('settingsServer')}
+ link="/settings/server"
+ icon={<StorageIcon />}
+ selected={location.pathname === '/settings/server'}
+ />
+ <MenuItem
+ title={t('settingsUsers')}
+ link="/settings/users"
+ icon={<PeopleIcon />}
+ selected={location.pathname.startsWith('/settings/user') && location.pathname !== `/settings/user/${userId}`}
+ />
+ </List>
+ </>
+ )}
+ </>
+ );
+};
+
+export default SettingsMenu;