aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
authorBoubrid Ihab <boubrid.ihab@gmail.com>2022-05-23 17:15:39 +0100
committerBoubrid Ihab <boubrid.ihab@gmail.com>2022-05-23 17:15:39 +0100
commit0b9209f877c42d9eb69d4b94cfa9c7f16a81967e (patch)
tree72286ee879bea52fa2dad73f36d1e2b36a35bc15 /modern
parentc30e57579868b8f3d21e6e6083e37cba8bcfa408 (diff)
parent0ed610e71b28461d6c9cc996764b00db5ac0b2ba (diff)
downloadtrackermap-web-0b9209f877c42d9eb69d4b94cfa9c7f16a81967e.tar.gz
trackermap-web-0b9209f877c42d9eb69d4b94cfa9c7f16a81967e.tar.bz2
trackermap-web-0b9209f877c42d9eb69d4b94cfa9c7f16a81967e.zip
Merge branch 'master' of github.com:traccar/traccar-web into fix-key-issue
Diffstat (limited to 'modern')
-rw-r--r--modern/.eslintrc.js2
-rw-r--r--modern/package.json38
-rw-r--r--modern/src/App.js166
-rw-r--r--modern/src/Navigation.js145
-rw-r--r--modern/src/SocketController.js8
-rw-r--r--modern/src/common/components/BottomMenu.js26
-rw-r--r--modern/src/common/components/ErrorHandler.js4
-rw-r--r--modern/src/common/components/LinkField.js6
-rw-r--r--modern/src/common/components/LocalizationProvider.js7
-rw-r--r--modern/src/common/components/NavBar.js10
-rw-r--r--modern/src/common/components/PageLayout.js26
-rw-r--r--modern/src/common/components/PositionValue.js2
-rw-r--r--modern/src/common/components/RemoveDialog.js5
-rw-r--r--modern/src/common/components/SelectField.js6
-rw-r--r--modern/src/common/components/SideNav.js2
-rw-r--r--modern/src/common/theme/components.js15
-rw-r--r--modern/src/common/theme/index.js6
-rw-r--r--modern/src/common/theme/overrides.js87
-rw-r--r--modern/src/index.js38
-rw-r--r--modern/src/login/LoginLayout.js14
-rw-r--r--modern/src/login/LoginPage.js40
-rw-r--r--modern/src/login/RegisterPage.js18
-rw-r--r--modern/src/login/ResetPasswordPage.js17
-rw-r--r--modern/src/main/DevicesList.js45
-rw-r--r--modern/src/main/MainPage.js35
-rw-r--r--modern/src/main/StatusCard.js82
-rw-r--r--modern/src/map/MapGeofenceEdit.js8
-rw-r--r--modern/src/other/EventPage.js22
-rw-r--r--modern/src/other/GeofencesList.js39
-rw-r--r--modern/src/other/GeofencesPage.js34
-rw-r--r--modern/src/other/PositionPage.js23
-rw-r--r--modern/src/other/ReplayPage.js31
-rw-r--r--modern/src/reports/ChartReportPage.js9
-rw-r--r--modern/src/reports/EventReportPage.js5
-rw-r--r--modern/src/reports/RouteReportPage.js2
-rw-r--r--modern/src/reports/StatisticsPage.js2
-rw-r--r--modern/src/reports/StopReportPage.js2
-rw-r--r--modern/src/reports/SummaryReportPage.js6
-rw-r--r--modern/src/reports/TripReportPage.js2
-rw-r--r--modern/src/reports/components/ColumnSelect.js5
-rw-r--r--modern/src/reports/components/ReportFilter.js15
-rw-r--r--modern/src/reports/components/ReportsMenu.js16
-rw-r--r--modern/src/settings/AccumulatorsPage.js24
-rw-r--r--modern/src/settings/CalendarPage.js10
-rw-r--r--modern/src/settings/CalendarsPage.js63
-rw-r--r--modern/src/settings/CommandPage.js8
-rw-r--r--modern/src/settings/CommandSendPage.js22
-rw-r--r--modern/src/settings/CommandsPage.js71
-rw-r--r--modern/src/settings/ComputedAttributePage.js22
-rw-r--r--modern/src/settings/ComputedAttributesPage.js79
-rw-r--r--modern/src/settings/DevicePage.js27
-rw-r--r--modern/src/settings/DriverPage.js11
-rw-r--r--modern/src/settings/DriversPage.js67
-rw-r--r--modern/src/settings/GeofencePage.js10
-rw-r--r--modern/src/settings/GroupPage.js17
-rw-r--r--modern/src/settings/GroupsPage.js63
-rw-r--r--modern/src/settings/MaintenancePage.js23
-rw-r--r--modern/src/settings/MaintenancesPage.js76
-rw-r--r--modern/src/settings/NotificationPage.js111
-rw-r--r--modern/src/settings/NotificationsPage.js75
-rw-r--r--modern/src/settings/PreferencesPage.js28
-rw-r--r--modern/src/settings/ServerPage.js47
-rw-r--r--modern/src/settings/UserPage.js70
-rw-r--r--modern/src/settings/UsersPage.js75
-rw-r--r--modern/src/settings/components/AddAttributeDialog.js9
-rw-r--r--modern/src/settings/components/BaseCommandView.js4
-rw-r--r--modern/src/settings/components/CollectionActions.js48
-rw-r--r--modern/src/settings/components/CollectionFab.js36
-rw-r--r--modern/src/settings/components/EditAttributesView.js22
-rw-r--r--modern/src/settings/components/EditCollectionView.js89
-rw-r--r--modern/src/settings/components/EditItemView.js16
-rw-r--r--modern/src/settings/components/SettingsMenu.js22
72 files changed, 1182 insertions, 1134 deletions
diff --git a/modern/.eslintrc.js b/modern/.eslintrc.js
index f380f4c6..6cef63d8 100644
--- a/modern/.eslintrc.js
+++ b/modern/.eslintrc.js
@@ -15,7 +15,9 @@ module.exports = {
'no-prototype-builtins': [0],
'no-nested-ternary': [0],
'operator-linebreak': [0],
+ 'import/no-unresolved': [0],
'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
+ 'react/function-component-definition': [1, { namedComponents: 'arrow-function', unnamedComponents: 'arrow-function' }],
'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 771a786b..320d3411 100644
--- a/modern/package.json
+++ b/modern/package.json
@@ -4,28 +4,30 @@
"private": true,
"dependencies": {
"@craco/craco": "^5.9.0",
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
"@mapbox/mapbox-gl-draw": "^1.3.0",
- "@material-ui/core": "^4.12.4",
- "@material-ui/icons": "^4.11.3",
- "@material-ui/lab": "^4.0.0-alpha.58",
- "@reduxjs/toolkit": "^1.6.0",
+ "@mui/icons-material": "^5.8.0",
+ "@mui/lab": "^5.0.0-alpha.82",
+ "@mui/material": "^5.8.0",
+ "@mui/styles": "^5.8.0",
+ "@reduxjs/toolkit": "^1.8.1",
"@tmcw/togeojson": "^4.5.0",
"@turf/circle": "^6.5.0",
- "@turf/turf": "^6.4.0",
- "mapbox-gl": "^1.13.1",
- "maplibre-gl": "^1.15.0",
+ "@turf/turf": "^6.5.0",
+ "mapbox-gl": "^1.13.2",
+ "maplibre-gl": "^2.1.9",
"material-ui-dropzone": "^3.5.0",
- "moment": "^2.29.1",
+ "moment": "^2.29.3",
"react": "^17.0.2",
- "react-container-dimensions": "^1.4.1",
"react-dom": "^17.0.2",
- "react-redux": "^7.2.4",
- "react-router-dom": "^5.2.0",
+ "react-redux": "^8.0.2",
+ "react-router-dom": "^6.3.0",
"react-scripts": "^3.4.4",
"react-virtualized-auto-sizer": "^1.0.5",
"react-window": "^1.8.6",
- "recharts": "^2.0.9",
- "redux": "^4.1.0",
+ "recharts": "^2.1.10",
+ "redux": "^4.2.0",
"typeface-roboto": "1.1.13",
"wellknown": "^0.5.0"
},
@@ -50,10 +52,10 @@
]
},
"devDependencies": {
- "eslint": "^7.30.0",
- "eslint-config-airbnb": "^18.2.1",
- "eslint-plugin-import": "^2.23.4",
- "eslint-plugin-react": "^7.24.0",
- "eslint-plugin-react-hooks": "^1.7.0"
+ "eslint": "^8.16.0",
+ "eslint-config-airbnb": "^19.0.4",
+ "eslint-plugin-import": "^2.26.0",
+ "eslint-plugin-react": "^7.30.0",
+ "eslint-plugin-react-hooks": "^4.5.0"
}
}
diff --git a/modern/src/App.js b/modern/src/App.js
index fee94e5b..3ba6b2e1 100644
--- a/modern/src/App.js
+++ b/modern/src/App.js
@@ -1,56 +1,10 @@
-import React, { useState } from 'react';
-import { ThemeProvider } from '@material-ui/core/styles';
-import { Switch, Route, useHistory } from 'react-router-dom';
-import CssBaseline from '@material-ui/core/CssBaseline';
-import { useDispatch, useSelector } from 'react-redux';
-import { makeStyles, LinearProgress, useMediaQuery } from '@material-ui/core';
-import MainPage from './main/MainPage';
-import RouteReportPage from './reports/RouteReportPage';
-import ServerPage from './settings/ServerPage';
-import UsersPage from './settings/UsersPage';
-import DevicePage from './settings/DevicePage';
-import UserPage from './settings/UserPage';
-import SocketController from './SocketController';
-import NotificationsPage from './settings/NotificationsPage';
-import NotificationPage from './settings/NotificationPage';
-import GroupsPage from './settings/GroupsPage';
-import GroupPage from './settings/GroupPage';
-import PositionPage from './other/PositionPage';
-import EventReportPage from './reports/EventReportPage';
-import ReplayPage from './other/ReplayPage';
-import TripReportPage from './reports/TripReportPage';
-import StopReportPage from './reports/StopReportPage';
-import SummaryReportPage from './reports/SummaryReportPage';
-import ChartReportPage from './reports/ChartReportPage';
-import DriversPage from './settings/DriversPage';
-import DriverPage from './settings/DriverPage';
-import CalendarsPage from './settings/CalendarsPage';
-import CalendarPage from './settings/CalendarPage';
-import ComputedAttributesPage from './settings/ComputedAttributesPage';
-import ComputedAttributePage from './settings/ComputedAttributePage';
-import MaintenancesPage from './settings/MaintenancesPage';
-import MaintenancePage from './settings/MaintenancePage';
-import CommandsPage from './settings/CommandsPage';
-import CommandPage from './settings/CommandPage';
-import StatisticsPage from './reports/StatisticsPage';
-import CachingController from './CachingController';
-
-import LoginPage from './login/LoginPage';
-import RegisterPage from './login/RegisterPage';
-import ResetPasswordPage from './login/ResetPasswordPage';
-
+import React from 'react';
+import { Outlet } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import { LinearProgress, useMediaQuery } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import theme from './common/theme';
-import GeofencesPage from './other/GeofencesPage';
-import GeofencePage from './settings/GeofencePage';
-import useQuery from './common/util/useQuery';
-import { useEffectAsync } from './reactHelper';
-import { devicesActions } from './store';
-import EventPage from './other/EventPage';
-import PreferencesPage from './settings/PreferencesPage';
import BottomMenu from './common/components/BottomMenu';
-import AccumulatorsPage from './settings/AccumulatorsPage';
-import CommandSendPage from './settings/CommandSendPage';
-import ErrorHandler from './common/components/ErrorHandler';
const useStyles = makeStyles(() => ({
page: {
@@ -63,107 +17,27 @@ const useStyles = makeStyles(() => ({
}));
const App = () => {
- const history = useHistory();
- const dispatch = useDispatch();
const classes = useStyles();
const desktop = useMediaQuery(theme.breakpoints.up('md'));
const initialized = useSelector((state) => !!state.session.server && !!state.session.user);
- const [redirectsHandled, setRedirectsHandled] = useState(false);
-
- const query = useQuery();
-
- useEffectAsync(async () => {
- if (query.get('token')) {
- const token = query.get('token');
- await fetch(`/api/session?token=${encodeURIComponent(token)}`);
- history.push('/');
- } else if (query.get('deviceId')) {
- const deviceId = query.get('deviceId');
- const response = await fetch(`/api/devices?uniqueId=${deviceId}`);
- if (response.ok) {
- const items = await response.json();
- if (items.length > 0) {
- dispatch(devicesActions.select(items[0].id));
- }
- } else {
- throw Error(await response.text());
- }
- history.push('/');
- } else if (query.get('eventId')) {
- const eventId = parseInt(query.get('eventId'), 10);
- history.push(`/event/${eventId}`);
- } else {
- setRedirectsHandled(true);
- }
- }, [query]);
-
- return (!redirectsHandled ? (<LinearProgress />) : (
- <ThemeProvider theme={theme}>
- <CssBaseline />
- <SocketController />
- <CachingController />
- <Switch>
- <Route exact path="/login" component={LoginPage} />
- <Route exact path="/register" component={RegisterPage} />
- <Route exact path="/reset-password" component={ResetPasswordPage} />
- <Route>
- {!initialized ? (<LinearProgress />) : (
- <>
- <div className={classes.page}>
- <Switch>
- <Route exact path="/" component={MainPage} />
-
- <Route exact path="/position/:id?" component={PositionPage} />
- <Route exact path="/event/:id?" component={EventPage} />
- <Route exact path="/replay" component={ReplayPage} />
- <Route exact path="/geofences" component={GeofencesPage} />
-
- <Route exact path="/settings/accumulators/:deviceId?" component={AccumulatorsPage} />
- <Route exact path="/settings/calendars" component={CalendarsPage} />
- <Route exact path="/settings/calendar/:id?" component={CalendarPage} />
- <Route exact path="/settings/commands" component={CommandsPage} />
- <Route exact path="/settings/command/:id?" component={CommandPage} />
- <Route exact path="/settings/command-send/:deviceId?" component={CommandSendPage} />
- <Route exact path="/settings/attributes" component={ComputedAttributesPage} />
- <Route exact path="/settings/attribute/:id?" component={ComputedAttributePage} />
- <Route exact path="/settings/device/:id?" component={DevicePage} />
- <Route exact path="/settings/drivers" component={DriversPage} />
- <Route exact path="/settings/driver/:id?" component={DriverPage} />
- <Route exact path="/settings/geofence/:id?" component={GeofencePage} />
- <Route exact path="/settings/groups" component={GroupsPage} />
- <Route exact path="/settings/group/:id?" component={GroupPage} />
- <Route exact path="/settings/maintenances" component={MaintenancesPage} />
- <Route exact path="/settings/maintenance/:id?" component={MaintenancePage} />
- <Route exact path="/settings/notifications" component={NotificationsPage} />
- <Route exact path="/settings/notification/:id?" component={NotificationPage} />
- <Route exact path="/settings/preferences" component={PreferencesPage} />
- <Route exact path="/settings/server" component={ServerPage} />
- <Route exact path="/settings/users" component={UsersPage} />
- <Route exact path="/settings/user/:id?" component={UserPage} />
- <Route exact path="/reports/chart" component={ChartReportPage} />
- <Route exact path="/reports/event" component={EventReportPage} />
- <Route exact path="/reports/route" component={RouteReportPage} />
- <Route exact path="/reports/statistics" component={StatisticsPage} />
- <Route exact path="/reports/stop" component={StopReportPage} />
- <Route exact path="/reports/summary" component={SummaryReportPage} />
- <Route exact path="/reports/trip" component={TripReportPage} />
- </Switch>
- </div>
- {!desktop && (
- <div className={classes.menu}>
- <BottomMenu />
- </div>
- )}
- </>
- )}
- </Route>
- </Switch>
- <ErrorHandler />
- </ThemeProvider>
- ));
+ if (!initialized) {
+ return (<LinearProgress />);
+ }
+ return (
+ <>
+ <div className={classes.page}>
+ <Outlet />
+ </div>
+ {!desktop && (
+ <div className={classes.menu}>
+ <BottomMenu />
+ </div>
+ )}
+ </>
+ );
};
export default App;
diff --git a/modern/src/Navigation.js b/modern/src/Navigation.js
new file mode 100644
index 00000000..b4156713
--- /dev/null
+++ b/modern/src/Navigation.js
@@ -0,0 +1,145 @@
+import React, { useState } from 'react';
+import { Route, Routes, useNavigate } from 'react-router-dom';
+import { useDispatch } from 'react-redux';
+import { LinearProgress } from '@mui/material';
+import MainPage from './main/MainPage';
+import RouteReportPage from './reports/RouteReportPage';
+import ServerPage from './settings/ServerPage';
+import UsersPage from './settings/UsersPage';
+import DevicePage from './settings/DevicePage';
+import UserPage from './settings/UserPage';
+import NotificationsPage from './settings/NotificationsPage';
+import NotificationPage from './settings/NotificationPage';
+import GroupsPage from './settings/GroupsPage';
+import GroupPage from './settings/GroupPage';
+import PositionPage from './other/PositionPage';
+import EventReportPage from './reports/EventReportPage';
+import ReplayPage from './other/ReplayPage';
+import TripReportPage from './reports/TripReportPage';
+import StopReportPage from './reports/StopReportPage';
+import SummaryReportPage from './reports/SummaryReportPage';
+import ChartReportPage from './reports/ChartReportPage';
+import DriversPage from './settings/DriversPage';
+import DriverPage from './settings/DriverPage';
+import CalendarsPage from './settings/CalendarsPage';
+import CalendarPage from './settings/CalendarPage';
+import ComputedAttributesPage from './settings/ComputedAttributesPage';
+import ComputedAttributePage from './settings/ComputedAttributePage';
+import MaintenancesPage from './settings/MaintenancesPage';
+import MaintenancePage from './settings/MaintenancePage';
+import CommandsPage from './settings/CommandsPage';
+import CommandPage from './settings/CommandPage';
+import StatisticsPage from './reports/StatisticsPage';
+import LoginPage from './login/LoginPage';
+import RegisterPage from './login/RegisterPage';
+import ResetPasswordPage from './login/ResetPasswordPage';
+import GeofencesPage from './other/GeofencesPage';
+import GeofencePage from './settings/GeofencePage';
+import useQuery from './common/util/useQuery';
+import { useEffectAsync } from './reactHelper';
+import { devicesActions } from './store';
+import EventPage from './other/EventPage';
+import PreferencesPage from './settings/PreferencesPage';
+import AccumulatorsPage from './settings/AccumulatorsPage';
+import CommandSendPage from './settings/CommandSendPage';
+import App from './App';
+
+const Navigation = () => {
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ const [redirectsHandled, setRedirectsHandled] = useState(false);
+
+ const query = useQuery();
+
+ useEffectAsync(async () => {
+ if (query.get('token')) {
+ const token = query.get('token');
+ await fetch(`/api/session?token=${encodeURIComponent(token)}`);
+ navigate('/');
+ } else if (query.get('deviceId')) {
+ const deviceId = query.get('deviceId');
+ const response = await fetch(`/api/devices?uniqueId=${deviceId}`);
+ if (response.ok) {
+ const items = await response.json();
+ if (items.length > 0) {
+ dispatch(devicesActions.select(items[0].id));
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ navigate('/');
+ } else if (query.get('eventId')) {
+ const eventId = parseInt(query.get('eventId'), 10);
+ navigate(`/event/${eventId}`);
+ } else {
+ setRedirectsHandled(true);
+ }
+ }, [query]);
+
+ if (!redirectsHandled) {
+ return (<LinearProgress />);
+ }
+ return (
+ <Routes>
+ <Route path="/login" element={<LoginPage />} />
+ <Route path="/register" element={<RegisterPage />} />
+ <Route path="/reset-password" element={<ResetPasswordPage />} />
+ <Route path="/" element={<App />}>
+ <Route index element={<MainPage />} />
+
+ <Route path="position/:id" element={<PositionPage />} />
+ <Route path="event/:id" element={<EventPage />} />
+ <Route path="replay" element={<ReplayPage />} />
+ <Route path="geofences" element={<GeofencesPage />} />
+
+ <Route path="settings">
+ <Route path="accumulators/:deviceId" element={<AccumulatorsPage />} />
+ <Route path="calendars" element={<CalendarsPage />} />
+ <Route path="calendar/:id" element={<CalendarPage />} />
+ <Route path="calendar" element={<CalendarPage />} />
+ <Route path="commands" element={<CommandsPage />} />
+ <Route path="command/:id" element={<CommandPage />} />
+ <Route path="command" element={<CommandPage />} />
+ <Route path="command-send/:deviceId" element={<CommandSendPage />} />
+ <Route path="attributes" element={<ComputedAttributesPage />} />
+ <Route path="attribute/:id" element={<ComputedAttributePage />} />
+ <Route path="attribute" element={<ComputedAttributePage />} />
+ <Route path="device/:id" element={<DevicePage />} />
+ <Route path="device" element={<DevicePage />} />
+ <Route path="drivers" element={<DriversPage />} />
+ <Route path="driver/:id" element={<DriverPage />} />
+ <Route path="driver" element={<DriverPage />} />
+ <Route path="geofence/:id" element={<GeofencePage />} />
+ <Route path="geofence" element={<GeofencePage />} />
+ <Route path="groups" element={<GroupsPage />} />
+ <Route path="group/:id" element={<GroupPage />} />
+ <Route path="group" element={<GroupPage />} />
+ <Route path="maintenances" element={<MaintenancesPage />} />
+ <Route path="maintenance/:id" element={<MaintenancePage />} />
+ <Route path="maintenance" element={<MaintenancePage />} />
+ <Route path="notifications" element={<NotificationsPage />} />
+ <Route path="notification/:id" element={<NotificationPage />} />
+ <Route path="notification" element={<NotificationPage />} />
+ <Route path="preferences" element={<PreferencesPage />} />
+ <Route path="server" element={<ServerPage />} />
+ <Route path="users" element={<UsersPage />} />
+ <Route path="user/:id" element={<UserPage />} />
+ <Route path="user" element={<UserPage />} />
+ </Route>
+
+ <Route path="reports">
+ <Route path="chart" element={<ChartReportPage />} />
+ <Route path="event" element={<EventReportPage />} />
+ <Route path="route" element={<RouteReportPage />} />
+ <Route path="statistics" element={<StatisticsPage />} />
+ <Route path="stop" element={<StopReportPage />} />
+ <Route path="summary" element={<SummaryReportPage />} />
+ <Route path="trip" element={<TripReportPage />} />
+ </Route>
+ </Route>
+ </Routes>
+ );
+};
+
+export default Navigation;
diff --git a/modern/src/SocketController.js b/modern/src/SocketController.js
index 9826d4b1..57ffa0d5 100644
--- a/modern/src/SocketController.js
+++ b/modern/src/SocketController.js
@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector, connect } from 'react-redux';
-import { Snackbar } from '@material-ui/core';
-import { useHistory } from 'react-router-dom';
+import { Snackbar } from '@mui/material';
+import { useNavigate } from 'react-router-dom';
import { positionsActions, devicesActions, sessionActions } from './store';
import { useEffectAsync } from './reactHelper';
@@ -11,7 +11,7 @@ import { snackBarDurationLongMs } from './common/util/duration';
const SocketController = () => {
const dispatch = useDispatch();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
const authenticated = useSelector((state) => !!state.session.user);
@@ -74,7 +74,7 @@ const SocketController = () => {
if (response.ok) {
dispatch(sessionActions.updateUser(await response.json()));
} else {
- history.push('/login');
+ navigate('/login');
}
return null;
}, [authenticated]);
diff --git a/modern/src/common/components/BottomMenu.js b/modern/src/common/components/BottomMenu.js
index 30960396..b011638e 100644
--- a/modern/src/common/components/BottomMenu.js
+++ b/modern/src/common/components/BottomMenu.js
@@ -1,22 +1,22 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { useHistory, useLocation } from 'react-router-dom';
+import { useNavigate, useLocation } from 'react-router-dom';
import {
Paper, BottomNavigation, BottomNavigationAction, Menu, MenuItem, Typography,
-} from '@material-ui/core';
+} from '@mui/material';
-import DescriptionIcon from '@material-ui/icons/Description';
-import SettingsIcon from '@material-ui/icons/Settings';
-import MapIcon from '@material-ui/icons/Map';
-import PersonIcon from '@material-ui/icons/Person';
-import ExitToAppIcon from '@material-ui/icons/ExitToApp';
+import DescriptionIcon from '@mui/icons-material/Description';
+import SettingsIcon from '@mui/icons-material/Settings';
+import MapIcon from '@mui/icons-material/Map';
+import PersonIcon from '@mui/icons-material/Person';
+import ExitToAppIcon from '@mui/icons-material/ExitToApp';
import { sessionActions } from '../../store';
import { useTranslation } from './LocalizationProvider';
import { useReadonly } from '../util/permissions';
const BottomMenu = () => {
- const history = useHistory();
+ const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
const t = useTranslation();
@@ -41,26 +41,26 @@ const BottomMenu = () => {
const handleAccount = () => {
setAnchorEl(null);
- history.push(`/settings/user/${userId}`);
+ navigate(`/settings/user/${userId}`);
};
const handleLogout = async () => {
setAnchorEl(null);
await fetch('/api/session', { method: 'DELETE' });
- history.push('/login');
+ navigate('/login');
dispatch(sessionActions.updateUser(null));
};
const handleSelection = (event, value) => {
switch (value) {
case 0:
- history.push('/');
+ navigate('/');
break;
case 1:
- history.push('/reports/route');
+ navigate('/reports/route');
break;
case 2:
- history.push('/settings/preferences');
+ navigate('/settings/preferences');
break;
case 3:
if (readonly) {
diff --git a/modern/src/common/components/ErrorHandler.js b/modern/src/common/components/ErrorHandler.js
index 5a238956..757a78ef 100644
--- a/modern/src/common/components/ErrorHandler.js
+++ b/modern/src/common/components/ErrorHandler.js
@@ -1,5 +1,4 @@
-import { Snackbar } from '@material-ui/core';
-import { Alert } from '@material-ui/lab';
+import { Snackbar, Alert } from '@mui/material';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { usePrevious } from '../../reactHelper';
@@ -15,7 +14,6 @@ const ErrorHandler = () => {
<Snackbar open={!!error}>
<Alert
elevation={6}
- variant="filled"
onClose={() => dispatch(errorsActions.pop())}
severity="error"
>
diff --git a/modern/src/common/components/LinkField.js b/modern/src/common/components/LinkField.js
index 0f6cc7ba..f87a8963 100644
--- a/modern/src/common/components/LinkField.js
+++ b/modern/src/common/components/LinkField.js
@@ -1,12 +1,11 @@
import {
FormControl, InputLabel, MenuItem, Select,
-} from '@material-ui/core';
+} from '@mui/material';
import React, { useState } from 'react';
import { useEffectAsync } from '../../reactHelper';
const LinkField = ({
margin,
- variant,
label,
endpointAll,
endpointLinked,
@@ -69,9 +68,10 @@ const LinkField = ({
if (items && linked) {
return (
- <FormControl margin={margin} variant={variant}>
+ <FormControl margin={margin} fullWidth>
<InputLabel>{label}</InputLabel>
<Select
+ label={label}
multiple
value={linked}
onChange={onChange}
diff --git a/modern/src/common/components/LocalizationProvider.js b/modern/src/common/components/LocalizationProvider.js
index 0df242dc..147ac5ca 100644
--- a/modern/src/common/components/LocalizationProvider.js
+++ b/modern/src/common/components/LocalizationProvider.js
@@ -1,5 +1,5 @@
+/* eslint-disable import/no-relative-packages */
import React, { createContext, useContext, useMemo } from 'react';
-import usePersistedState from '../util/usePersistedState';
import af from '../../../../web/l10n/af.json';
import ar from '../../../../web/l10n/ar.json';
@@ -55,6 +55,7 @@ import uz from '../../../../web/l10n/uz.json';
import vi from '../../../../web/l10n/vi.json';
import zh from '../../../../web/l10n/zh.json';
import zhTW from '../../../../web/l10n/zh_TW.json';
+import usePersistedState from '../util/usePersistedState';
const languages = {
af: { data: af, name: 'Afrikaans' },
@@ -143,8 +144,10 @@ const LocalizationContext = createContext({
export const LocalizationProvider = ({ children }) => {
const [language, setLanguage] = usePersistedState('language', getDefaultLanguage());
+ const value = useMemo(() => ({ languages, language, setLanguage }), [languages, language, setLanguage]);
+
return (
- <LocalizationContext.Provider value={{ languages, language, setLanguage }}>
+ <LocalizationContext.Provider value={value}>
{children}
</LocalizationContext.Provider>
);
diff --git a/modern/src/common/components/NavBar.js b/modern/src/common/components/NavBar.js
index ac689e76..a1a73fdf 100644
--- a/modern/src/common/components/NavBar.js
+++ b/modern/src/common/components/NavBar.js
@@ -1,13 +1,17 @@
import React from 'react';
import {
AppBar, Toolbar, Typography, IconButton,
-} from '@material-ui/core';
-import MenuIcon from '@material-ui/icons/Menu';
+} from '@mui/material';
+import MenuIcon from '@mui/icons-material/Menu';
const Navbar = ({ setOpenDrawer, title }) => (
<AppBar position="sticky" color="inherit">
<Toolbar>
- <IconButton color="inherit" edge="start" onClick={() => setOpenDrawer(true)}>
+ <IconButton
+ color="inherit"
+ edge="start"
+ onClick={() => setOpenDrawer(true)}
+ >
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
diff --git a/modern/src/common/components/PageLayout.js b/modern/src/common/components/PageLayout.js
index a1f117c4..17567fb1 100644
--- a/modern/src/common/components/PageLayout.js
+++ b/modern/src/common/components/PageLayout.js
@@ -1,10 +1,20 @@
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';
+ AppBar,
+ Breadcrumbs,
+ Divider,
+ Drawer,
+ Hidden,
+ IconButton,
+ Toolbar,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import MenuIcon from '@mui/icons-material/Menu';
+import { useNavigate } from 'react-router-dom';
import { useTranslation } from './LocalizationProvider';
const useStyles = makeStyles((theme) => ({
@@ -56,13 +66,13 @@ const PageTitle = ({ breadcrumbs }) => {
const PageLayout = ({ menu, breadcrumbs, children }) => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const [openDrawer, setOpenDrawer] = useState(false);
return (
<>
- <Hidden smDown>
+ <Hidden mdDown>
<div className={classes.desktopRoot}>
<Drawer
variant="permanent"
@@ -71,7 +81,7 @@ const PageLayout = ({ menu, breadcrumbs, children }) => {
>
<div className={classes.toolbar}>
<Toolbar>
- <IconButton color="inherit" edge="start" onClick={() => history.push('/')}>
+ <IconButton color="inherit" edge="start" onClick={() => navigate('/')}>
<ArrowBackIcon />
</IconButton>
<PageTitle breadcrumbs={breadcrumbs} />
diff --git a/modern/src/common/components/PositionValue.js b/modern/src/common/components/PositionValue.js
index 1162a150..3dd0eb6f 100644
--- a/modern/src/common/components/PositionValue.js
+++ b/modern/src/common/components/PositionValue.js
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
-import { Link } from '@material-ui/core';
+import { Link } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';
import {
formatAlarm, formatBoolean, formatCoordinate, formatCourse, formatDistance, formatNumber, formatPercentage, formatSpeed, formatTime,
diff --git a/modern/src/common/components/RemoveDialog.js b/modern/src/common/components/RemoveDialog.js
index a11af4c2..1af4e6a8 100644
--- a/modern/src/common/components/RemoveDialog.js
+++ b/modern/src/common/components/RemoveDialog.js
@@ -1,6 +1,7 @@
import React from 'react';
-import Button from '@material-ui/core/Button';
-import { Snackbar, makeStyles } from '@material-ui/core';
+import Button from '@mui/material/Button';
+import { Snackbar } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useTranslation } from './LocalizationProvider';
import { useCatch } from '../../reactHelper';
import { snackBarDurationLongMs } from '../util/duration';
diff --git a/modern/src/common/components/SelectField.js b/modern/src/common/components/SelectField.js
index 5a9a176b..d3de5c46 100644
--- a/modern/src/common/components/SelectField.js
+++ b/modern/src/common/components/SelectField.js
@@ -1,12 +1,11 @@
import {
FormControl, InputLabel, MenuItem, Select,
-} from '@material-ui/core';
+} from '@mui/material';
import React, { useState } from 'react';
import { useEffectAsync } from '../../reactHelper';
const SelectField = ({
margin,
- variant,
label,
multiple,
value,
@@ -33,9 +32,10 @@ const SelectField = ({
if (items) {
return (
- <FormControl margin={margin} variant={variant}>
+ <FormControl margin={margin} fullWidth>
<InputLabel>{label}</InputLabel>
<Select
+ label={label}
multiple={multiple}
value={value}
onChange={onChange}
diff --git a/modern/src/common/components/SideNav.js b/modern/src/common/components/SideNav.js
index ad7c212a..648059d1 100644
--- a/modern/src/common/components/SideNav.js
+++ b/modern/src/common/components/SideNav.js
@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import {
List, ListItem, ListItemText, ListItemIcon, Divider, ListSubheader,
-} from '@material-ui/core';
+} from '@mui/material';
import { Link, useLocation } from 'react-router-dom';
const SideNav = ({ routes }) => {
diff --git a/modern/src/common/theme/components.js b/modern/src/common/theme/components.js
new file mode 100644
index 00000000..f5f3acaa
--- /dev/null
+++ b/modern/src/common/theme/components.js
@@ -0,0 +1,15 @@
+export default {
+ MuiFormControl: {
+ defaultProps: {
+ size: 'small',
+ },
+ },
+ MuiSnackbar: {
+ defaultProps: {
+ anchorOrigin: {
+ vertical: 'bottom',
+ horizontal: 'center',
+ },
+ },
+ },
+};
diff --git a/modern/src/common/theme/index.js b/modern/src/common/theme/index.js
index 02865c23..0cc999ad 100644
--- a/modern/src/common/theme/index.js
+++ b/modern/src/common/theme/index.js
@@ -1,12 +1,12 @@
-import { createTheme } from '@material-ui/core/styles';
+import { createTheme } from '@mui/material/styles';
import palette from './palette';
-import overrides from './overrides';
import dimensions from './dimensions';
+import components from './components';
const theme = createTheme({
palette,
- overrides,
dimensions,
+ components,
});
export default theme;
diff --git a/modern/src/common/theme/overrides.js b/modern/src/common/theme/overrides.js
deleted file mode 100644
index d1fe844c..00000000
--- a/modern/src/common/theme/overrides.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import dimensions from './dimensions';
-
-export default {
- MuiFormControl: {
- root: {
- marginTop: 5,
- marginBottom: 5,
- },
- },
- MuiInputLabel: {
- filled: {
- transform: 'translate(12px, 14px) scale(1)',
- '&$shrink': {
- transform: 'translate(12px, -14px) scale(0.72)',
- },
- },
- },
- MuiFilledInput: {
- root: {
- height: dimensions.inputHeight,
- borderRadius: dimensions.borderRadius,
- backgroundColor: 'rgba(0, 0, 0, 0.035)',
- },
- input: {
- height: dimensions.inputHeight,
- borderRadius: dimensions.borderRadius,
- paddingTop: '11.5px',
- paddingBottom: '11.5px',
- boxSizing: 'border-box',
- '&:-webkit-autofill': {
- WebkitBoxShadow: '0 0 0 100px #eeeeee inset',
- },
- },
- underline: {
- '&:before': {
- borderBottom: 'none',
- },
- '&:after': {
- borderBottom: 'none',
- },
- '&:hover:before': {
- borderBottom: 'none',
- },
- },
- },
- MuiSelect: {
- select: {
- borderRadius: dimensions.borderRadius,
- '&&:focus': {
- borderRadius: dimensions.borderRadius,
- },
- },
- },
- MuiButton: {
- root: {
- height: dimensions.inputHeight,
- marginTop: 5,
- marginBottom: 5,
- '&$disabled': {
- opacity: 0.4,
- color: undefined,
- },
- },
- contained: {
- '&$disabled': {
- opacity: 0.4,
- color: undefined,
- backgroundColor: undefined,
- },
- },
- },
- MuiFormHelperText: {
- root: {
- marginBottom: -10,
- },
- contained: {
- marginLeft: 12,
- },
- },
- MuiAutocomplete: {
- inputRoot: {
- '&.MuiFilledInput-root': {
- paddingTop: 0,
- },
- },
- },
-};
diff --git a/modern/src/index.js b/modern/src/index.js
index cb6710d5..547e39ef 100644
--- a/modern/src/index.js
+++ b/modern/src/index.js
@@ -3,22 +3,36 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
-
-import App from './App';
+import { CssBaseline, ThemeProvider, StyledEngineProvider } from '@mui/material';
import * as serviceWorker from './serviceWorker';
import store from './store';
import { LocalizationProvider } from './common/components/LocalizationProvider';
+import ErrorHandler from './common/components/ErrorHandler';
+import CachingController from './CachingController';
+import SocketController from './SocketController';
+import theme from './common/theme';
+import Navigation from './Navigation';
-const base = window.location.href.indexOf('modern') >= 0 ? '/modern' : null;
+const base = window.location.href.indexOf('modern') >= 0 ? '/modern' : '/';
-ReactDOM.render((
- <Provider store={store}>
- <LocalizationProvider>
- <BrowserRouter basename={base}>
- <App />
- </BrowserRouter>
- </LocalizationProvider>
- </Provider>
-), document.getElementById('root'));
+ReactDOM.render(
+ (
+ <Provider store={store}>
+ <LocalizationProvider>
+ <StyledEngineProvider injectFirst>
+ <ThemeProvider theme={theme}>
+ <CssBaseline />
+ <BrowserRouter basename={base}>
+ <SocketController />
+ <CachingController />
+ <Navigation />
+ </BrowserRouter>
+ <ErrorHandler />
+ </ThemeProvider>
+ </StyledEngineProvider>
+ </LocalizationProvider>
+ </Provider>
+ ), document.getElementById('root'),
+);
serviceWorker.register();
diff --git a/modern/src/login/LoginLayout.js b/modern/src/login/LoginLayout.js
index 4a2bf43a..cfdd837c 100644
--- a/modern/src/login/LoginLayout.js
+++ b/modern/src/login/LoginLayout.js
@@ -1,6 +1,7 @@
import React from 'react';
-import { useMediaQuery, makeStyles, Paper } from '@material-ui/core';
-import { useTheme } from '@material-ui/core/styles';
+import { useMediaQuery, Paper } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTheme } from '@mui/material/styles';
const useStyles = makeStyles((theme) => ({
root: {
@@ -15,10 +16,10 @@ const useStyles = makeStyles((theme) => ({
background: theme.palette.primary.main,
paddingBottom: theme.spacing(5),
width: theme.dimensions.sidebarWidth,
- [theme.breakpoints.down('md')]: {
+ [theme.breakpoints.down('lg')]: {
width: theme.dimensions.sidebarWidthTablet,
},
- [theme.breakpoints.down('xs')]: {
+ [theme.breakpoints.down('sm')]: {
width: '0px',
},
},
@@ -54,12 +55,11 @@ const LoginLayout = ({ children }) => {
<>
<main className={classes.root}>
<div className={classes.sidebar}>
- {!useMediaQuery(theme.breakpoints.down('md'))
- && (
+ {!useMediaQuery(theme.breakpoints.down('lg')) && (
<svg height="64" width="240">
<use xlinkHref="/logo.svg#img" />
</svg>
- )}
+ )}
</div>
<Paper className={classes.paper}>
<form className={classes.form}>
diff --git a/modern/src/login/LoginPage.js b/modern/src/login/LoginPage.js
index 5b690cdc..cc010427 100644
--- a/modern/src/login/LoginPage.js
+++ b/modern/src/login/LoginPage.js
@@ -1,12 +1,24 @@
import React, { useState } from 'react';
import {
- Grid, useMediaQuery, makeStyles, InputLabel, Select, MenuItem, FormControl, Button, TextField, Link, Snackbar, IconButton, Tooltip,
-} from '@material-ui/core';
-import CloseIcon from '@material-ui/icons/Close';
-import CachedIcon from '@material-ui/icons/Cached';
-import { useTheme } from '@material-ui/core/styles';
+ Grid,
+ useMediaQuery,
+ InputLabel,
+ Select,
+ MenuItem,
+ FormControl,
+ Button,
+ TextField,
+ Link,
+ Snackbar,
+ IconButton,
+ Tooltip,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import CloseIcon from '@mui/icons-material/Close';
+import CachedIcon from '@mui/icons-material/Cached';
+import { useTheme } from '@mui/material/styles';
import { useDispatch, useSelector } from 'react-redux';
-import { useHistory } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import { sessionActions } from '../store';
import { useLocalization, useTranslation } from '../common/components/LocalizationProvider';
import LoginLayout from './LoginLayout';
@@ -30,7 +42,7 @@ const useStyles = makeStyles((theme) => ({
const LoginPage = () => {
const classes = useStyles();
const dispatch = useDispatch();
- const history = useHistory();
+ const navigate = useNavigate();
const theme = useTheme();
const t = useTranslation();
@@ -58,7 +70,7 @@ const LoginPage = () => {
if (response.ok) {
const user = await response.json();
dispatch(sessionActions.updateUser(user));
- history.push('/');
+ navigate('/');
} else {
throw Error(await response.text());
}
@@ -82,7 +94,7 @@ const LoginPage = () => {
</IconButton>
</Tooltip>
<Grid container direction="column" spacing={2}>
- {useMediaQuery(theme.breakpoints.down('md'))
+ {useMediaQuery(theme.breakpoints.down('lg'))
&& (
<Grid item className={classes.logoContainer}>
<svg height="64" width="240">
@@ -103,7 +115,6 @@ const LoginPage = () => {
onChange={(e) => setEmail(e.target.value)}
onKeyUp={handleSpecialKey}
helperText={failed && 'Invalid username or password'}
- variant="filled"
/>
</Grid>
<Grid item>
@@ -119,7 +130,6 @@ const LoginPage = () => {
autoFocus={!!email}
onChange={(e) => setPassword(e.target.value)}
onKeyUp={handleSpecialKey}
- variant="filled"
/>
</Grid>
<Grid item>
@@ -136,14 +146,14 @@ const LoginPage = () => {
</Grid>
<Grid item container spacing={2}>
<Grid item>
- <Button onClick={() => history.push('/register')} disabled={!registrationEnabled} color="secondary">
+ <Button onClick={() => navigate('/register')} disabled={!registrationEnabled} color="secondary">
{t('loginRegister')}
</Button>
</Grid>
<Grid item xs>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('loginLanguage')}</InputLabel>
- <Select value={language} onChange={(e) => setLanguage(e.target.value)}>
+ <Select label={t('loginLanguage')} value={language} onChange={(e) => setLanguage(e.target.value)}>
{languageList.map((it) => <MenuItem key={it.code} value={it.code}>{it.name}</MenuItem>)}
</Select>
</FormControl>
@@ -152,7 +162,7 @@ const LoginPage = () => {
{emailEnabled && (
<Grid item container justifyContent="flex-end">
<Grid item>
- <Link onClick={() => history.push('/reset-password')} className={classes.resetPassword} underline="none">{t('loginReset')}</Link>
+ <Link onClick={() => navigate('/reset-password')} className={classes.resetPassword} underline="none">{t('loginReset')}</Link>
</Grid>
</Grid>
)}
diff --git a/modern/src/login/RegisterPage.js b/modern/src/login/RegisterPage.js
index 78728b58..986bf894 100644
--- a/modern/src/login/RegisterPage.js
+++ b/modern/src/login/RegisterPage.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import {
- Grid, Button, TextField, Typography, Link, makeStyles, Snackbar,
-} from '@material-ui/core';
-import { useHistory } from 'react-router-dom';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+ Grid, Button, TextField, Typography, Link, Snackbar,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useNavigate } from 'react-router-dom';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import LoginLayout from './LoginLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
import { snackBarDurationShortMs } from '../common/util/duration';
@@ -26,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
const RegisterPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
const [name, setName] = useState('');
@@ -51,7 +52,7 @@ const RegisterPage = () => {
<LoginLayout>
<Snackbar
open={snackbarOpen}
- onClose={() => history.push('/login')}
+ onClose={() => navigate('/login')}
autoHideDuration={snackBarDurationShortMs}
message={t('loginCreated')}
/>
@@ -59,7 +60,7 @@ const RegisterPage = () => {
<Grid container item>
<Grid item>
<Typography className={classes.link} color="primary">
- <Link onClick={() => history.push('/login')}>
+ <Link onClick={() => navigate('/login')}>
<ArrowBackIcon />
</Link>
</Typography>
@@ -80,7 +81,6 @@ const RegisterPage = () => {
autoComplete="name"
autoFocus
onChange={(event) => setName(event.target.value)}
- variant="filled"
/>
</Grid>
<Grid item>
@@ -93,7 +93,6 @@ const RegisterPage = () => {
value={email}
autoComplete="email"
onChange={(event) => setEmail(event.target.value)}
- variant="filled"
/>
</Grid>
<Grid item>
@@ -106,7 +105,6 @@ const RegisterPage = () => {
type="password"
autoComplete="current-password"
onChange={(event) => setPassword(event.target.value)}
- variant="filled"
/>
</Grid>
<Grid item>
diff --git a/modern/src/login/ResetPasswordPage.js b/modern/src/login/ResetPasswordPage.js
index 93e154e3..c3e91fe6 100644
--- a/modern/src/login/ResetPasswordPage.js
+++ b/modern/src/login/ResetPasswordPage.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import {
- Grid, Button, TextField, Typography, Link, makeStyles, Snackbar,
-} from '@material-ui/core';
-import { useHistory } from 'react-router-dom';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+ Grid, Button, TextField, Typography, Link, Snackbar,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useNavigate } from 'react-router-dom';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import LoginLayout from './LoginLayout';
import { useTranslation } from '../common/components/LocalizationProvider';
import useQuery from '../common/util/useQuery';
@@ -27,7 +28,7 @@ const useStyles = makeStyles((theme) => ({
const ResetPasswordPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
const query = useQuery();
@@ -62,7 +63,7 @@ const ResetPasswordPage = () => {
<LoginLayout>
<Snackbar
open={snackbarOpen}
- onClose={() => history.push('/login')}
+ onClose={() => navigate('/login')}
autoHideDuration={snackBarDurationShortMs}
message={!token ? t('loginResetSuccess') : t('loginUpdateSuccess')}
/>
@@ -70,7 +71,7 @@ const ResetPasswordPage = () => {
<Grid container item>
<Grid item>
<Typography className={classes.link} color="primary">
- <Link onClick={() => history.push('/login')}>
+ <Link onClick={() => navigate('/login')}>
<ArrowBackIcon />
</Link>
</Typography>
@@ -93,7 +94,6 @@ const ResetPasswordPage = () => {
value={email}
autoComplete="email"
onChange={(event) => setEmail(event.target.value)}
- variant="filled"
/>
</Grid>
)
@@ -108,7 +108,6 @@ const ResetPasswordPage = () => {
type="password"
autoComplete="current-password"
onChange={(event) => setPassword(event.target.value)}
- variant="filled"
/>
</Grid>
)}
diff --git a/modern/src/main/DevicesList.js b/modern/src/main/DevicesList.js
index 6ff08c6a..06b75715 100644
--- a/modern/src/main/DevicesList.js
+++ b/modern/src/main/DevicesList.js
@@ -1,27 +1,26 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { makeStyles } from '@material-ui/core/styles';
-import { IconButton, Tooltip } from '@material-ui/core';
-import Avatar from '@material-ui/core/Avatar';
-import List from '@material-ui/core/List';
-import ListItem from '@material-ui/core/ListItem';
-import ListItemAvatar from '@material-ui/core/ListItemAvatar';
-import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
-import ListItemText from '@material-ui/core/ListItemText';
+import makeStyles from '@mui/styles/makeStyles';
+import { IconButton, Tooltip } from '@mui/material';
+import Avatar from '@mui/material/Avatar';
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemAvatar from '@mui/material/ListItemAvatar';
+import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
+import ListItemText from '@mui/material/ListItemText';
import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
-import BatteryFullIcon from '@material-ui/icons/BatteryFull';
-import BatteryChargingFullIcon from '@material-ui/icons/BatteryChargingFull';
-import Battery60Icon from '@material-ui/icons/Battery60';
-import BatteryCharging60Icon from '@material-ui/icons/BatteryCharging60';
-import Battery20Icon from '@material-ui/icons/Battery20';
-import BatteryCharging20Icon from '@material-ui/icons/BatteryCharging20';
-import FlashOnIcon from '@material-ui/icons/FlashOn';
-import FlashOffIcon from '@material-ui/icons/FlashOff';
-import ErrorIcon from '@material-ui/icons/Error';
+import BatteryFullIcon from '@mui/icons-material/BatteryFull';
+import BatteryChargingFullIcon from '@mui/icons-material/BatteryChargingFull';
+import Battery60Icon from '@mui/icons-material/Battery60';
+import BatteryCharging60Icon from '@mui/icons-material/BatteryCharging60';
+import Battery20Icon from '@mui/icons-material/Battery20';
+import BatteryCharging20Icon from '@mui/icons-material/BatteryCharging20';
+import FlashOnIcon from '@mui/icons-material/FlashOn';
+import FlashOffIcon from '@mui/icons-material/FlashOff';
+import ErrorIcon from '@mui/icons-material/Error';
import { devicesActions } from '../store';
-import EditCollectionView from '../settings/components/EditCollectionView';
import { useEffectAsync } from '../reactHelper';
import {
formatAlarm, formatBoolean, formatPercentage, formatStatus, getStatusColor,
@@ -139,7 +138,7 @@ const DeviceRow = ({ data, index, style }) => {
);
};
-const DeviceView = ({ updateTimestamp, onMenuClick, filter }) => {
+const DevicesList = ({ filter }) => {
const classes = useStyles();
const dispatch = useDispatch();
const listInnerEl = useRef(null);
@@ -167,7 +166,7 @@ const DeviceView = ({ updateTimestamp, onMenuClick, filter }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, []);
return (
<AutoSizer className={classes.list}>
@@ -177,7 +176,7 @@ const DeviceView = ({ updateTimestamp, onMenuClick, filter }) => {
width={width}
height={height}
itemCount={filteredItems.length}
- itemData={{ items: filteredItems, onMenuClick }}
+ itemData={{ items: filteredItems }}
itemSize={72}
overscanCount={10}
innerRef={listInnerEl}
@@ -190,8 +189,4 @@ const DeviceView = ({ updateTimestamp, onMenuClick, filter }) => {
);
};
-const DevicesList = ({ filter }) => (
- <EditCollectionView content={DeviceView} editPath="/settings/device" endpoint="devices" disableAdd filter={filter} />
-);
-
export default DevicesList;
diff --git a/modern/src/main/MainPage.js b/modern/src/main/MainPage.js
index 61c10d81..059d7043 100644
--- a/modern/src/main/MainPage.js
+++ b/modern/src/main/MainPage.js
@@ -1,15 +1,17 @@
import React, { useState, useEffect } from 'react';
-import { useHistory } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import {
- makeStyles, Paper, Toolbar, TextField, IconButton, Button,
-} from '@material-ui/core';
+ Paper, Toolbar, TextField, IconButton, Button,
+} from '@mui/material';
-import { useTheme } from '@material-ui/core/styles';
-import useMediaQuery from '@material-ui/core/useMediaQuery';
-import AddIcon from '@material-ui/icons/Add';
-import CloseIcon from '@material-ui/icons/Close';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import ListIcon from '@material-ui/icons/ViewList';
+import makeStyles from '@mui/styles/makeStyles';
+
+import { useTheme } from '@mui/material/styles';
+import useMediaQuery from '@mui/material/useMediaQuery';
+import AddIcon from '@mui/icons-material/Add';
+import CloseIcon from '@mui/icons-material/Close';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import ListIcon from '@mui/icons-material/ViewList';
import { useDispatch, useSelector } from 'react-redux';
import DevicesList from './DevicesList';
@@ -46,7 +48,7 @@ const useStyles = makeStyles((theme) => ({
bottom: theme.dimensions.bottomBarHeight,
transition: 'transform .5s ease',
backgroundColor: 'white',
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
width: '100%',
margin: 0,
},
@@ -54,7 +56,7 @@ const useStyles = makeStyles((theme) => ({
sidebarCollapsed: {
transform: `translateX(-${theme.dimensions.drawerWidthDesktop})`,
marginLeft: 0,
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
transform: 'translateX(-100vw)',
},
},
@@ -75,7 +77,7 @@ const useStyles = makeStyles((theme) => ({
left: `calc(50% + ${theme.dimensions.drawerWidthDesktop} / 2)`,
bottom: theme.spacing(3),
},
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
left: '50%',
bottom: theme.spacing(3) + theme.dimensions.bottomBarHeight,
},
@@ -87,7 +89,7 @@ const useStyles = makeStyles((theme) => ({
top: theme.spacing(3),
borderRadius: '0px',
minWidth: 0,
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
left: 0,
},
},
@@ -115,14 +117,14 @@ const useStyles = makeStyles((theme) => ({
const MainPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const dispatch = useDispatch();
const theme = useTheme();
const t = useTranslation();
const deviceReadonly = useDeviceReadonly();
const desktop = useMediaQuery(theme.breakpoints.up('md'));
- const phone = useMediaQuery(theme.breakpoints.down('xs'));
+ const phone = useMediaQuery(theme.breakpoints.down('sm'));
const [mapLiveRoutes] = usePersistedState('mapLiveRoutes', false);
@@ -176,9 +178,8 @@ const MainPage = () => {
autoComplete="searchKeyword"
onChange={(event) => setSearchKeyword(event.target.value)}
placeholder={t('sharedSearchDevices')}
- variant="filled"
/>
- <IconButton onClick={() => history.push('/settings/device')} disabled={deviceReadonly}>
+ <IconButton onClick={() => navigate('/settings/device')} disabled={deviceReadonly}>
<AddIcon />
</IconButton>
{desktop && (
diff --git a/modern/src/main/StatusCard.js b/modern/src/main/StatusCard.js
index b470890e..ec33efa2 100644
--- a/modern/src/main/StatusCard.js
+++ b/modern/src/main/StatusCard.js
@@ -1,15 +1,27 @@
import React, { useState } from 'react';
-import { useSelector } from 'react-redux';
-import { useHistory } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
import {
- makeStyles, Card, CardContent, Typography, CardActions, CardHeader, IconButton, Avatar, Table, TableBody, TableRow, TableCell, TableContainer,
-} from '@material-ui/core';
-import CloseIcon from '@material-ui/icons/Close';
-import PostAddIcon from '@material-ui/icons/PostAdd';
-import ReplayIcon from '@material-ui/icons/Replay';
-import PublishIcon from '@material-ui/icons/Publish';
-import EditIcon from '@material-ui/icons/Edit';
-import DeleteIcon from '@material-ui/icons/Delete';
+ Card,
+ CardContent,
+ Typography,
+ CardActions,
+ CardHeader,
+ IconButton,
+ Avatar,
+ Table,
+ TableBody,
+ TableRow,
+ TableCell,
+ TableContainer,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import CloseIcon from '@mui/icons-material/Close';
+import PostAddIcon from '@mui/icons-material/PostAdd';
+import ReplayIcon from '@mui/icons-material/Replay';
+import PublishIcon from '@mui/icons-material/Publish';
+import EditIcon from '@mui/icons-material/Edit';
+import DeleteIcon from '@mui/icons-material/Delete';
import { useTranslation } from '../common/components/LocalizationProvider';
import { formatStatus } from '../common/util/formatter';
@@ -19,6 +31,8 @@ import dimensions from '../common/theme/dimensions';
import { useDeviceReadonly, useReadonly } from '../common/util/permissions';
import usePersistedState from '../common/util/usePersistedState';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import { devicesActions } from '../store';
+import { useCatch } from '../reactHelper';
const useStyles = makeStyles((theme) => ({
card: {
@@ -63,7 +77,8 @@ const StatusRow = ({ name, content }) => {
const StatusCard = ({ deviceId, onClose }) => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
const t = useTranslation();
const readonly = useReadonly();
@@ -75,7 +90,19 @@ const StatusCard = ({ deviceId, onClose }) => {
const positionAttributes = usePositionAttributes(t);
const [positionItems] = usePersistedState('positionItems', ['speed', 'address', 'totalDistance', 'course']);
- const [removeDialogShown, setRemoveDialogShown] = useState(false);
+ const [removing, setRemoving] = useState(false);
+
+ const handleRemove = useCatch(async (removed) => {
+ if (removed) {
+ const response = await fetch('/api/devices');
+ if (response.ok) {
+ dispatch(devicesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ setRemoving(false);
+ });
return (
<>
@@ -119,29 +146,46 @@ const StatusCard = ({ deviceId, onClose }) => {
</CardContent>
)}
<CardActions classes={{ root: classes.actions }} disableSpacing>
- <IconButton onClick={() => history.push(`/position/${position.id}`)} disabled={!position} color="secondary">
+ <IconButton
+ color="secondary"
+ onClick={() => navigate(`/position/${position.id}`)}
+ disabled={!position}
+ >
<PostAddIcon />
</IconButton>
- <IconButton onClick={() => history.push('/replay')} disabled={!position}>
+ <IconButton
+ onClick={() => navigate('/replay')}
+ disabled={!position}
+ >
<ReplayIcon />
</IconButton>
- <IconButton onClick={() => history.push(`/settings/command-send/${deviceId}`)} disabled={readonly}>
+ <IconButton
+ onClick={() => navigate(`/settings/command-send/${deviceId}`)}
+ disabled={readonly}
+ >
<PublishIcon />
</IconButton>
- <IconButton onClick={() => history.push(`/settings/device/${deviceId}`)} disabled={deviceReadonly}>
+ <IconButton
+ onClick={() => navigate(`/settings/device/${deviceId}`)}
+ disabled={deviceReadonly}
+ >
<EditIcon />
</IconButton>
- <IconButton onClick={() => setRemoveDialogShown(true)} disabled={deviceReadonly} className={classes.negative}>
+ <IconButton
+ onClick={() => setRemoving(true)}
+ disabled={deviceReadonly}
+ className={classes.negative}
+ >
<DeleteIcon />
</IconButton>
</CardActions>
</Card>
)}
<RemoveDialog
- open={removeDialogShown}
+ open={removing}
endpoint="devices"
itemId={deviceId}
- onResult={() => setRemoveDialogShown(false)}
+ onResult={(removed) => handleRemove(removed)}
/>
</>
);
diff --git a/modern/src/map/MapGeofenceEdit.js b/modern/src/map/MapGeofenceEdit.js
index c64eb736..0b5062d8 100644
--- a/modern/src/map/MapGeofenceEdit.js
+++ b/modern/src/map/MapGeofenceEdit.js
@@ -5,7 +5,7 @@ import theme from '@mapbox/mapbox-gl-draw/src/lib/theme';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { useHistory } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import { map } from './core/Map';
import { geofenceToFeature, geometryToArea } from './core/mapUtil';
import { errorsActions, geofencesActions } from '../store';
@@ -36,7 +36,7 @@ const draw = new MapboxDraw({
const MapGeofenceEdit = () => {
const dispatch = useDispatch();
- const history = useHistory();
+ const navigate = useNavigate();
const geofences = useSelector((state) => state.geofences.items);
@@ -69,7 +69,7 @@ const MapGeofenceEdit = () => {
});
if (response.ok) {
const item = await response.json();
- history.push(`/settings/geofence/${item.id}`);
+ navigate(`/settings/geofence/${item.id}`);
} else {
throw Error(await response.text());
}
@@ -80,7 +80,7 @@ const MapGeofenceEdit = () => {
map.on('draw.create', listener);
return () => map.off('draw.create', listener);
- }, [dispatch, history]);
+ }, [dispatch, navigate]);
useEffect(() => {
const listener = async (event) => {
diff --git a/modern/src/other/EventPage.js b/modern/src/other/EventPage.js
index 8174de5a..f4427ca9 100644
--- a/modern/src/other/EventPage.js
+++ b/modern/src/other/EventPage.js
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import {
- makeStyles, Typography, AppBar, Toolbar, IconButton,
-} from '@material-ui/core';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import { useHistory, useParams } from 'react-router-dom';
-import ContainerDimensions from 'react-container-dimensions';
+ Typography, AppBar, Toolbar, IconButton,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate, useParams } from 'react-router-dom';
import { useEffectAsync } from '../reactHelper';
import { useTranslation } from '../common/components/LocalizationProvider';
import Map from '../map/core/Map';
@@ -24,7 +24,7 @@ const useStyles = makeStyles(() => ({
const EventPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
const { id } = useParams();
@@ -61,18 +61,16 @@ const EventPage = () => {
<div className={classes.root}>
<AppBar color="inherit" position="static">
<Toolbar>
- <IconButton color="inherit" edge="start" onClick={() => history.push('/')}>
+ <IconButton color="inherit" edge="start" onClick={() => navigate('/')}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6">{t('positionEvent')}</Typography>
</Toolbar>
</AppBar>
<div className={classes.mapContainer}>
- <ContainerDimensions>
- <Map>
- {position && <MapPositions positions={[position]} />}
- </Map>
- </ContainerDimensions>
+ <Map>
+ {position && <MapPositions positions={[position]} />}
+ </Map>
</div>
</div>
);
diff --git a/modern/src/other/GeofencesList.js b/modern/src/other/GeofencesList.js
index d0d0853f..7521de80 100644
--- a/modern/src/other/GeofencesList.js
+++ b/modern/src/other/GeofencesList.js
@@ -1,16 +1,14 @@
import React, { Fragment } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { makeStyles } from '@material-ui/core/styles';
-import Divider from '@material-ui/core/Divider';
-import IconButton from '@material-ui/core/IconButton';
-import List from '@material-ui/core/List';
-import ListItem from '@material-ui/core/ListItem';
-import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
-import ListItemText from '@material-ui/core/ListItemText';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+import makeStyles from '@mui/styles/makeStyles';
+import Divider from '@mui/material/Divider';
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemText from '@mui/material/ListItemText';
-import { devicesActions } from '../store';
-import EditCollectionView from '../settings/components/EditCollectionView';
+import { devicesActions, geofencesActions } from '../store';
+import CollectionActions from '../settings/components/CollectionActions';
+import { useCatchCallback } from '../reactHelper';
const useStyles = makeStyles(() => ({
list: {
@@ -24,23 +22,28 @@ const useStyles = makeStyles(() => ({
},
}));
-const GeofenceView = ({ onMenuClick }) => {
+const GeofencesList = () => {
const classes = useStyles();
const dispatch = useDispatch();
const items = useSelector((state) => state.geofences.items);
+ const refreshGeofences = useCatchCallback(async () => {
+ const response = await fetch('/api/geofences');
+ if (response.ok) {
+ dispatch(geofencesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }, [dispatch]);
+
return (
<List className={classes.list}>
{Object.values(items).map((item, index, list) => (
<Fragment key={item.id}>
<ListItem button key={item.id} onClick={() => dispatch(devicesActions.select(item.id))}>
<ListItemText primary={item.name} />
- <ListItemSecondaryAction>
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </ListItemSecondaryAction>
+ <CollectionActions itemId={item.id} editPath="/settings/geofence" endpoint="geofences" setTimestamp={refreshGeofences} />
</ListItem>
{index < list.length - 1 ? <Divider /> : null}
</Fragment>
@@ -49,8 +52,4 @@ const GeofenceView = ({ onMenuClick }) => {
);
};
-const GeofencesList = () => (
- <EditCollectionView content={GeofenceView} editPath="/settings/geofence" endpoint="geofences" disableAdd />
-);
-
export default GeofencesList;
diff --git a/modern/src/other/GeofencesPage.js b/modern/src/other/GeofencesPage.js
index 1e866de6..6706ec98 100644
--- a/modern/src/other/GeofencesPage.js
+++ b/modern/src/other/GeofencesPage.js
@@ -1,12 +1,12 @@
import React from 'react';
import {
- Divider, makeStyles, Typography, IconButton, useMediaQuery,
-} from '@material-ui/core';
-import { useTheme } from '@material-ui/core/styles';
-import Drawer from '@material-ui/core/Drawer';
-import ContainerDimensions from 'react-container-dimensions';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import { useHistory } from 'react-router-dom';
+ Divider, Typography, IconButton, useMediaQuery,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTheme } from '@mui/material/styles';
+import Drawer from '@mui/material/Drawer';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate } from 'react-router-dom';
import Map from '../map/core/Map';
import MapCurrentLocation from '../map/MapCurrentLocation';
import MapGeofenceEdit from '../map/MapGeofenceEdit';
@@ -25,7 +25,7 @@ const useStyles = makeStyles((theme) => ({
overflow: 'hidden',
display: 'flex',
flexDirection: 'row',
- [theme.breakpoints.down('xs')]: {
+ [theme.breakpoints.down('sm')]: {
flexDirection: 'column-reverse',
},
},
@@ -34,7 +34,7 @@ const useStyles = makeStyles((theme) => ({
[theme.breakpoints.up('sm')]: {
width: dimensions.drawerWidthTablet,
},
- [theme.breakpoints.down('xs')]: {
+ [theme.breakpoints.down('sm')]: {
height: dimensions.drawerHeightPhone,
},
},
@@ -52,10 +52,10 @@ const useStyles = makeStyles((theme) => ({
const GeofencesPage = () => {
const theme = useTheme();
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
- const isPhone = useMediaQuery(theme.breakpoints.down('xs'));
+ const isPhone = useMediaQuery(theme.breakpoints.down('sm'));
return (
<div className={classes.root}>
@@ -66,7 +66,7 @@ const GeofencesPage = () => {
classes={{ paper: classes.drawerPaper }}
>
<div className={classes.drawerHeader}>
- <IconButton onClick={() => history.goBack()}>
+ <IconButton onClick={() => navigate(-1)}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6" color="inherit" noWrap>
@@ -77,12 +77,10 @@ const GeofencesPage = () => {
<GeofencesList />
</Drawer>
<div className={classes.mapContainer}>
- <ContainerDimensions>
- <Map>
- <MapCurrentLocation />
- <MapGeofenceEdit />
- </Map>
- </ContainerDimensions>
+ <Map>
+ <MapCurrentLocation />
+ <MapGeofenceEdit />
+ </Map>
</div>
</div>
</div>
diff --git a/modern/src/other/PositionPage.js b/modern/src/other/PositionPage.js
index 76bb560f..61c40902 100644
--- a/modern/src/other/PositionPage.js
+++ b/modern/src/other/PositionPage.js
@@ -2,10 +2,21 @@ import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import {
- makeStyles, Typography, Container, Paper, AppBar, Toolbar, IconButton, Table, TableHead, TableRow, TableCell, TableBody,
-} from '@material-ui/core';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import { useHistory, useParams } from 'react-router-dom';
+ Typography,
+ Container,
+ Paper,
+ AppBar,
+ Toolbar,
+ IconButton,
+ Table,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate, useParams } from 'react-router-dom';
import { useEffectAsync } from '../reactHelper';
import { prefixString } from '../common/util/stringUtils';
import { useTranslation } from '../common/components/LocalizationProvider';
@@ -26,7 +37,7 @@ const useStyles = makeStyles((theme) => ({
const PositionPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const t = useTranslation();
const { id } = useParams();
@@ -63,7 +74,7 @@ const PositionPage = () => {
<div className={classes.root}>
<AppBar position="sticky" color="inherit">
<Toolbar>
- <IconButton color="inherit" edge="start" onClick={() => history.goBack()}>
+ <IconButton color="inherit" edge="start" onClick={() => navigate(-1)}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6">
diff --git a/modern/src/other/ReplayPage.js b/modern/src/other/ReplayPage.js
index 5e759d7e..1c82c913 100644
--- a/modern/src/other/ReplayPage.js
+++ b/modern/src/other/ReplayPage.js
@@ -2,15 +2,16 @@ import React, {
useState, useEffect, useRef, useCallback,
} from 'react';
import {
- Grid, IconButton, makeStyles, Paper, Slider, Toolbar, Tooltip, Typography,
-} from '@material-ui/core';
-import ArrowBackIcon from '@material-ui/icons/ArrowBack';
-import SettingsIcon from '@material-ui/icons/Settings';
-import PlayArrowIcon from '@material-ui/icons/PlayArrow';
-import PauseIcon from '@material-ui/icons/Pause';
-import FastForwardIcon from '@material-ui/icons/FastForward';
-import FastRewindIcon from '@material-ui/icons/FastRewind';
-import { useHistory } from 'react-router-dom';
+ Grid, IconButton, Paper, Slider, Toolbar, Tooltip, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import SettingsIcon from '@mui/icons-material/Settings';
+import PlayArrowIcon from '@mui/icons-material/PlayArrow';
+import PauseIcon from '@mui/icons-material/Pause';
+import FastForwardIcon from '@mui/icons-material/FastForward';
+import FastRewindIcon from '@mui/icons-material/FastRewind';
+import { useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import Map from '../map/core/Map';
import MapReplayPath from '../map/MapReplayPath';
@@ -33,7 +34,7 @@ const useStyles = makeStyles((theme) => ({
top: 0,
margin: theme.spacing(1.5),
width: theme.dimensions.drawerWidthDesktop,
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
width: '100%',
margin: 0,
},
@@ -60,7 +61,7 @@ const useStyles = makeStyles((theme) => ({
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(2),
- [theme.breakpoints.down('sm')]: {
+ [theme.breakpoints.down('md')]: {
margin: theme.spacing(1),
},
[theme.breakpoints.up('md')]: {
@@ -78,7 +79,7 @@ const TimeLabel = ({ children, open, value }) => (
const ReplayPage = () => {
const t = useTranslation();
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const timerRef = useRef();
const defaultDeviceId = useSelector((state) => state.devices.selectedId);
@@ -100,8 +101,8 @@ const ReplayPage = () => {
});
const onClick = useCallback((positionId) => {
- history.push(`/position/${positionId}`);
- }, [history]);
+ navigate(`/position/${positionId}`);
+ }, [navigate]);
useEffect(() => {
if (playing && positions.length > 0) {
@@ -146,7 +147,7 @@ const ReplayPage = () => {
<div className={classes.sidebar}>
<Paper elevation={3} square>
<Toolbar>
- <IconButton onClick={() => history.push('/')}>
+ <IconButton onClick={() => navigate('/')}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>{t('reportReplay')}</Typography>
diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js
index bb71fa95..becdfb57 100644
--- a/modern/src/reports/ChartReportPage.js
+++ b/modern/src/reports/ChartReportPage.js
@@ -1,7 +1,8 @@
import React, { useState } from 'react';
import {
- FormControl, InputLabel, Select, MenuItem, makeStyles,
-} from '@material-ui/core';
+ FormControl, InputLabel, Select, MenuItem,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import {
CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis,
} from 'recharts';
@@ -58,9 +59,9 @@ const ChartReportPage = () => {
<PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportChart']}>
<ReportFilter handleSubmit={handleSubmit} showOnly>
<div className={filterClasses.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('reportChartType')}</InputLabel>
- <Select value={type} onChange={(e) => setType(e.target.value)}>
+ <Select label={t('reportChartType')} value={type} onChange={(e) => setType(e.target.value)}>
{Object.keys(positionAttributes).filter((key) => positionAttributes[key].type === 'number').map((key) => (
<MenuItem key={key} value={key}>{positionAttributes[key].name}</MenuItem>
))}
diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js
index bd0ad68b..8d71d50b 100644
--- a/modern/src/reports/EventReportPage.js
+++ b/modern/src/reports/EventReportPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
FormControl, InputLabel, Select, MenuItem, TableContainer, Table, TableHead, TableRow, TableCell, TableBody,
-} from '@material-ui/core';
+} from '@mui/material';
import { useSelector } from 'react-redux';
import { formatDate } from '../common/util/formatter';
import ReportFilter, { useFilterStyles } from './components/ReportFilter';
@@ -99,9 +99,10 @@ const EventReportPage = () => {
<PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportEvents']}>
<ReportFilter handleSubmit={handleSubmit}>
<div className={classes.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('reportEventTypes')}</InputLabel>
<Select
+ label={t('reportEventTypes')}
value={eventTypes}
onChange={(event, child) => {
let values = event.target.value;
diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js
index b67833f9..3f89888f 100644
--- a/modern/src/reports/RouteReportPage.js
+++ b/modern/src/reports/RouteReportPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
Table, TableBody, TableCell, TableContainer, TableHead, TableRow,
-} from '@material-ui/core';
+} from '@mui/material';
import ReportFilter from './components/ReportFilter';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
diff --git a/modern/src/reports/StatisticsPage.js b/modern/src/reports/StatisticsPage.js
index 9ed830eb..b9b14902 100644
--- a/modern/src/reports/StatisticsPage.js
+++ b/modern/src/reports/StatisticsPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
-} from '@material-ui/core';
+} from '@mui/material';
import { formatDate } from '../common/util/formatter';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
diff --git a/modern/src/reports/StopReportPage.js b/modern/src/reports/StopReportPage.js
index 5b5fdc29..587e1dd4 100644
--- a/modern/src/reports/StopReportPage.js
+++ b/modern/src/reports/StopReportPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
Table, TableBody, TableCell, TableContainer, TableHead, TableRow,
-} from '@material-ui/core';
+} from '@mui/material';
import {
formatDistance, formatHours, formatDate, formatVolume,
} from '../common/util/formatter';
diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js
index 12f39ac8..d58f7568 100644
--- a/modern/src/reports/SummaryReportPage.js
+++ b/modern/src/reports/SummaryReportPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
FormControl, InputLabel, Select, MenuItem, TableContainer, Table, TableHead, TableRow, TableBody, TableCell,
-} from '@material-ui/core';
+} from '@mui/material';
import {
formatDistance, formatHours, formatDate, formatSpeed, formatVolume,
} from '../common/util/formatter';
@@ -81,9 +81,9 @@ const SummaryReportPage = () => {
<PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportSummary']}>
<ReportFilter handleSubmit={handleSubmit}>
<div className={classes.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('sharedType')}</InputLabel>
- <Select value={daily} onChange={(e) => setDaily(e.target.value)}>
+ <Select label={t('sharedType')} value={daily} onChange={(e) => setDaily(e.target.value)}>
<MenuItem value={false}>{t('reportSummary')}</MenuItem>
<MenuItem value>{t('reportDaily')}</MenuItem>
</Select>
diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js
index 5662e18a..8e03931b 100644
--- a/modern/src/reports/TripReportPage.js
+++ b/modern/src/reports/TripReportPage.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import {
Table, TableBody, TableCell, TableContainer, TableHead, TableRow,
-} from '@material-ui/core';
+} from '@mui/material';
import {
formatDistance, formatSpeed, formatHours, formatDate, formatVolume,
} from '../common/util/formatter';
diff --git a/modern/src/reports/components/ColumnSelect.js b/modern/src/reports/components/ColumnSelect.js
index d7306c58..8a79108e 100644
--- a/modern/src/reports/components/ColumnSelect.js
+++ b/modern/src/reports/components/ColumnSelect.js
@@ -1,7 +1,7 @@
import React from 'react';
import {
FormControl, InputLabel, MenuItem, Select,
-} from '@material-ui/core';
+} from '@mui/material';
import { useTranslation } from '../../common/components/LocalizationProvider';
import { useFilterStyles } from './ReportFilter';
@@ -13,9 +13,10 @@ const ColumnSelect = ({
return (
<div className={classes.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('sharedColumns')}</InputLabel>
<Select
+ label={t('sharedColumns')}
value={columns}
onChange={(e) => setColumns(e.target.value)}
renderValue={(it) => it.length}
diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js
index 350bbe6d..42fcaa9a 100644
--- a/modern/src/reports/components/ReportFilter.js
+++ b/modern/src/reports/components/ReportFilter.js
@@ -1,7 +1,8 @@
import React, { useState } from 'react';
import {
- FormControl, InputLabel, Select, MenuItem, Button, TextField, Typography, makeStyles,
-} from '@material-ui/core';
+ FormControl, InputLabel, Select, MenuItem, Button, TextField, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useSelector } from 'react-redux';
import moment from 'moment';
import { useTranslation } from '../../common/components/LocalizationProvider';
@@ -89,9 +90,9 @@ const ReportFilter = ({
<div className={classes.filter}>
{!ignoreDevice && (
<div className={classes.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('reportDevice')}</InputLabel>
- <Select value={deviceId || ''} onChange={(e) => setDeviceId(e.target.value)}>
+ <Select label={t('reportDevice')} value={deviceId || ''} onChange={(e) => setDeviceId(e.target.value)}>
{Object.values(devices).map((device) => (
<MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
))}
@@ -100,9 +101,9 @@ const ReportFilter = ({
</div>
)}
<div className={classes.item}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('reportPeriod')}</InputLabel>
- <Select value={period} onChange={(e) => setPeriod(e.target.value)}>
+ <Select label={t('reportPeriod')} 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>
@@ -116,7 +117,6 @@ const ReportFilter = ({
{period === 'custom' && (
<div className={classes.item}>
<TextField
- variant="filled"
label={t('reportFrom')}
type="datetime-local"
value={from.format(moment.HTML5_FMT.DATETIME_LOCAL)}
@@ -128,7 +128,6 @@ const ReportFilter = ({
{period === 'custom' && (
<div className={classes.item}>
<TextField
- variant="filled"
label={t('reportTo')}
type="datetime-local"
value={to.format(moment.HTML5_FMT.DATETIME_LOCAL)}
diff --git a/modern/src/reports/components/ReportsMenu.js b/modern/src/reports/components/ReportsMenu.js
index 8e973562..afe9de79 100644
--- a/modern/src/reports/components/ReportsMenu.js
+++ b/modern/src/reports/components/ReportsMenu.js
@@ -1,14 +1,14 @@
import React from 'react';
import {
Divider, List, ListItem, ListItemIcon, ListItemText,
-} 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 BarChartIcon from '@material-ui/icons/BarChart';
+} from '@mui/material';
+import TimelineIcon from '@mui/icons-material/Timeline';
+import PauseCircleFilledIcon from '@mui/icons-material/PauseCircleFilled';
+import PlayCircleFilledIcon from '@mui/icons-material/PlayCircleFilled';
+import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive';
+import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
+import TrendingUpIcon from '@mui/icons-material/TrendingUp';
+import BarChartIcon from '@mui/icons-material/BarChart';
import { Link, useLocation } from 'react-router-dom';
import { useTranslation } from '../../common/components/LocalizationProvider';
import { useAdministrator } from '../../common/util/permissions';
diff --git a/modern/src/settings/AccumulatorsPage.js b/modern/src/settings/AccumulatorsPage.js
index b22fe02e..ce2d18a2 100644
--- a/modern/src/settings/AccumulatorsPage.js
+++ b/modern/src/settings/AccumulatorsPage.js
@@ -1,10 +1,18 @@
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
-import { useHistory, useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Container, TextField, FormControl, Button,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ TextField,
+ FormControl,
+ Button,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
@@ -27,7 +35,7 @@ const useStyles = makeStyles((theme) => ({
}));
const AccumulatorsPage = () => {
- const history = useHistory();
+ const navigate = useNavigate();
const classes = useStyles();
const t = useTranslation();
@@ -54,7 +62,7 @@ const AccumulatorsPage = () => {
});
if (response.ok) {
- history.goBack();
+ navigate(-1);
} else {
throw Error(await response.text());
}
@@ -77,7 +85,6 @@ const AccumulatorsPage = () => {
value={item.hours}
onChange={(event) => setItem({ ...item, hours: Number(event.target.value) })}
label={t('positionHours')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -85,7 +92,6 @@ const AccumulatorsPage = () => {
value={item.totalDistance}
onChange={(event) => setItem({ ...item, totalDistance: Number(event.target.value) })}
label={t('deviceTotalDistance')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
@@ -95,7 +101,7 @@ const AccumulatorsPage = () => {
type="button"
color="primary"
variant="outlined"
- onClick={() => history.goBack()}
+ onClick={() => navigate(-1)}
>
{t('sharedCancel')}
</Button>
diff --git a/modern/src/settings/CalendarPage.js b/modern/src/settings/CalendarPage.js
index 154b3f11..1d967a35 100644
--- a/modern/src/settings/CalendarPage.js
+++ b/modern/src/settings/CalendarPage.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { DropzoneArea } from 'material-ui-dropzone';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
@@ -60,7 +61,6 @@ const CalendarPage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
<DropzoneArea
filesLimit={1}
diff --git a/modern/src/settings/CalendarsPage.js b/modern/src/settings/CalendarsPage.js
index 06697647..a3ff51d5 100644
--- a/modern/src/settings/CalendarsPage.js
+++ b/modern/src/settings/CalendarsPage.js
@@ -1,13 +1,14 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -16,10 +17,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const CalendarsView = ({ updateTimestamp, onMenuClick }) => {
+const CalendarsPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -29,38 +31,33 @@ const CalendarsView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedName')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.name}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedCalendars']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedName')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/calendar" endpoint="calendars" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.name}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/calendar" />
+ </PageLayout>
);
};
-const CalendarsPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedCalendars']}>
- <EditCollectionView content={CalendarsView} editPath="/settings/calendar" endpoint="calendars" />
- </PageLayout>
-);
-
export default CalendarsPage;
diff --git a/modern/src/settings/CommandPage.js b/modern/src/settings/CommandPage.js
index 4785021b..5c306e4c 100644
--- a/modern/src/settings/CommandPage.js
+++ b/modern/src/settings/CommandPage.js
@@ -1,8 +1,9 @@
import React, { useState } from 'react';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, TextField,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion, AccordionSummary, AccordionDetails, Typography, TextField,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import { useTranslation } from '../common/components/LocalizationProvider';
import BaseCommandView from './components/BaseCommandView';
@@ -44,7 +45,6 @@ const CommandPage = () => {
value={item.description || ''}
onChange={(event) => setItem({ ...item, description: event.target.value })}
label={t('sharedDescription')}
- variant="filled"
/>
<BaseCommandView item={item} setItem={setItem} />
</AccordionDetails>
diff --git a/modern/src/settings/CommandSendPage.js b/modern/src/settings/CommandSendPage.js
index cc0dae63..29c9df46 100644
--- a/modern/src/settings/CommandSendPage.js
+++ b/modern/src/settings/CommandSendPage.js
@@ -1,9 +1,16 @@
import React, { useState } from 'react';
-import { useHistory, useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Container, Button, FormControl,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ Button,
+ FormControl,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useTranslation } from '../common/components/LocalizationProvider';
import BaseCommandView from './components/BaseCommandView';
import SelectField from '../common/components/SelectField';
@@ -28,7 +35,7 @@ const useStyles = makeStyles((theme) => ({
}));
const CommandSendPage = () => {
- const history = useHistory();
+ const navigate = useNavigate();
const classes = useStyles();
const t = useTranslation();
@@ -59,7 +66,7 @@ const CommandSendPage = () => {
});
if (response.ok) {
- history.goBack();
+ navigate(-1);
} else {
throw Error(await response.text());
}
@@ -85,7 +92,6 @@ const CommandSendPage = () => {
endpoint={`/api/commands/send?deviceId=${deviceId}`}
titleGetter={(it) => it.description}
label={t('sharedSavedCommand')}
- variant="filled"
/>
{!savedId && (
<BaseCommandView item={item} setItem={setItem} />
@@ -98,7 +104,7 @@ const CommandSendPage = () => {
type="button"
color="primary"
variant="outlined"
- onClick={() => history.goBack()}
+ onClick={() => navigate(-1)}
>
{t('sharedCancel')}
</Button>
diff --git a/modern/src/settings/CommandsPage.js b/modern/src/settings/CommandsPage.js
index 1b09a8bd..dd1559d9 100644
--- a/modern/src/settings/CommandsPage.js
+++ b/modern/src/settings/CommandsPage.js
@@ -1,15 +1,16 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
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';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -18,10 +19,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const CommandsView = ({ updateTimestamp, onMenuClick }) => {
+const CommandsPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -31,42 +33,37 @@ const CommandsView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedDescription')}</TableCell>
- <TableCell>{t('sharedType')}</TableCell>
- <TableCell>{t('commandSendSms')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.description}</TableCell>
- <TableCell>{t(prefixString('command', item.type))}</TableCell>
- <TableCell>{formatBoolean(item.textChannel, t)}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedSavedCommands']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedDescription')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ <TableCell>{t('commandSendSms')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/command" endpoint="commands" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.description}</TableCell>
+ <TableCell>{t(prefixString('command', item.type))}</TableCell>
+ <TableCell>{formatBoolean(item.textChannel, t)}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/command" />
+ </PageLayout>
);
};
-const CommandsPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedSavedCommands']}>
- <EditCollectionView content={CommandsView} editPath="/settings/command" endpoint="commands" />
- </PageLayout>
-);
-
export default CommandsPage;
diff --git a/modern/src/settings/ComputedAttributePage.js b/modern/src/settings/ComputedAttributePage.js
index 984339b8..ba5fe712 100644
--- a/modern/src/settings/ComputedAttributePage.js
+++ b/modern/src/settings/ComputedAttributePage.js
@@ -1,8 +1,17 @@
import React, { useState } from 'react';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, MenuItem, Select, TextField,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+ TextField,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import { useTranslation } from '../common/components/LocalizationProvider';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
@@ -64,11 +73,11 @@ const ComputedAttributePage = () => {
value={item.description || ''}
onChange={(event) => setItem({ ...item, description: event.target.value })}
label={t('sharedDescription')}
- variant="filled"
/>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('sharedAttribute')}</InputLabel>
<Select
+ label={t('sharedAttribute')}
value={item.attribute || ''}
onChange={handleChange}
>
@@ -84,16 +93,15 @@ const ComputedAttributePage = () => {
label={t('sharedExpression')}
multiline
rows={4}
- variant="filled"
/>
<FormControl
- variant="filled"
margin="normal"
fullWidth
disabled={key in positionAttributes}
>
<InputLabel>{t('sharedType')}</InputLabel>
<Select
+ label={t('sharedType')}
value={item.type || ''}
onChange={(event) => setItem({ ...item, type: event.target.value })}
>
diff --git a/modern/src/settings/ComputedAttributesPage.js b/modern/src/settings/ComputedAttributesPage.js
index 86704c3b..b754f9fe 100644
--- a/modern/src/settings/ComputedAttributesPage.js
+++ b/modern/src/settings/ComputedAttributesPage.js
@@ -1,14 +1,15 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { useTranslation } from '../common/components/LocalizationProvider';
import { useAdministrator } from '../common/util/permissions';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -17,10 +18,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => {
+const ComputedAttributesPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
const administrator = useAdministrator();
@@ -31,46 +33,41 @@ const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- {administrator && <TableCell className={classes.columnAction} />}
- <TableCell>{t('sharedDescription')}</TableCell>
- <TableCell>{t('sharedAttribute')}</TableCell>
- <TableCell>{t('sharedExpression')}</TableCell>
- <TableCell>{t('sharedType')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- {administrator && (
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- )}
- <TableCell>{item.description}</TableCell>
- <TableCell>{item.attribute}</TableCell>
- <TableCell>{item.expression}</TableCell>
- <TableCell>{item.type}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedComputedAttributes']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ {administrator && <TableCell className={classes.columnAction} />}
+ <TableCell>{t('sharedDescription')}</TableCell>
+ <TableCell>{t('sharedAttribute')}</TableCell>
+ <TableCell>{t('sharedExpression')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ {administrator && (
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/attribute" endpoint="attributes/computed" setTimestamp={setTimestamp} />
+ </TableCell>
+ )}
+ <TableCell>{item.description}</TableCell>
+ <TableCell>{item.attribute}</TableCell>
+ <TableCell>{item.expression}</TableCell>
+ <TableCell>{item.type}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/attribute" />
+ </PageLayout>
);
};
-const ComputedAttributesPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedComputedAttributes']}>
- <EditCollectionView content={ComputedAttributeView} editPath="/settings/attribute" endpoint="attributes/computed" />
- </PageLayout>
-);
-
export default ComputedAttributesPage;
diff --git a/modern/src/settings/DevicePage.js b/modern/src/settings/DevicePage.js
index 56a589dc..3f194570 100644
--- a/modern/src/settings/DevicePage.js
+++ b/modern/src/settings/DevicePage.js
@@ -1,10 +1,16 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControlLabel,
+ Checkbox,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import SelectField from '../common/components/SelectField';
@@ -59,14 +65,12 @@ const DevicePage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.uniqueId || ''}
onChange={(event) => setItem({ ...item, uniqueId: event.target.value })}
label={t('deviceIdentifier')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
@@ -83,28 +87,24 @@ const DevicePage = () => {
onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
endpoint="/api/groups"
label={t('groupParent')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.phone || ''}
onChange={(event) => setItem({ ...item, phone: event.target.value })}
label={t('sharedPhone')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.model || ''}
onChange={(event) => setItem({ ...item, model: event.target.value })}
label={t('deviceModel')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.contact || ''}
onChange={(event) => setItem({ ...item, contact: event.target.value })}
label={t('deviceContact')}
- variant="filled"
/>
<SelectField
margin="normal"
@@ -116,7 +116,6 @@ const DevicePage = () => {
name: t(`category${category.replace(/^\w/, (c) => c.toUpperCase())}`),
}))}
label={t('deviceCategory')}
- variant="filled"
/>
{admin && (
<FormControlLabel
@@ -156,7 +155,6 @@ const DevicePage = () => {
keyBase="deviceId"
keyLink="geofenceId"
label={t('sharedGeofences')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -167,7 +165,6 @@ const DevicePage = () => {
keyLink="notificationId"
titleGetter={(it) => t(prefixString('event', it.type))}
label={t('sharedNotifications')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -177,7 +174,6 @@ const DevicePage = () => {
keyBase="deviceId"
keyLink="driverId"
label={t('sharedDrivers')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -188,7 +184,6 @@ const DevicePage = () => {
keyLink="attributeId"
titleGetter={(it) => it.description}
label={t('sharedComputedAttributes')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -199,7 +194,6 @@ const DevicePage = () => {
keyLink="commandId"
titleGetter={(it) => it.description}
label={t('sharedSavedCommands')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -209,7 +203,6 @@ const DevicePage = () => {
keyBase="deviceId"
keyLink="maintenanceId"
label={t('sharedMaintenance')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
diff --git a/modern/src/settings/DriverPage.js b/modern/src/settings/DriverPage.js
index 93d09c8e..707e2977 100644
--- a/modern/src/settings/DriverPage.js
+++ b/modern/src/settings/DriverPage.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import { useTranslation } from '../common/components/LocalizationProvider';
@@ -46,14 +47,12 @@ const DriverPage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.uniqueId || ''}
onChange={(event) => setItem({ ...item, uniqueId: event.target.value })}
label={t('deviceIdentifier')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
diff --git a/modern/src/settings/DriversPage.js b/modern/src/settings/DriversPage.js
index 26601777..c4de30e9 100644
--- a/modern/src/settings/DriversPage.js
+++ b/modern/src/settings/DriversPage.js
@@ -1,13 +1,14 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -16,10 +17,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const DriversView = ({ updateTimestamp, onMenuClick }) => {
+const DriversPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -29,40 +31,35 @@ const DriversView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedName')}</TableCell>
- <TableCell>{t('deviceIdentifier')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.name}</TableCell>
- <TableCell>{item.uniqueId}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('deviceIdentifier')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/driver" endpoint="drivers" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.uniqueId}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/driver" />
+ </PageLayout>
);
};
-const DriversPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}>
- <EditCollectionView content={DriversView} editPath="/settings/driver" endpoint="drivers" />
- </PageLayout>
-);
-
export default DriversPage;
diff --git a/modern/src/settings/GeofencePage.js b/modern/src/settings/GeofencePage.js
index e23d49fb..353eb2b8 100644
--- a/modern/src/settings/GeofencePage.js
+++ b/modern/src/settings/GeofencePage.js
@@ -1,10 +1,11 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import { useTranslation } from '../common/components/LocalizationProvider';
@@ -50,7 +51,6 @@ const GeofencePage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
diff --git a/modern/src/settings/GroupPage.js b/modern/src/settings/GroupPage.js
index 9dcd65c9..3f4cc8d8 100644
--- a/modern/src/settings/GroupPage.js
+++ b/modern/src/settings/GroupPage.js
@@ -1,10 +1,11 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
import SelectField from '../common/components/SelectField';
@@ -55,7 +56,6 @@ const GroupPage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
@@ -72,7 +72,6 @@ const GroupPage = () => {
onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
endpoint="/api/groups"
label={t('groupParent')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
@@ -106,7 +105,6 @@ const GroupPage = () => {
keyBase="groupId"
keyLink="geofenceId"
label={t('sharedGeofences')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -117,7 +115,6 @@ const GroupPage = () => {
keyLink="notificationId"
titleGetter={(it) => t(prefixString('event', it.type))}
label={t('sharedNotifications')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -127,7 +124,6 @@ const GroupPage = () => {
keyBase="groupId"
keyLink="driverId"
label={t('sharedDrivers')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -138,7 +134,6 @@ const GroupPage = () => {
keyLink="attributeId"
titleGetter={(it) => it.description}
label={t('sharedComputedAttributes')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -149,7 +144,6 @@ const GroupPage = () => {
keyLink="commandId"
titleGetter={(it) => it.description}
label={t('sharedSavedCommands')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -159,7 +153,6 @@ const GroupPage = () => {
keyBase="groupId"
keyLink="maintenanceId"
label={t('sharedMaintenance')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js
index 257d0bca..ebaa3b5e 100644
--- a/modern/src/settings/GroupsPage.js
+++ b/modern/src/settings/GroupsPage.js
@@ -1,13 +1,14 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -16,10 +17,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const GroupsView = ({ updateTimestamp, onMenuClick }) => {
+const GroupsPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -29,38 +31,33 @@ const GroupsView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedName')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.name}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsGroups']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedName')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/group" endpoint="groups" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.name}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/group" />
+ </PageLayout>
);
};
-const GroupsPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsGroups']}>
- <EditCollectionView content={GroupsView} editPath="/settings/group" endpoint="groups" />
- </PageLayout>
-);
-
export default GroupsPage;
diff --git a/modern/src/settings/MaintenancePage.js b/modern/src/settings/MaintenancePage.js
index 9e53aca1..5694d77b 100644
--- a/modern/src/settings/MaintenancePage.js
+++ b/modern/src/settings/MaintenancePage.js
@@ -1,9 +1,18 @@
import React, { useState } from 'react';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, TextField, FormControl, InputLabel, MenuItem, Select,
-} from '@material-ui/core';
-import InputAdornment from '@material-ui/core/InputAdornment';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ TextField,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import InputAdornment from '@mui/material/InputAdornment';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { prefixString } from '../common/util/stringUtils';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
@@ -120,11 +129,11 @@ const MaintenancePage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('sharedType')}</InputLabel>
<Select
+ label={t('sharedType')}
value={item.type || ''}
onChange={onMaintenanceTypeChange}
>
@@ -139,7 +148,6 @@ const MaintenancePage = () => {
value={rawToValue(item.start) || ''}
onChange={(event) => setItem({ ...item, start: valueToRaw(event.target.value) })}
label={t('maintenanceStart')}
- variant="filled"
InputProps={{
endAdornment: <InputAdornment position="start">{labels.start}</InputAdornment>,
}}
@@ -150,7 +158,6 @@ const MaintenancePage = () => {
value={rawToValue(item.period) || ''}
onChange={(event) => setItem({ ...item, period: valueToRaw(event.target.value) })}
label={t('maintenancePeriod')}
- variant="filled"
InputProps={{
endAdornment: <InputAdornment position="start">{labels.period}</InputAdornment>,
}}
diff --git a/modern/src/settings/MaintenancesPage.js b/modern/src/settings/MaintenancesPage.js
index ea00e7e1..d4a06fd2 100644
--- a/modern/src/settings/MaintenancesPage.js
+++ b/modern/src/settings/MaintenancesPage.js
@@ -1,17 +1,17 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-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 { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -20,12 +20,13 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const MaintenancesView = ({ updateTimestamp, onMenuClick }) => {
+const MaintenacesPage = () => {
const classes = useStyles();
const t = useTranslation();
const positionAttributes = usePositionAttributes(t);
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
const speedUnit = useAttributePreference('speedUnit');
const distanceUnit = useAttributePreference('distanceUnit');
@@ -37,7 +38,7 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
const convertAttribute = (key, value) => {
const attribute = positionAttributes[key];
@@ -56,41 +57,36 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => {
};
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedName')}</TableCell>
- <TableCell>{t('sharedType')}</TableCell>
- <TableCell>{t('maintenanceStart')}</TableCell>
- <TableCell>{t('maintenancePeriod')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.name}</TableCell>
- <TableCell>{item.type}</TableCell>
- <TableCell>{convertAttribute(item.type, item.start)}</TableCell>
- <TableCell>{convertAttribute(item.type, item.period)}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedMaintenance']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ <TableCell>{t('maintenanceStart')}</TableCell>
+ <TableCell>{t('maintenancePeriod')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/maintenance" endpoint="maintenance" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.type}</TableCell>
+ <TableCell>{convertAttribute(item.type, item.start)}</TableCell>
+ <TableCell>{convertAttribute(item.type, item.period)}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/maintenance" />
+ </PageLayout>
);
};
-const MaintenacesPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedMaintenance']}>
- <EditCollectionView content={MaintenancesView} editPath="/settings/maintenance" endpoint="maintenance" />
- </PageLayout>
-);
-
export default MaintenacesPage;
diff --git a/modern/src/settings/NotificationPage.js b/modern/src/settings/NotificationPage.js
index 38ba19e5..a7c06f68 100644
--- a/modern/src/settings/NotificationPage.js
+++ b/modern/src/settings/NotificationPage.js
@@ -1,9 +1,15 @@
import React, { useState } from 'react';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControlLabel,
+ Checkbox,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useTranslation, useTranslationKeys } from '../common/components/LocalizationProvider';
import EditItemView from './components/EditItemView';
import { prefixString, unprefixString } from '../common/util/stringUtils';
@@ -39,60 +45,55 @@ const NotificationPage = () => {
breadcrumbs={['settingsTitle', 'sharedNotification']}
>
{item && (
- <>
- <Accordion defaultExpanded>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1">
- {t('sharedRequired')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <SelectField
- margin="normal"
- value={item.type}
- emptyValue={null}
- onChange={(e) => setItem({ ...item, type: e.target.value })}
- endpoint="/api/notifications/types"
- keyGetter={(it) => it.type}
- titleGetter={(it) => t(prefixString('event', it.type))}
- label={t('sharedType')}
- variant="filled"
- />
- <SelectField
- multiple
- margin="normal"
- value={item.notificators ? item.notificators.split(/[, ]+/) : []}
- onChange={(e) => setItem({ ...item, notificators: e.target.value.join() })}
- endpoint="/api/notifications/notificators"
- keyGetter={(it) => it.type}
- titleGetter={(it) => t(prefixString('notificator', it.type))}
- label={t('notificationNotificators')}
- variant="filled"
- />
- {(!item.type || item.type === 'alarm') && (
- <SelectField
- multiple
- margin="normal"
- value={item.attributes && item.attributes.alarms ? item.attributes.alarms.split(/[, ]+/) : []}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, alarms: e.target.value.join() } })}
- data={alarms}
- keyGetter={(it) => it.key}
- label={t('sharedAlarms')}
- variant="filled"
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ margin="normal"
+ value={item.type}
+ emptyValue={null}
+ onChange={(e) => setItem({ ...item, type: e.target.value })}
+ endpoint="/api/notifications/types"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('event', it.type))}
+ label={t('sharedType')}
+ />
+ <SelectField
+ multiple
+ margin="normal"
+ value={item.notificators ? item.notificators.split(/[, ]+/) : []}
+ onChange={(e) => setItem({ ...item, notificators: e.target.value.join() })}
+ endpoint="/api/notifications/notificators"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('notificator', it.type))}
+ label={t('notificationNotificators')}
+ />
+ {(!item.type || item.type === 'alarm') && (
+ <SelectField
+ multiple
+ margin="normal"
+ value={item.attributes && item.attributes.alarms ? item.attributes.alarms.split(/[, ]+/) : []}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, alarms: e.target.value.join() } })}
+ data={alarms}
+ keyGetter={(it) => it.key}
+ label={t('sharedAlarms')}
+ />
+ )}
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={item.always}
+ onChange={(event) => setItem({ ...item, always: event.target.checked })}
/>
- )}
- <FormControlLabel
- control={(
- <Checkbox
- checked={item.always}
- onChange={(event) => setItem({ ...item, always: event.target.checked })}
- />
)}
- label={t('notificationAlways')}
- />
- </AccordionDetails>
- </Accordion>
- </>
+ label={t('notificationAlways')}
+ />
+ </AccordionDetails>
+ </Accordion>
)}
</EditItemView>
);
diff --git a/modern/src/settings/NotificationsPage.js b/modern/src/settings/NotificationsPage.js
index de3e762f..ae463dd3 100644
--- a/modern/src/settings/NotificationsPage.js
+++ b/modern/src/settings/NotificationsPage.js
@@ -1,15 +1,16 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { prefixString } from '../common/util/stringUtils';
import { formatBoolean } from '../common/util/formatter';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -18,10 +19,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const NotificationsView = ({ updateTimestamp, onMenuClick }) => {
+const NotificationsPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -31,7 +33,7 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
const formatList = (prefix, value) => {
if (value) {
@@ -45,41 +47,36 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => {
};
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('notificationType')}</TableCell>
- <TableCell>{t('notificationAlways')}</TableCell>
- <TableCell>{t('sharedAlarms')}</TableCell>
- <TableCell>{t('notificationNotificators')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{t(prefixString('event', item.type))}</TableCell>
- <TableCell>{formatBoolean(item.always, t)}</TableCell>
- <TableCell>{formatList('alarm', item.attributes.alarms)}</TableCell>
- <TableCell>{formatList('notificator', item.notificators)}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedNotifications']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('notificationType')}</TableCell>
+ <TableCell>{t('notificationAlways')}</TableCell>
+ <TableCell>{t('sharedAlarms')}</TableCell>
+ <TableCell>{t('notificationNotificators')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/notification" endpoint="notifications" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{t(prefixString('event', item.type))}</TableCell>
+ <TableCell>{formatBoolean(item.always, t)}</TableCell>
+ <TableCell>{formatList('alarm', item.attributes.alarms)}</TableCell>
+ <TableCell>{formatList('notificator', item.notificators)}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/notification" />
+ </PageLayout>
);
};
-const NotificationsPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedNotifications']}>
- <EditCollectionView content={NotificationsView} editPath="/settings/notification" endpoint="notifications" />
- </PageLayout>
-);
-
export default NotificationsPage;
diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js
index 94d55adf..93ded1f0 100644
--- a/modern/src/settings/PreferencesPage.js
+++ b/modern/src/settings/PreferencesPage.js
@@ -1,8 +1,19 @@
import React from 'react';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ Checkbox,
+ FormControlLabel,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useLocalization, useTranslation } from '../common/components/LocalizationProvider';
import usePersistedState from '../common/util/usePersistedState';
import PageLayout from '../common/components/PageLayout';
@@ -42,9 +53,13 @@ const PreferencesPage = () => {
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.details}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('loginLanguage')}</InputLabel>
- <Select value={language} onChange={(e) => setLanguage(e.target.value)}>
+ <Select
+ label={t('loginLanguage')}
+ value={language}
+ onChange={(e) => setLanguage(e.target.value)}
+ >
{languageList.map((it) => <MenuItem key={it.code} value={it.code}>{it.name}</MenuItem>)}
</Select>
</FormControl>
@@ -57,9 +72,10 @@ const PreferencesPage = () => {
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.details}>
- <FormControl variant="filled" fullWidth>
+ <FormControl fullWidth>
<InputLabel>{t('sharedAttributes')}</InputLabel>
<Select
+ label={t('sharedAttributes')}
value={positionItems}
onChange={(e) => setPositionItems(e.target.value)}
renderValue={(it) => it.length}
diff --git a/modern/src/settings/ServerPage.js b/modern/src/settings/ServerPage.js
index b745cb96..80353deb 100644
--- a/modern/src/settings/ServerPage.js
+++ b/modern/src/settings/ServerPage.js
@@ -1,11 +1,23 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Button, FormControl, Container, Checkbox, FormControlLabel, InputLabel, Select, MenuItem,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
-import { useHistory } from 'react-router-dom';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Button,
+ FormControl,
+ Container,
+ Checkbox,
+ FormControlLabel,
+ InputLabel,
+ Select,
+ MenuItem,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { sessionActions } from '../store';
import EditAttributesView from './components/EditAttributesView';
@@ -35,7 +47,7 @@ const useStyles = makeStyles((theme) => ({
const ServerPage = () => {
const classes = useStyles();
- const history = useHistory();
+ const navigate = useNavigate();
const dispatch = useDispatch();
const t = useTranslation();
@@ -54,7 +66,7 @@ const ServerPage = () => {
if (response.ok) {
dispatch(sessionActions.updateServer(await response.json()));
- history.goBack();
+ navigate(-1);
} else {
throw Error(await response.text());
}
@@ -77,7 +89,6 @@ const ServerPage = () => {
value={item.mapUrl || ''}
onChange={(event) => setItem({ ...item, mapUrl: event.target.value })}
label={t('mapCustomLabel')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -85,7 +96,6 @@ const ServerPage = () => {
value={item.latitude || 0}
onChange={(event) => setItem({ ...item, latitude: Number(event.target.value) })}
label={t('positionLatitude')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -93,7 +103,6 @@ const ServerPage = () => {
value={item.longitude || 0}
onChange={(event) => setItem({ ...item, longitude: Number(event.target.value) })}
label={t('positionLongitude')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -101,11 +110,11 @@ const ServerPage = () => {
value={item.zoom || 0}
onChange={(event) => setItem({ ...item, zoom: Number(event.target.value) })}
label={t('serverZoom')}
- variant="filled"
/>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
<Select
+ label={t('settingsCoordinateFormat')}
value={item.coordinateFormat || 'dd'}
onChange={(event) => setItem({ ...item, coordinateFormat: event.target.value })}
>
@@ -114,9 +123,10 @@ const ServerPage = () => {
<MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsSpeedUnit')}</InputLabel>
<Select
+ label={t('settingsSpeedUnit')}
value={item.attributes.speedUnit || 'kn'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, speedUnit: e.target.value } })}
>
@@ -125,9 +135,10 @@ const ServerPage = () => {
<MenuItem value="mph">{t('sharedMph')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsDistanceUnit')}</InputLabel>
<Select
+ label={t('settingsDistanceUnit')}
value={item.attributes.distanceUnit || 'km'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, distanceUnit: e.target.value } })}
>
@@ -136,9 +147,10 @@ const ServerPage = () => {
<MenuItem value="nmi">{t('sharedNmi')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsVolumeUnit')}</InputLabel>
<Select
+ label={t('settingsVolumeUnit')}
value={item.attributes.volumeUnit || 'ltr'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, volumeUnit: e.target.value } })}
>
@@ -156,21 +168,18 @@ const ServerPage = () => {
keyGetter={(it) => it}
titleGetter={(it) => it}
label={t('sharedTimezone')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.poiLayer || ''}
onChange={(event) => setItem({ ...item, poiLayer: event.target.value })}
label={t('mapPoiLayer')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.announcement || ''}
onChange={(event) => setItem({ ...item, announcement: event.target.value })}
label={t('serverAnnouncement')}
- variant="filled"
/>
<FormControlLabel
control={<Checkbox checked={item.twelveHourFormat} onChange={(event) => setItem({ ...item, twelveHourFormat: event.target.checked })} />}
@@ -229,7 +238,7 @@ const ServerPage = () => {
)}
<FormControl fullWidth margin="normal">
<div className={classes.buttons}>
- <Button type="button" color="primary" variant="outlined" onClick={() => history.goBack()}>
+ <Button type="button" color="primary" variant="outlined" onClick={() => navigate(-1)}>
{t('sharedCancel')}
</Button>
<Button type="button" color="primary" variant="contained" onClick={handleSave}>
diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js
index a0f6f753..d9238c22 100644
--- a/modern/src/settings/UserPage.js
+++ b/modern/src/settings/UserPage.js
@@ -1,11 +1,24 @@
import React, { useState } from 'react';
-import TextField from '@material-ui/core/TextField';
+import TextField from '@mui/material/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox, InputAdornment, IconButton, FilledInput,
-} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
-import CachedIcon from '@material-ui/icons/Cached';
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ FormControlLabel,
+ Checkbox,
+ InputAdornment,
+ IconButton,
+ OutlinedInput,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import CachedIcon from '@mui/icons-material/Cached';
import { useDispatch, useSelector } from 'react-redux';
import moment from 'moment';
import EditItemView from './components/EditItemView';
@@ -74,21 +87,18 @@ const UserPage = () => {
value={item.name || ''}
onChange={(event) => setItem({ ...item, name: event.target.value })}
label={t('sharedName')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.email || ''}
onChange={(event) => setItem({ ...item, email: event.target.value })}
label={t('userEmail')}
- variant="filled"
/>
<TextField
margin="normal"
type="password"
onChange={(event) => setItem({ ...item, password: event.target.value })}
label={t('userPassword')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
@@ -104,7 +114,6 @@ const UserPage = () => {
value={item.phone || ''}
onChange={(event) => setItem({ ...item, phone: event.target.value })}
label={t('sharedPhone')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -112,7 +121,6 @@ const UserPage = () => {
value={item.latitude || 0}
onChange={(event) => setItem({ ...item, latitude: Number(event.target.value) })}
label={t('positionLatitude')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -120,7 +128,6 @@ const UserPage = () => {
value={item.longitude || 0}
onChange={(event) => setItem({ ...item, longitude: Number(event.target.value) })}
label={t('positionLongitude')}
- variant="filled"
/>
<TextField
margin="normal"
@@ -128,11 +135,11 @@ const UserPage = () => {
value={item.zoom || 0}
onChange={(event) => setItem({ ...item, zoom: Number(event.target.value) })}
label={t('serverZoom')}
- variant="filled"
/>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
<Select
+ label={t('settingsCoordinateFormat')}
value={item.coordinateFormat || 'dd'}
onChange={(event) => setItem({ ...item, coordinateFormat: event.target.value })}
>
@@ -141,9 +148,10 @@ const UserPage = () => {
<MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsSpeedUnit')}</InputLabel>
<Select
+ label={t('settingsSpeedUnit')}
value={(item.attributes && item.attributes.speedUnit) || 'kn'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, speedUnit: e.target.value } })}
>
@@ -152,9 +160,10 @@ const UserPage = () => {
<MenuItem value="mph">{t('sharedMph')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsDistanceUnit')}</InputLabel>
<Select
+ label={t('settingsDistanceUnit')}
value={(item.attributes && item.attributes.distanceUnit) || 'km'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, distanceUnit: e.target.value } })}
>
@@ -163,9 +172,10 @@ const UserPage = () => {
<MenuItem value="nmi">{t('sharedNmi')}</MenuItem>
</Select>
</FormControl>
- <FormControl variant="filled" margin="normal" fullWidth>
+ <FormControl margin="normal" fullWidth>
<InputLabel>{t('settingsVolumeUnit')}</InputLabel>
<Select
+ label={t('settingsVolumeUnit')}
value={(item.attributes && item.attributes.volumeUnit) || 'ltr'}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, volumeUnit: e.target.value } })}
>
@@ -183,14 +193,12 @@ const UserPage = () => {
keyGetter={(it) => it}
titleGetter={(it) => it}
label={t('sharedTimezone')}
- variant="filled"
/>
<TextField
margin="normal"
value={item.poiLayer || ''}
onChange={(event) => setItem({ ...item, poiLayer: event.target.value })}
label={t('mapPoiLayer')}
- variant="filled"
/>
<FormControlLabel
control={<Checkbox checked={item.twelveHourFormat} onChange={(event) => setItem({ ...item, twelveHourFormat: event.target.checked })} />}
@@ -205,18 +213,19 @@ const UserPage = () => {
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.details}>
- <FormControl variant="filled" margin="normal">
+ <FormControl margin="normal">
<InputLabel>{t('userToken')}</InputLabel>
- <FilledInput
+ <OutlinedInput
type="text"
value={item.token || ''}
onChange={(e) => setItem({ ...item, token: e.target.value })}
endAdornment={(
<InputAdornment position="end">
- <IconButton onClick={() => {
- const token = [...Array(30)].map(() => Math.random().toString(36)[2]).join('');
- setItem({ ...item, token });
- }}
+ <IconButton
+ onClick={() => {
+ const token = [...Array(30)].map(() => Math.random().toString(36)[2]).join('');
+ setItem({ ...item, token });
+ }}
>
<CachedIcon />
</IconButton>
@@ -226,7 +235,6 @@ const UserPage = () => {
</FormControl>
<TextField
margin="normal"
- variant="filled"
label={t('userExpirationTime')}
type="date"
value={(item.expirationTime && item.expirationTime.format(moment.HTML5_FMT.DATE)) || '2999-01-01'}
@@ -239,7 +247,6 @@ const UserPage = () => {
value={item.deviceLimit || 0}
onChange={(e) => setItem({ ...item, deviceLimit: Number(e.target.value) })}
label={t('userDeviceLimit')}
- variant="filled"
disabled={!admin}
/>
<TextField
@@ -248,7 +255,6 @@ const UserPage = () => {
value={item.userLimit || 0}
onChange={(e) => setItem({ ...item, userLimit: Number(e.target.value) })}
label={t('userUserLimit')}
- variant="filled"
disabled={!admin}
/>
<FormControlLabel
@@ -313,7 +319,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="deviceId"
label={t('deviceTitle')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -323,7 +328,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="groupId"
label={t('settingsGroups')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -333,7 +337,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="geofenceId"
label={t('sharedGeofences')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -344,7 +347,6 @@ const UserPage = () => {
keyLink="notificationId"
titleGetter={(it) => t(prefixString('event', it.type))}
label={t('sharedNotifications')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -354,7 +356,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="calendarId"
label={t('sharedCalendars')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -364,7 +365,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="managedUserId"
label={t('settingsUsers')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -375,7 +375,6 @@ const UserPage = () => {
keyLink="attributeId"
titleGetter={(it) => it.description}
label={t('sharedComputedAttributes')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -385,7 +384,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="driverId"
label={t('sharedDrivers')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -396,7 +394,6 @@ const UserPage = () => {
keyLink="commandId"
titleGetter={(it) => it.description}
label={t('sharedSavedCommands')}
- variant="filled"
/>
<LinkField
margin="normal"
@@ -406,7 +403,6 @@ const UserPage = () => {
keyBase="userId"
keyLink="maintenanceId"
label={t('sharedMaintenance')}
- variant="filled"
/>
</AccordionDetails>
</Accordion>
diff --git a/modern/src/settings/UsersPage.js b/modern/src/settings/UsersPage.js
index 235ae4c7..8f3aca46 100644
--- a/modern/src/settings/UsersPage.js
+++ b/modern/src/settings/UsersPage.js
@@ -1,14 +1,15 @@
import React, { useState } from 'react';
import {
- TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton,
-} from '@material-ui/core';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
+ TableContainer, Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
-import EditCollectionView from './components/EditCollectionView';
import { formatBoolean } from '../common/util/formatter';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
const useStyles = makeStyles((theme) => ({
columnAction: {
@@ -17,10 +18,11 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const UsersView = ({ updateTimestamp, onMenuClick }) => {
+const UsersPage = () => {
const classes = useStyles();
const t = useTranslation();
+ const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
useEffectAsync(async () => {
@@ -30,44 +32,39 @@ const UsersView = ({ updateTimestamp, onMenuClick }) => {
} else {
throw Error(await response.text());
}
- }, [updateTimestamp]);
+ }, [timestamp]);
return (
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell className={classes.columnAction} />
- <TableCell>{t('sharedName')}</TableCell>
- <TableCell>{t('userEmail')}</TableCell>
- <TableCell>{t('userAdmin')}</TableCell>
- <TableCell>{t('sharedDisabled')}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {items.map((item) => (
- <TableRow key={item.id}>
- <TableCell className={classes.columnAction} padding="none">
- <IconButton size="small" onClick={(event) => onMenuClick(event.currentTarget, item.id)}>
- <MoreVertIcon />
- </IconButton>
- </TableCell>
- <TableCell>{item.name}</TableCell>
- <TableCell>{item.email}</TableCell>
- <TableCell>{formatBoolean(item.administrator, t)}</TableCell>
- <TableCell>{formatBoolean(item.disabled, t)}</TableCell>
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsUsers']}>
+ <TableContainer>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('userEmail')}</TableCell>
+ <TableCell>{t('userAdmin')}</TableCell>
+ <TableCell>{t('sharedDisabled')}</TableCell>
</TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
+ </TableHead>
+ <TableBody>
+ {items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/user" endpoint="users" setTimestamp={setTimestamp} />
+ </TableCell>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.email}</TableCell>
+ <TableCell>{formatBoolean(item.administrator, t)}</TableCell>
+ <TableCell>{formatBoolean(item.disabled, t)}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <CollectionFab editPath="/settings/user" />
+ </PageLayout>
);
};
-const UsersPage = () => (
- <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsUsers']}>
- <EditCollectionView content={UsersView} editPath="/settings/user" endpoint="users" />
- </PageLayout>
-);
-
export default UsersPage;
diff --git a/modern/src/settings/components/AddAttributeDialog.js b/modern/src/settings/components/AddAttributeDialog.js
index e7965360..de5d22b7 100644
--- a/modern/src/settings/components/AddAttributeDialog.js
+++ b/modern/src/settings/components/AddAttributeDialog.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import {
Button, Dialog, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, TextField,
-} from '@material-ui/core';
+ Autocomplete,
+} from '@mui/material';
-import { Autocomplete, createFilterOptions } from '@material-ui/lab';
+import { createFilterOptions } from '@mui/material/useAutocomplete';
import { useTranslation } from '../../common/components/LocalizationProvider';
const AddAttributeDialog = ({ open, onResult, definitions }) => {
@@ -47,17 +48,17 @@ const AddAttributeDialog = ({ open, onResult, definitions }) => {
renderOption={(option) => option.name}
freeSolo
renderInput={(params) => (
- <TextField {...params} label={t('sharedAttribute')} variant="filled" margin="normal" />
+ <TextField {...params} label={t('sharedAttribute')} margin="normal" />
)}
/>
<FormControl
- variant="filled"
margin="normal"
fullWidth
disabled={key in definitions}
>
<InputLabel>{t('sharedType')}</InputLabel>
<Select
+ label={t('sharedType')}
value={type}
onChange={(e) => setType(e.target.value)}
>
diff --git a/modern/src/settings/components/BaseCommandView.js b/modern/src/settings/components/BaseCommandView.js
index 836e8789..c578e111 100644
--- a/modern/src/settings/components/BaseCommandView.js
+++ b/modern/src/settings/components/BaseCommandView.js
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import {
TextField, FormControlLabel, Checkbox,
-} from '@material-ui/core';
+} from '@mui/material';
import { useTranslation } from '../../common/components/LocalizationProvider';
import SelectField from '../../common/components/SelectField';
import { prefixString } from '../../common/util/stringUtils';
@@ -32,7 +32,6 @@ const BaseCommandView = ({ item, setItem }) => {
keyGetter={(it) => it.type}
titleGetter={(it) => t(prefixString('command', it.type))}
label={t('sharedType')}
- variant="filled"
/>
{attributes.map(({ key, name, type }) => {
if (type === 'boolean') {
@@ -63,7 +62,6 @@ const BaseCommandView = ({ item, setItem }) => {
setItem(updateItem);
}}
label={name}
- variant="filled"
/>
);
})}
diff --git a/modern/src/settings/components/CollectionActions.js b/modern/src/settings/components/CollectionActions.js
new file mode 100644
index 00000000..c5e14949
--- /dev/null
+++ b/modern/src/settings/components/CollectionActions.js
@@ -0,0 +1,48 @@
+import React, { useState } from 'react';
+import { IconButton, Menu, MenuItem } from '@mui/material';
+import MoreVertIcon from '@mui/icons-material/MoreVert';
+import { useNavigate } from 'react-router-dom';
+import RemoveDialog from '../../common/components/RemoveDialog';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+
+const CollectionActions = ({
+ itemId, editPath, endpoint, setTimestamp,
+}) => {
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const [menuAnchorEl, setMenuAnchorEl] = useState(null);
+ const [removing, setRemoving] = useState(false);
+
+ const handleEdit = () => {
+ navigate(`${editPath}/${itemId}`);
+ setMenuAnchorEl(null);
+ };
+
+ const handleRemove = () => {
+ setRemoving(true);
+ setMenuAnchorEl(null);
+ };
+
+ const handleRemoveResult = (removed) => {
+ setRemoving(false);
+ if (removed) {
+ setTimestamp(Date.now());
+ }
+ };
+
+ return (
+ <>
+ <IconButton size="small" onClick={(event) => setMenuAnchorEl(event.currentTarget)}>
+ <MoreVertIcon />
+ </IconButton>
+ <Menu open={!!menuAnchorEl} anchorEl={menuAnchorEl} onClose={() => setMenuAnchorEl(null)}>
+ <MenuItem onClick={handleEdit}>{t('sharedEdit')}</MenuItem>
+ <MenuItem onClick={handleRemove}>{t('sharedRemove')}</MenuItem>
+ </Menu>
+ <RemoveDialog style={{ transform: 'none' }} open={removing} endpoint={endpoint} itemId={itemId} onResult={handleRemoveResult} />
+ </>
+ );
+};
+
+export default CollectionActions;
diff --git a/modern/src/settings/components/CollectionFab.js b/modern/src/settings/components/CollectionFab.js
new file mode 100644
index 00000000..2fb5b5c9
--- /dev/null
+++ b/modern/src/settings/components/CollectionFab.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { Fab } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import AddIcon from '@mui/icons-material/Add';
+import { useNavigate } from 'react-router-dom';
+import { useReadonly } from '../../common/util/permissions';
+import dimensions from '../../common/theme/dimensions';
+
+const useStyles = makeStyles((theme) => ({
+ fab: {
+ position: 'fixed',
+ bottom: theme.spacing(2),
+ right: theme.spacing(2),
+ [theme.breakpoints.down('md')]: {
+ bottom: dimensions.bottomBarHeight + theme.spacing(2),
+ },
+ },
+}));
+
+const CollectionFab = ({ editPath, disabled }) => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+
+ const readonly = useReadonly();
+
+ if (!readonly && !disabled) {
+ return (
+ <Fab size="medium" color="primary" className={classes.fab} onClick={() => navigate(editPath)}>
+ <AddIcon />
+ </Fab>
+ );
+ }
+ return '';
+};
+
+export default CollectionFab;
diff --git a/modern/src/settings/components/EditAttributesView.js b/modern/src/settings/components/EditAttributesView.js
index e28909e9..e6e1ddf0 100644
--- a/modern/src/settings/components/EditAttributesView.js
+++ b/modern/src/settings/components/EditAttributesView.js
@@ -1,10 +1,19 @@
import React, { useState } from 'react';
import {
- Button, Checkbox, FilledInput, FormControl, FormControlLabel, Grid, IconButton, InputAdornment, InputLabel, makeStyles,
-} from '@material-ui/core';
-import CloseIcon from '@material-ui/icons/Close';
-import AddIcon from '@material-ui/icons/Add';
+ Button,
+ Checkbox,
+ OutlinedInput,
+ FormControl,
+ FormControlLabel,
+ Grid,
+ IconButton,
+ InputAdornment,
+ InputLabel,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import CloseIcon from '@mui/icons-material/Close';
+import AddIcon from '@mui/icons-material/Add';
import AddAttributeDialog from './AddAttributeDialog';
import { useTranslation } from '../../common/components/LocalizationProvider';
import { useAttributePreference } from '../../common/util/preferences';
@@ -164,9 +173,9 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => {
);
}
return (
- <FormControl variant="filled" margin="normal" key={key}>
+ <FormControl margin="normal" key={key}>
<InputLabel>{getAttributeName(key, subtype)}</InputLabel>
- <FilledInput
+ <OutlinedInput
type={type === 'number' ? 'number' : 'text'}
value={getDisplayValue(value, subtype)}
onChange={(e) => updateAttribute(key, e.target.value, type, subtype)}
@@ -182,7 +191,6 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => {
);
})}
<Button
- size="large"
variant="outlined"
color="primary"
onClick={() => setAddDialogShown(true)}
diff --git a/modern/src/settings/components/EditCollectionView.js b/modern/src/settings/components/EditCollectionView.js
deleted file mode 100644
index b0bf3ea0..00000000
--- a/modern/src/settings/components/EditCollectionView.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import React, { useState } from 'react';
-import { makeStyles } from '@material-ui/core/styles';
-import { useHistory } from 'react-router-dom';
-import Menu from '@material-ui/core/Menu';
-import MenuItem from '@material-ui/core/MenuItem';
-import Fab from '@material-ui/core/Fab';
-import AddIcon from '@material-ui/icons/Add';
-
-import RemoveDialog from '../../common/components/RemoveDialog';
-import { useTranslation } from '../../common/components/LocalizationProvider';
-import dimensions from '../../common/theme/dimensions';
-import { useReadonly } from '../../common/util/permissions';
-
-const useStyles = makeStyles((theme) => ({
- fab: {
- position: 'fixed',
- bottom: theme.spacing(2),
- right: theme.spacing(2),
- [theme.breakpoints.down('sm')]: {
- bottom: dimensions.bottomBarHeight + theme.spacing(2),
- },
- },
-}));
-
-const EditCollectionView = ({
- content, editPath, endpoint, disableAdd, filter,
-}) => {
- const classes = useStyles();
- const history = useHistory();
- const t = useTranslation();
-
- const readonly = useReadonly();
-
- const [selectedId, setSelectedId] = useState(null);
- const [selectedAnchorEl, setSelectedAnchorEl] = useState(null);
- const [removeDialogShown, setRemoveDialogShown] = useState(false);
- const [updateTimestamp, setUpdateTimestamp] = useState(Date.now());
-
- const menuShow = (anchorId, itemId) => {
- setSelectedAnchorEl(anchorId);
- setSelectedId(itemId);
- };
-
- const menuHide = () => {
- setSelectedAnchorEl(null);
- };
-
- const handleAdd = () => {
- history.push(editPath);
- menuHide();
- };
-
- const handleEdit = () => {
- history.push(`${editPath}/${selectedId}`);
- menuHide();
- };
-
- const handleRemove = () => {
- setRemoveDialogShown(true);
- menuHide();
- };
-
- const handleRemoveResult = (removed) => {
- setRemoveDialogShown(false);
- if (removed) {
- setUpdateTimestamp(Date.now());
- }
- };
-
- const Content = content;
-
- return (
- <>
- <Content updateTimestamp={updateTimestamp} onMenuClick={menuShow} filter={filter} />
- {!readonly && !disableAdd && (
- <Fab size="medium" color="primary" className={classes.fab} onClick={handleAdd}>
- <AddIcon />
- </Fab>
- )}
- <Menu open={!!selectedAnchorEl} anchorEl={selectedAnchorEl} onClose={menuHide}>
- <MenuItem onClick={handleEdit}>{t('sharedEdit')}</MenuItem>
- <MenuItem onClick={handleRemove}>{t('sharedRemove')}</MenuItem>
- </Menu>
- <RemoveDialog open={removeDialogShown} endpoint={endpoint} itemId={selectedId} onResult={handleRemoveResult} />
- </>
- );
-};
-
-export default EditCollectionView;
diff --git a/modern/src/settings/components/EditItemView.js b/modern/src/settings/components/EditItemView.js
index 28598e77..83624b8f 100644
--- a/modern/src/settings/components/EditItemView.js
+++ b/modern/src/settings/components/EditItemView.js
@@ -1,9 +1,9 @@
import React from 'react';
-import { useHistory, useParams } from 'react-router-dom';
-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 { useNavigate, useParams } from 'react-router-dom';
+import makeStyles from '@mui/styles/makeStyles';
+import Container from '@mui/material/Container';
+import Button from '@mui/material/Button';
+import FormControl from '@mui/material/FormControl';
import { useCatch, useEffectAsync } from '../../reactHelper';
import { useTranslation } from '../../common/components/LocalizationProvider';
@@ -25,7 +25,7 @@ const useStyles = makeStyles((theme) => ({
const EditItemView = ({
children, endpoint, item, setItem, defaultItem, validate, onItemSaved, menu, breadcrumbs,
}) => {
- const history = useHistory();
+ const navigate = useNavigate();
const classes = useStyles();
const t = useTranslation();
@@ -60,7 +60,7 @@ const EditItemView = ({
if (onItemSaved) {
onItemSaved(await response.json());
}
- history.goBack();
+ navigate(-1);
} else {
throw Error(await response.text());
}
@@ -76,7 +76,7 @@ const EditItemView = ({
type="button"
color="primary"
variant="outlined"
- onClick={() => history.goBack()}
+ onClick={() => navigate(-1)}
>
{t('sharedCancel')}
</Button>
diff --git a/modern/src/settings/components/SettingsMenu.js b/modern/src/settings/components/SettingsMenu.js
index 036f4101..cb2dba2f 100644
--- a/modern/src/settings/components/SettingsMenu.js
+++ b/modern/src/settings/components/SettingsMenu.js
@@ -1,17 +1,17 @@
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';
+} from '@mui/material';
+import SettingsIcon from '@mui/icons-material/Settings';
+import CreateIcon from '@mui/icons-material/Create';
+import NotificationsIcon from '@mui/icons-material/Notifications';
+import FolderIcon from '@mui/icons-material/Folder';
+import PersonIcon from '@mui/icons-material/Person';
+import StorageIcon from '@mui/icons-material/Storage';
+import BuildIcon from '@mui/icons-material/Build';
+import PeopleIcon from '@mui/icons-material/People';
+import TodayIcon from '@mui/icons-material/Today';
+import PublishIcon from '@mui/icons-material/Publish';
import { Link, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { useTranslation } from '../../common/components/LocalizationProvider';