From 0512964d71a25c172735f2149ef60c3a8b20f683 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 10 Jul 2021 15:24:10 -0700 Subject: Use modified airbnb eslint --- modern/.eslintrc.js | 18 +++ modern/craco.config.js | 8 +- modern/package.json | 11 +- modern/src/App.js | 68 +++++------ modern/src/CachingController.js | 22 ++-- modern/src/DevicePage.js | 97 +++++++++------- modern/src/DevicesList.js | 15 ++- modern/src/EditCollectionView.js | 27 +++-- modern/src/EditItemView.js | 18 +-- modern/src/GeofencePage.js | 22 ++-- modern/src/GeofencesList.js | 12 +- modern/src/GeofencesPage.js | 13 ++- modern/src/Logo.js | 47 ++++---- modern/src/MainPage.js | 11 +- modern/src/MainToolbar.js | 38 +++--- modern/src/PositionPage.js | 31 ++--- modern/src/RegisterDialog.js | 62 +++++----- modern/src/RemoveDialog.js | 9 +- modern/src/SocketController.js | 30 ++--- modern/src/StartPage.js | 19 +-- modern/src/UserPage.js | 47 +++++--- modern/src/admin/ServerPage.js | 56 +++++---- modern/src/admin/StatisticsPage.js | 30 ++--- modern/src/admin/UsersPage.js | 74 ++++++------ modern/src/attributes/AddAttributeDialog.js | 36 +++--- modern/src/attributes/EditAttributesView.js | 129 +++++++++++---------- modern/src/attributes/deviceAttributes.js | 4 +- modern/src/attributes/geofenceAttributes.js | 4 +- modern/src/attributes/positionAttributes.js | 10 +- modern/src/attributes/userAttributes.js | 12 +- modern/src/common/converter.js | 24 ++-- modern/src/common/deviceCategories.js | 2 +- modern/src/common/formatter.js | 54 ++++----- modern/src/common/localization.js | 116 +++++++++--------- modern/src/common/preferences.js | 30 ++--- modern/src/common/stringUtils.js | 8 +- modern/src/components/registration/LoginForm.js | 71 +++++++----- modern/src/components/registration/RegisterForm.js | 82 ++++++------- .../components/registration/ResetPasswordForm.js | 13 +-- modern/src/components/reports/ReportNavbar.js | 42 +++---- modern/src/components/reports/ReportSidebar.js | 18 +-- modern/src/form/LinkField.js | 44 +++---- modern/src/form/SelectField.js | 23 ++-- modern/src/index.js | 4 +- modern/src/map/AccuracyMap.js | 36 +++--- modern/src/map/CurrentLocationMap.js | 2 +- modern/src/map/CurrentPositionsMap.js | 6 +- modern/src/map/GeofenceEditMap.js | 36 +++--- modern/src/map/GeofenceMap.js | 56 ++++----- modern/src/map/Map.js | 33 +++--- modern/src/map/PositionsMap.js | 71 ++++++------ modern/src/map/ReplayPathMap.js | 28 ++--- modern/src/map/SelectedDeviceMap.js | 4 +- modern/src/map/StatusView.js | 65 +++++++++-- modern/src/map/mapStyles.js | 42 +++---- modern/src/map/mapUtil.js | 57 ++++----- modern/src/map/switcher/switcher.js | 5 +- modern/src/reactHelper.js | 5 +- modern/src/reports/ChartReportPage.js | 69 ++++++----- modern/src/reports/EventReportPage.js | 39 ++++--- modern/src/reports/Graph.js | 37 +++--- modern/src/reports/ReplayPage.js | 36 +++--- modern/src/reports/ReportFilter.js | 85 ++++++++------ modern/src/reports/ReportLayoutPage.js | 45 +++---- modern/src/reports/RouteReportPage.js | 24 ++-- modern/src/reports/StopReportPage.js | 33 +++--- modern/src/reports/SummaryReportPage.js | 38 +++--- modern/src/reports/TripReportPage.js | 33 +++--- modern/src/serviceWorker.js | 38 +++--- modern/src/settings/ComputedAttributePage.js | 60 +++++----- modern/src/settings/ComputedAttributesPage.js | 73 ++++++------ modern/src/settings/DriverPage.js | 27 +++-- modern/src/settings/DriversPage.js | 66 +++++------ modern/src/settings/GroupPage.js | 27 +++-- modern/src/settings/GroupsPage.js | 62 +++++----- modern/src/settings/MaintenancePage.js | 94 ++++++++------- modern/src/settings/MaintenancesPage.js | 78 ++++++------- modern/src/settings/NotificationPage.js | 56 +++++---- modern/src/settings/NotificationsPage.js | 76 ++++++------ modern/src/setupProxy.js | 8 +- modern/src/store/devices.js | 8 +- modern/src/store/drivers.js | 4 +- modern/src/store/geofences.js | 6 +- modern/src/store/groups.js | 4 +- modern/src/store/maintenances.js | 4 +- modern/src/store/positions.js | 4 +- modern/src/theme/dimensions.js | 2 +- modern/src/theme/index.js | 2 +- modern/src/theme/overrides.js | 50 ++++---- 89 files changed, 1661 insertions(+), 1484 deletions(-) create mode 100644 modern/.eslintrc.js diff --git a/modern/.eslintrc.js b/modern/.eslintrc.js new file mode 100644 index 00000000..a1089804 --- /dev/null +++ b/modern/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + "extends": "airbnb", + "plugins": [ + "react" + ], + "ignorePatterns": ["serviceWorker.js", "localization.js", "switcher.js"], + "rules": { + "max-len": [0], + "no-shadow": [0], + "no-return-assign": [0], + "no-param-reassign": [0], + "no-prototype-builtins": [0], + "react/jsx-filename-extension": [1, { "extensions": [".js"] }], + "react/prop-types": [0], + "react/jsx-props-no-spreading": [0], + "jsx-a11y/anchor-is-valid": [0] + } +}; diff --git a/modern/craco.config.js b/modern/craco.config.js index 3e5d98e9..32971f24 100644 --- a/modern/craco.config.js +++ b/modern/craco.config.js @@ -1,12 +1,12 @@ module.exports = { webpack: { - configure: webpackConfig => { + configure: (webpackConfig) => { const scopePluginIndex = webpackConfig.resolve.plugins.findIndex( - ({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin' + ({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin', ); webpackConfig.resolve.plugins.splice(scopePluginIndex, 1); return webpackConfig; - } - } + }, + }, }; diff --git a/modern/package.json b/modern/package.json index 6c74cbe5..f986b314 100644 --- a/modern/package.json +++ b/modern/package.json @@ -10,6 +10,7 @@ "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.58", "@reduxjs/toolkit": "^1.6.0", + "@turf/circle": "^6.5.0", "@turf/turf": "^6.4.0", "canvas-tint-image": "^2.0.1", "maplibre-gl": "^1.15.0", @@ -31,7 +32,9 @@ "start": "craco start", "build": "craco build", "build_release": "PUBLIC_URL=/modern/ craco build", - "test": "craco test" + "test": "craco test", + "lint": "eslint .", + "lint:fix": "eslint --fix --ext .js ." }, "browserslist": { "production": [ @@ -44,5 +47,11 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "eslint": "^6.8.0", + "eslint-config-airbnb": "^18.2.1", + "eslint-plugin-react": "^7.24.0", + "eslint-plugin-react-hooks": "^1.7.0" } } diff --git a/modern/src/App.js b/modern/src/App.js index b76995ea..ac7cdb26 100644 --- a/modern/src/App.js +++ b/modern/src/App.js @@ -1,7 +1,9 @@ import React from 'react'; import { ThemeProvider } from '@material-ui/core/styles'; -import { Switch, Route } from 'react-router-dom' +import { Switch, Route } from 'react-router-dom'; import CssBaseline from '@material-ui/core/CssBaseline'; +import { useSelector } from 'react-redux'; +import { LinearProgress } from '@material-ui/core'; import MainPage from './MainPage'; import RouteReportPage from './reports/RouteReportPage'; import ServerPage from './admin/ServerPage'; @@ -16,8 +18,6 @@ import GroupPage from './settings/GroupPage'; import PositionPage from './PositionPage'; import EventReportPage from './reports/EventReportPage'; import ReplayPage from './reports/ReplayPage'; -import { useSelector } from 'react-redux'; -import { LinearProgress } from '@material-ui/core'; import TripReportPage from './reports/TripReportPage'; import StopReportPage from './reports/StopReportPage'; import SummaryReportPage from './reports/SummaryReportPage'; @@ -40,7 +40,7 @@ import GeofencesPage from './GeofencesPage'; import GeofencePage from './GeofencePage'; const App = () => { - const initialized = useSelector(state => !!state.session.server && !!state.session.user); + const initialized = useSelector((state) => !!state.session.server && !!state.session.user); return ( @@ -48,44 +48,44 @@ const App = () => { - - - + + + {!initialized ? () : ( - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + )} ); -} +}; export default App; diff --git a/modern/src/CachingController.js b/modern/src/CachingController.js index 3f808de2..d6e20060 100644 --- a/modern/src/CachingController.js +++ b/modern/src/CachingController.js @@ -1,10 +1,12 @@ -import { useDispatch, useSelector } from 'react-redux'; -import { connect } from 'react-redux'; -import { geofencesActions, groupsActions, driversActions, maintenancesActions } from './store'; +import { useDispatch, useSelector, connect } from 'react-redux'; + +import { + geofencesActions, groupsActions, driversActions, maintenancesActions, +} from './store'; import { useEffectAsync } from './reactHelper'; const CachingController = () => { - const authenticated = useSelector(state => !!state.session.user); + const authenticated = useSelector((state) => !!state.session.user); const dispatch = useDispatch(); useEffectAsync(async () => { @@ -23,8 +25,8 @@ const CachingController = () => { dispatch(groupsActions.update(await response.json())); } } - }, [authenticated]); - + }, [authenticated]); + useEffectAsync(async () => { if (authenticated) { const response = await fetch('/api/drivers'); @@ -33,7 +35,7 @@ const CachingController = () => { } } }, [authenticated]); - + useEffectAsync(async () => { if (authenticated) { const response = await fetch('/api/maintenance'); @@ -41,9 +43,9 @@ const CachingController = () => { dispatch(maintenancesActions.update(await response.json())); } } - }, [authenticated]); - + }, [authenticated]); + return null; -} +}; export default connect()(CachingController); diff --git a/modern/src/DevicePage.js b/modern/src/DevicePage.js index ed5a6cba..699e97c6 100644 --- a/modern/src/DevicePage.js +++ b/modern/src/DevicePage.js @@ -1,14 +1,16 @@ import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from './common/localization'; import EditItemView from './EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import EditAttributesView from './attributes/EditAttributesView'; import deviceAttributes from './attributes/deviceAttributes'; import SelectField from './form/SelectField'; -import { deviceCategories } from './common/deviceCategories'; +import deviceCategories from './common/deviceCategories'; import LinkField from './form/LinkField'; import { prefixString } from './common/stringUtils'; @@ -25,7 +27,8 @@ const DevicePage = () => { return ( - {item && + {item + && ( <> }> @@ -37,15 +40,17 @@ const DevicePage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> setItem({...item, uniqueId: event.target.value})} + onChange={(event) => setItem({ ...item, uniqueId: event.target.value })} label={t('deviceIdentifier')} - variant="filled" /> + variant="filled" + /> @@ -58,42 +63,48 @@ const DevicePage = () => { setItem({...item, groupId: Number(event.target.value)})} + onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })} endpoint="/api/groups" label={t('groupParent')} - variant="filled" /> + variant="filled" + /> setItem({...item, phone: event.target.value})} + onChange={(event) => setItem({ ...item, phone: event.target.value })} label={t('sharedPhone')} - variant="filled" /> + variant="filled" + /> setItem({...item, model: event.target.value})} + onChange={(event) => setItem({ ...item, model: event.target.value })} label={t('deviceModel')} - variant="filled" /> + variant="filled" + /> setItem({...item, contact: event.target.value})} + onChange={(event) => setItem({ ...item, contact: event.target.value })} label={t('deviceContact')} - variant="filled" /> + variant="filled" + /> setItem({...item, category: event.target.value})} - data={deviceCategories.map(category => ({ + onChange={(event) => setItem({ ...item, category: event.target.value })} + data={deviceCategories.map((category) => ({ id: category, - name: t(`category${category.replace(/^\w/, c => c.toUpperCase())}`) + name: t(`category${category.replace(/^\w/, (c) => c.toUpperCase())}`), }))} label={t('deviceCategory')} - variant="filled" /> + variant="filled" + /> setItem({...item, disabled: event.target.checked})} />} - label={t('sharedDisabled')} /> + control={ setItem({ ...item, disabled: event.target.checked })} />} + label={t('sharedDisabled')} + /> @@ -105,12 +116,13 @@ const DevicePage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={deviceAttributes} - /> + /> - {item.id && + {item.id + && ( }> @@ -121,57 +133,62 @@ const DevicePage = () => { + variant="filled" + /> t(prefixString('event', it.type))} + titleGetter={(it) => t(prefixString('event', it.type))} label={t('sharedNotifications')} - variant="filled" /> + variant="filled" + /> + variant="filled" + /> it.description} + titleGetter={(it) => it.description} label={t('sharedComputedAttributes')} - variant="filled" /> + variant="filled" + /> + variant="filled" + /> - } + )} - } + )} ); -} +}; export default DevicePage; diff --git a/modern/src/DevicesList.js b/modern/src/DevicesList.js index f66d717d..85b936ce 100644 --- a/modern/src/DevicesList.js +++ b/modern/src/DevicesList.js @@ -34,7 +34,7 @@ const DeviceRow = ({ data, index, style }) => { const { items, onMenuClick } = data; const item = items[index]; - + return (
@@ -61,7 +61,7 @@ const DeviceView = ({ updateTimestamp, onMenuClick }) => { const classes = useStyles(); const dispatch = useDispatch(); - const items = useSelector(state => Object.values(state.devices.items)); + const items = useSelector((state) => Object.values(state.devices.items)); useEffectAsync(async () => { const response = await fetch('/api/devices'); @@ -79,7 +79,8 @@ const DeviceView = ({ updateTimestamp, onMenuClick }) => { height={height} itemCount={items.length} itemData={{ items, onMenuClick }} - itemSize={72 + 1} > + itemSize={72 + 1} + > {DeviceRow} @@ -88,10 +89,8 @@ const DeviceView = ({ updateTimestamp, onMenuClick }) => { ); }; -const DevicesList = () => { - return ( - - ); -}; +const DevicesList = () => ( + +); export default DevicesList; diff --git a/modern/src/EditCollectionView.js b/modern/src/EditCollectionView.js index 572a1d19..7d5e4b27 100644 --- a/modern/src/EditCollectionView.js +++ b/modern/src/EditCollectionView.js @@ -10,7 +10,7 @@ import { useSelector } from 'react-redux'; import t from './common/localization'; import RemoveDialog from './RemoveDialog'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ fab: { position: 'absolute', bottom: theme.spacing(2), @@ -18,7 +18,9 @@ const useStyles = makeStyles(theme => ({ }, })); -const EditCollectionView = ({ content, editPath, endpoint, disableAdd }) => { +const EditCollectionView = ({ + content, editPath, endpoint, disableAdd, +}) => { const classes = useStyles(); const history = useHistory(); @@ -26,47 +28,48 @@ const EditCollectionView = ({ content, editPath, endpoint, disableAdd }) => { const [selectedAnchorEl, setSelectedAnchorEl] = useState(null); const [removeDialogShown, setRemoveDialogShown] = useState(false); const [updateTimestamp, setUpdateTimestamp] = useState(Date.now()); - const adminEnabled = useSelector(state => state.session.user && state.session.user.administrator); + const adminEnabled = useSelector((state) => state.session.user && state.session.user.administrator); 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 = () => { setRemoveDialogShown(false); setUpdateTimestamp(Date.now()); - } + }; const Content = content; return ( <> - {adminEnabled && !disableAdd && + {adminEnabled && !disableAdd + && ( - } + )} {t('sharedEdit')} {t('sharedRemove')} @@ -74,6 +77,6 @@ const EditCollectionView = ({ content, editPath, endpoint, disableAdd }) => { ); -} +}; export default EditCollectionView; diff --git a/modern/src/EditItemView.js b/modern/src/EditItemView.js index 16fbbaee..9bf91a6a 100644 --- a/modern/src/EditItemView.js +++ b/modern/src/EditItemView.js @@ -1,15 +1,15 @@ import React from 'react'; -import MainToolbar from './MainToolbar'; 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 MainToolbar from './MainToolbar'; import t from './common/localization'; import { useEffectAsync } from './reactHelper'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ container: { marginTop: theme.spacing(2), }, @@ -22,7 +22,9 @@ const useStyles = makeStyles(theme => ({ }, })); -const EditItemView = ({ children, endpoint, item, setItem }) => { +const EditItemView = ({ + children, endpoint, item, setItem, +}) => { const history = useHistory(); const classes = useStyles(); const { id } = useParams(); @@ -58,14 +60,14 @@ const EditItemView = ({ children, endpoint, item, setItem }) => { return ( <> - + {children} - +
- -
@@ -73,6 +75,6 @@ const EditItemView = ({ children, endpoint, item, setItem }) => {
); -} +}; export default EditItemView; diff --git a/modern/src/GeofencePage.js b/modern/src/GeofencePage.js index 6c5db9bb..06456fca 100644 --- a/modern/src/GeofencePage.js +++ b/modern/src/GeofencePage.js @@ -1,10 +1,12 @@ import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from './common/localization'; import EditItemView from './EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import EditAttributesView from './attributes/EditAttributesView'; import geofenceAttributes from './attributes/geofenceAttributes'; @@ -21,7 +23,8 @@ const GeofencePage = () => { return ( - {item && + {item + && ( <> }> @@ -33,9 +36,10 @@ const GeofencePage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> @@ -47,15 +51,15 @@ const GeofencePage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={geofenceAttributes} - /> + /> - } + )} ); -} +}; export default GeofencePage; diff --git a/modern/src/GeofencesList.js b/modern/src/GeofencesList.js index 2988bef1..572ac5b1 100644 --- a/modern/src/GeofencesList.js +++ b/modern/src/GeofencesList.js @@ -28,7 +28,7 @@ const GeofenceView = ({ onMenuClick }) => { const classes = useStyles(); const dispatch = useDispatch(); - const items = useSelector(state => Object.values(state.geofences.items)); + const items = useSelector((state) => Object.values(state.geofences.items)); return ( @@ -47,12 +47,10 @@ const GeofenceView = ({ onMenuClick }) => { ))} ); -} +}; -const GeofencesList = () => { - return ( - - ); -} +const GeofencesList = () => ( + +); export default GeofencesList; diff --git a/modern/src/GeofencesPage.js b/modern/src/GeofencesPage.js index 389ac998..95c7151e 100644 --- a/modern/src/GeofencesPage.js +++ b/modern/src/GeofencesPage.js @@ -8,7 +8,7 @@ import CurrentLocationMap from './map/CurrentLocationMap'; import GeofenceEditMap from './map/GeofenceEditMap'; import GeofencesList from './GeofencesList'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { height: '100%', display: 'flex', @@ -21,7 +21,7 @@ const useStyles = makeStyles(theme => ({ flexDirection: 'row', [theme.breakpoints.down('xs')]: { flexDirection: 'column-reverse', - } + }, }, drawerPaper: { position: 'relative', @@ -30,7 +30,7 @@ const useStyles = makeStyles(theme => ({ }, [theme.breakpoints.down('xs')]: { height: 250, - } + }, }, mapContainer: { flexGrow: 1, @@ -46,8 +46,9 @@ const GeofencesPage = ({ width }) => {
+ variant="permanent" + classes={{ paper: classes.drawerPaper }} + >
@@ -61,6 +62,6 @@ const GeofencesPage = ({ width }) => {
); -} +}; export default withWidth()(GeofencesPage); diff --git a/modern/src/Logo.js b/modern/src/Logo.js index bea14d8c..1fb1ac8f 100644 --- a/modern/src/Logo.js +++ b/modern/src/Logo.js @@ -1,31 +1,28 @@ import React from 'react'; -const Logo = ({ fill }) => { - - return ( - - - - - - - - - - +const Logo = ({ fill }) => ( + + + + + + + + + + - - - - - - - - + + + + + + + - - - ) -} + + + +); export default Logo; diff --git a/modern/src/MainPage.js b/modern/src/MainPage.js index 8d0b18d2..88608df7 100644 --- a/modern/src/MainPage.js +++ b/modern/src/MainPage.js @@ -11,7 +11,7 @@ import GeofenceMap from './map/GeofenceMap'; import CurrentPositionsMap from './map/CurrentPositionsMap'; import CurrentLocationMap from './map/CurrentLocationMap'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { height: '100%', display: 'flex', @@ -24,7 +24,7 @@ const useStyles = makeStyles(theme => ({ flexDirection: 'row', [theme.breakpoints.down('xs')]: { flexDirection: 'column-reverse', - } + }, }, drawerPaper: { position: 'relative', @@ -50,8 +50,9 @@ const MainPage = ({ width }) => {
+ variant="permanent" + classes={{ paper: classes.drawerPaper }} + >
@@ -68,6 +69,6 @@ const MainPage = ({ width }) => {
); -} +}; export default withWidth()(MainPage); diff --git a/modern/src/MainToolbar.js b/modern/src/MainToolbar.js index 064507fb..da7c42c5 100644 --- a/modern/src/MainToolbar.js +++ b/modern/src/MainToolbar.js @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; import { useDispatch, useSelector } from 'react-redux'; -import { sessionActions } from './store'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; @@ -27,17 +26,18 @@ import FolderIcon from '@material-ui/icons/Folder'; import CreateIcon from '@material-ui/icons/Create'; import ReplayIcon from '@material-ui/icons/Replay'; import BuildIcon from '@material-ui/icons/Build'; +import { sessionActions } from './store'; import t from './common/localization'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ flex: { - flexGrow: 1 + flexGrow: 1, }, appBar: { zIndex: theme.zIndex.drawer + 1, }, list: { - width: 250 + width: 250, }, menuButton: { marginLeft: -12, @@ -50,11 +50,11 @@ const MainToolbar = () => { const [drawer, setDrawer] = useState(false); const classes = useStyles(); const history = useHistory(); - const adminEnabled = useSelector(state => state.session.user && state.session.user.administrator); - const userId = useSelector(state => state.session.user && state.session.user.id); + const adminEnabled = useSelector((state) => state.session.user && state.session.user.administrator); + const userId = useSelector((state) => state.session.user && state.session.user.id); - const openDrawer = () => { setDrawer(true) } - const closeDrawer = () => { setDrawer(false) } + const openDrawer = () => { setDrawer(true); }; + const closeDrawer = () => { setDrawer(false); }; const handleLogout = async () => { const response = await fetch('/api/session', { method: 'DELETE' }); @@ -62,7 +62,7 @@ const MainToolbar = () => { dispatch(sessionActions.updateUser(null)); history.push('/login'); } - } + }; return ( <> @@ -71,7 +71,8 @@ const MainToolbar = () => { + onClick={openDrawer} + > @@ -86,7 +87,8 @@ const MainToolbar = () => { className={classes.list} role="button" onClick={closeDrawer} - onKeyDown={closeDrawer}> + onKeyDown={closeDrawer} + > history.push('/')}> @@ -105,15 +107,16 @@ const MainToolbar = () => { - + {t('settingsTitle')} - }> + )} + > history.push(`/user/${userId}`)}> @@ -161,11 +164,12 @@ const MainToolbar = () => { <> {t('userAdmin')} - }> + )} + > history.push('/admin/server')}> @@ -191,6 +195,6 @@ const MainToolbar = () => { ); -} +}; export default MainToolbar; diff --git a/modern/src/PositionPage.js b/modern/src/PositionPage.js index a91a7a15..ce4c4ac2 100644 --- a/modern/src/PositionPage.js +++ b/modern/src/PositionPage.js @@ -1,14 +1,16 @@ import React, { Fragment, useState } from 'react'; -import t from './common/localization'; -import { makeStyles, Typography, ListItem, ListItemText, ListItemSecondaryAction, List, Container, Paper, Divider } from '@material-ui/core'; +import { + makeStyles, Typography, ListItem, ListItemText, ListItemSecondaryAction, List, Container, Paper, Divider, +} from '@material-ui/core'; import { useParams } from 'react-router-dom'; +import t from './common/localization'; import { useEffectAsync } from './reactHelper'; import MainToolbar from './MainToolbar'; import { formatPosition } from './common/formatter'; import { prefixString } from './common/stringUtils'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { marginTop: theme.spacing(2), marginBottom: theme.spacing(2), @@ -26,7 +28,7 @@ const PositionPage = () => { if (id) { const response = await fetch(`/api/positions?id=${id}`, { headers: { - 'Accept': 'application/json' + Accept: 'application/json', }, }); if (response.ok) { @@ -38,28 +40,27 @@ const PositionPage = () => { } }, [id]); - const formatKey = (key) => { - return t(prefixString('position', key)) || `${t('sharedAttribute')} "${key}"`; - }; + const formatKey = (key) => t(prefixString('position', key)) || `${t('sharedAttribute')} "${key}"`; const attributesList = () => { - const combinedList = {...item, ...item.attributes}; - return Object.entries(combinedList).filter(([_, value]) => typeof value !== 'object'); - } + const combinedList = { ...item, ...item.attributes }; + return Object.entries(combinedList).filter(([, value]) => typeof value !== 'object'); + }; return ( <> - + - {item && + {item + && ( {attributesList().map(([key, value], index, list) => ( + /> {formatPosition(value, key)} @@ -70,11 +71,11 @@ const PositionPage = () => { ))} - } + )} ); -} +}; export default PositionPage; diff --git a/modern/src/RegisterDialog.js b/modern/src/RegisterDialog.js index c640c515..aafec66b 100644 --- a/modern/src/RegisterDialog.js +++ b/modern/src/RegisterDialog.js @@ -1,4 +1,3 @@ -import t from './common/localization' import React, { useState } from 'react'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; @@ -7,6 +6,7 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import TextField from '@material-ui/core/TextField'; import Snackbar from '@material-ui/core/Snackbar'; +import t from './common/localization'; const RegisterDialog = ({ showDialog, onResult }) => { const [name, setName] = useState(''); @@ -14,81 +14,81 @@ const RegisterDialog = ({ showDialog, onResult }) => { const [password, setPassword] = useState(''); const [snackbarOpen, setSnackbarOpen] = useState(false); - const submitDisabled = () => { - return !name || !/(.+)@(.+)\.(.{2,})/.test(email) || !password; - } + const submitDisabled = () => !name || !/(.+)@(.+)\.(.{2,})/.test(email) || !password; const handleRegister = async () => { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({name, email, password}) + body: JSON.stringify({ name, email, password }), }); if (response.ok) { showDialog = false; setSnackbarOpen(true); - } - } + } + }; if (snackbarOpen) { - return ( { onResult(true) }} - message={t('loginCreated')} /> + onClose={() => { onResult(true); }} + message={t('loginCreated')} + /> ); - - } else if (showDialog) { - + } if (showDialog) { return ( { onResult(false) }}> + open + onClose={() => { onResult(false); }} + > {t('loginRegister')} setName(event.target.value)} /> + onChange={(event) => setName(event.target.value)} + /> setEmail(event.target.value)} /> + autoComplete="email" + onChange={(event) => setEmail(event.target.value)} + /> setPassword(event.target.value)} /> + type="password" + autoComplete="current-password" + onChange={(event) => setPassword(event.target.value)} + /> - ) - + ); } + return null; }; export default RegisterDialog; diff --git a/modern/src/RemoveDialog.js b/modern/src/RemoveDialog.js index bbcfb226..8ff162c2 100644 --- a/modern/src/RemoveDialog.js +++ b/modern/src/RemoveDialog.js @@ -1,12 +1,14 @@ -import t from './common/localization' import React from 'react'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; +import t from './common/localization'; -const RemoveDialog = ({ open, endpoint, itemId, onResult }) => { +const RemoveDialog = ({ + open, endpoint, itemId, onResult, +}) => { const handleRemove = async () => { const response = await fetch(`/api/${endpoint}/${itemId}`, { method: 'DELETE' }); if (response.ok) { @@ -17,7 +19,8 @@ const RemoveDialog = ({ open, endpoint, itemId, onResult }) => { return ( { onResult(false) }}> + onClose={() => { onResult(false); }} + > {t('sharedRemoveConfirm')} diff --git a/modern/src/SocketController.js b/modern/src/SocketController.js index cda693a1..ae82d134 100644 --- a/modern/src/SocketController.js +++ b/modern/src/SocketController.js @@ -1,19 +1,19 @@ -import { useDispatch, useSelector } from 'react-redux'; -import { connect } from 'react-redux'; -import { positionsActions, devicesActions, sessionActions } from './store'; +import { useDispatch, useSelector, connect } from 'react-redux'; + import { useHistory } from 'react-router-dom'; +import { positionsActions, devicesActions, sessionActions } from './store'; import { useEffectAsync } from './reactHelper'; -const displayNotifications = events => { - if ("Notification" in window) { - if (Notification.permission === "granted") { - for (const event of events) { +const displayNotifications = (events) => { + if ('Notification' in window) { + if (Notification.permission === 'granted') { + events.forEach((event) => { const notification = new Notification(`Event: ${event.type}`); setTimeout(notification.close.bind(notification), 4 * 1000); - } - } else if (Notification.permission !== "denied") { - Notification.requestPermission(permission => { - if (permission === "granted") { + }); + } else if (Notification.permission !== 'denied') { + Notification.requestPermission((permission) => { + if (permission === 'granted') { displayNotifications(events); } }); @@ -24,11 +24,11 @@ const displayNotifications = events => { const SocketController = () => { const dispatch = useDispatch(); const history = useHistory(); - const authenticated = useSelector(state => !!state.session.user); + const authenticated = useSelector((state) => !!state.session.user); const connectSocket = () => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const socket = new WebSocket(protocol + '//' + window.location.host + '/api/socket'); + const socket = new WebSocket(`${protocol}//${window.location.host}/api/socket`); socket.onclose = () => { setTimeout(() => connectSocket(), 60 * 1000); @@ -46,7 +46,7 @@ const SocketController = () => { displayNotifications(data.events); } }; - } + }; useEffectAsync(async () => { const response = await fetch('/api/server'); @@ -73,6 +73,6 @@ const SocketController = () => { }, [authenticated]); return null; -} +}; export default connect()(SocketController); diff --git a/modern/src/StartPage.js b/modern/src/StartPage.js index 0a086179..3edc5b31 100644 --- a/modern/src/StartPage.js +++ b/modern/src/StartPage.js @@ -2,7 +2,7 @@ import React from 'react'; import { useMediaQuery, makeStyles, Paper } from '@material-ui/core'; import { useTheme } from '@material-ui/core/styles'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { display: 'flex', height: '100vh', @@ -23,20 +23,20 @@ const useStyles = makeStyles(theme => ({ }, }, paper: { - display:'flex', + display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', flex: 1, boxShadow: '-2px 0px 16px rgba(0, 0, 0, 0.25)', [theme.breakpoints.up('lg')]: { - padding: theme.spacing(0, 25, 0, 0) + padding: theme.spacing(0, 25, 0, 0), }, }, form: { maxWidth: theme.spacing(52), padding: theme.spacing(5), - width: "100%", + width: '100%', }, })); @@ -47,11 +47,12 @@ const StartPage = ({ children }) => { return (
- {!useMediaQuery(theme.breakpoints.down('md')) && + {!useMediaQuery(theme.breakpoints.down('md')) + && ( - + - } + )}
@@ -59,7 +60,7 @@ const StartPage = ({ children }) => {
- ) -} + ); +}; export default StartPage; diff --git a/modern/src/UserPage.js b/modern/src/UserPage.js index dfe8b982..6afbdf7e 100644 --- a/modern/src/UserPage.js +++ b/modern/src/UserPage.js @@ -1,11 +1,13 @@ import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from './common/localization'; import userAttributes from './attributes/userAttributes'; import EditItemView from './EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import EditAttributesView from './attributes/EditAttributesView'; import LinkField from './form/LinkField'; @@ -22,7 +24,8 @@ const UserPage = () => { return ( - {item && + {item + && ( <> }> @@ -34,21 +37,24 @@ const UserPage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> setItem({...item, email: event.target.value})} + onChange={(event) => setItem({ ...item, email: event.target.value })} label={t('userEmail')} - variant="filled" /> + variant="filled" + /> setItem({...item, password: event.target.value})} + onChange={(event) => setItem({ ...item, password: event.target.value })} label={t('userPassword')} - variant="filled" /> + variant="filled" + /> @@ -60,12 +66,13 @@ const UserPage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={userAttributes} - /> + /> - {item.id && + {item.id + && ( }> @@ -76,28 +83,30 @@ const UserPage = () => { + variant="filled" + /> + variant="filled" + /> - } + )} - } + )} ); -} +}; export default UserPage; diff --git a/modern/src/admin/ServerPage.js b/modern/src/admin/ServerPage.js index 43664e54..7decac1a 100644 --- a/modern/src/admin/ServerPage.js +++ b/modern/src/admin/ServerPage.js @@ -1,18 +1,20 @@ import React from 'react'; import TextField from '@material-ui/core/TextField'; -import t from '../common/localization'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Button, FormControl, Container, Checkbox, FormControlLabel } from '@material-ui/core'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, Button, FormControl, Container, Checkbox, FormControlLabel, +} from '@material-ui/core'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { useHistory } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; +import t from '../common/localization'; import MainToolbar from '../MainToolbar'; import { sessionActions } from '../store'; import EditAttributesView from '../attributes/EditAttributesView'; import deviceAttributes from '../attributes/deviceAttributes'; import userAttributes from '../attributes/userAttributes'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ container: { marginTop: theme.spacing(2), }, @@ -33,7 +35,7 @@ const ServerPage = () => { const dispatch = useDispatch(); const classes = useStyles(); - const item = useSelector(state => state.session.server); + const item = useSelector((state) => state.session.server); const setItem = (updatedItem) => dispatch(sessionActions.updateServer(updatedItem)); const handleSave = async () => { @@ -51,8 +53,9 @@ const ServerPage = () => { return ( <> - - {item && + + {item + && ( <> }> @@ -64,9 +67,10 @@ const ServerPage = () => { setItem({...item, announcement: event.target.value})} + onChange={(event) => setItem({ ...item, announcement: event.target.value })} label={t('serverAnnouncement')} - variant="filled" /> + variant="filled" + /> @@ -77,17 +81,21 @@ const ServerPage = () => { setItem({...item, registration: event.target.checked})} />} - label={t('serverRegistration')} /> + control={ setItem({ ...item, registration: event.target.checked })} />} + label={t('serverRegistration')} + /> setItem({...item, readonly: event.target.checked})} />} - label={t('serverReadonly')} /> + control={ setItem({ ...item, readonly: event.target.checked })} />} + label={t('serverReadonly')} + /> setItem({...item, deviceReadonly: event.target.checked})} />} - label={t('userDeviceReadonly')} /> + control={ setItem({ ...item, deviceReadonly: event.target.checked })} />} + label={t('userDeviceReadonly')} + /> setItem({...item, limitCommands: event.target.checked})} />} - label={t('userLimitCommands')} /> + control={ setItem({ ...item, limitCommands: event.target.checked })} />} + label={t('userLimitCommands')} + /> @@ -99,19 +107,19 @@ const ServerPage = () => { setItem({...item, attributes})} - definitions={{...userAttributes, ...deviceAttributes}} - /> + setAttributes={(attributes) => setItem({ ...item, attributes })} + definitions={{ ...userAttributes, ...deviceAttributes }} + /> - } - + )} +
- -
@@ -119,6 +127,6 @@ const ServerPage = () => {
); -} +}; export default ServerPage; diff --git a/modern/src/admin/StatisticsPage.js b/modern/src/admin/StatisticsPage.js index 1e440a46..131ab7e4 100644 --- a/modern/src/admin/StatisticsPage.js +++ b/modern/src/admin/StatisticsPage.js @@ -1,10 +1,11 @@ - import React, { useState } from 'react'; -import { FormControl, InputLabel,Select, MenuItem, TextField, Button, TableContainer, Table, TableRow, TableCell, TableHead, TableBody, Paper } from '@material-ui/core'; +import { + FormControl, InputLabel, Select, MenuItem, TextField, Button, TableContainer, Table, TableRow, TableCell, TableHead, TableBody, Paper, +} from '@material-ui/core'; +import moment from 'moment'; import t from '../common/localization'; import { formatDate } from '../common/formatter'; import ReportLayoutPage from '../reports/ReportLayoutPage'; -import moment from 'moment'; const Filter = ({ setItems }) => { const [period, setPeriod] = useState('today'); @@ -50,7 +51,7 @@ const Filter = ({ setItems }) => { if (response.ok) { setItems(await response.json()); } - } + }; return ( <> @@ -73,8 +74,9 @@ const Filter = ({ setItems }) => { label={t('reportFrom')} type="datetime-local" value={from.format(moment.HTML5_FMT.DATETIME_LOCAL)} - onChange={e => setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} - fullWidth /> + onChange={(e) => setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth + /> )} {period === 'custom' && ( { label={t('reportTo')} type="datetime-local" value={to.format(moment.HTML5_FMT.DATETIME_LOCAL)} - onChange={e => setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} - fullWidth /> + onChange={(e) => setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth + /> )} - - ) -} + + ); +}; const StatisticsPage = () => { - const [items, setItems] = useState([]); return ( @@ -125,7 +127,7 @@ const StatisticsPage = () => { {item.mailSent} {item.smsSent} {item.geocoderRequests} - {item.geolocationRequests} + {item.geolocationRequests} ))} @@ -133,6 +135,6 @@ const StatisticsPage = () => { ); -} +}; export default StatisticsPage; diff --git a/modern/src/admin/UsersPage.js b/modern/src/admin/UsersPage.js index 630bea43..fc6e6736 100644 --- a/modern/src/admin/UsersPage.js +++ b/modern/src/admin/UsersPage.js @@ -1,13 +1,15 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; import { formatBoolean } from '../common/formatter'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -28,43 +30,41 @@ const UsersView = ({ updateTimestamp, onMenuClick }) => { return ( - - - - - {t('sharedName')} - {t('userEmail')} - {t('userAdmin')} - {t('sharedDisabled')} - - - - {items.map((item) => ( - - - onMenuClick(event.currentTarget, item.id)}> - - - - {item.name} - {item.email} - {formatBoolean(item, 'administrator')} - {formatBoolean(item, 'disabled')} +
+ + + + {t('sharedName')} + {t('userEmail')} + {t('userAdmin')} + {t('sharedDisabled')} - ))} - -
+ + + {items.map((item) => ( + + + onMenuClick(event.currentTarget, item.id)}> + + + + {item.name} + {item.email} + {formatBoolean(item, 'administrator')} + {formatBoolean(item, 'disabled')} + + ))} + +
); -} +}; -const UsersPage = () => { - return ( - <> - - - - ); -} +const UsersPage = () => ( + <> + + + +); export default UsersPage; diff --git a/modern/src/attributes/AddAttributeDialog.js b/modern/src/attributes/AddAttributeDialog.js index ee4c48c1..24c208a5 100644 --- a/modern/src/attributes/AddAttributeDialog.js +++ b/modern/src/attributes/AddAttributeDialog.js @@ -1,12 +1,14 @@ import React, { useState } from 'react'; -import { Button, Dialog, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, TextField } from "@material-ui/core"; +import { + Button, Dialog, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, TextField, +} from '@material-ui/core'; -import t from '../common/localization'; import { Autocomplete, createFilterOptions } from '@material-ui/lab'; +import t from '../common/localization'; const AddAttributeDialog = ({ open, onResult, definitions }) => { const filter = createFilterOptions({ - stringify: option => option.name, + stringify: (option) => option.name, }); const options = Object.entries(definitions).map(([key, value]) => ({ @@ -39,10 +41,8 @@ const AddAttributeDialog = ({ open, onResult, definitions }) => { return filtered; }} options={options} - getOptionLabel={option => { - return option && typeof option === 'object' ? option.name : option; - }} - renderOption={option => option.name} + getOptionLabel={(option) => (option && typeof option === 'object' ? option.name : option)} + renderOption={(option) => option.name} freeSolo renderInput={(params) => ( @@ -52,14 +52,16 @@ const AddAttributeDialog = ({ open, onResult, definitions }) => { variant="filled" margin="normal" fullWidth - disabled={key in definitions}> + disabled={key in definitions} + > {t('sharedType')} @@ -67,17 +69,19 @@ const AddAttributeDialog = ({ open, onResult, definitions }) => {
- ) -} + ); +}; export default AddAttributeDialog; diff --git a/modern/src/attributes/EditAttributesView.js b/modern/src/attributes/EditAttributesView.js index 619d857c..e38c02ae 100644 --- a/modern/src/attributes/EditAttributesView.js +++ b/modern/src/attributes/EditAttributesView.js @@ -1,13 +1,14 @@ import React, { useState } from 'react'; -import t from '../common/localization'; - -import { Button, Checkbox, FilledInput, FormControl, FormControlLabel, Grid, IconButton, InputAdornment, InputLabel, makeStyles } from "@material-ui/core"; +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'; +import t from '../common/localization'; import AddAttributeDialog from './AddAttributeDialog'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ addButton: { marginTop: theme.spacing(2), marginBottom: theme.spacing(1), @@ -22,10 +23,36 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { const [addDialogShown, setAddDialogShown] = useState(false); + const updateAttribute = (key, value) => { + const updatedAttributes = { ...attributes }; + updatedAttributes[key] = value; + setAttributes(updatedAttributes); + }; + + const deleteAttribute = (key) => { + const updatedAttributes = { ...attributes }; + delete updatedAttributes[key]; + setAttributes(updatedAttributes); + }; + + const getAttributeName = (key) => { + const definition = definitions[key]; + return definition ? definition.name : key; + }; + + const getAttributeType = (value) => { + if (typeof value === 'number') { + return 'number'; + } if (typeof value === 'boolean') { + return 'boolean'; + } + return 'string'; + }; + const convertToList = (attributes) => { - let booleanList = []; - let otherList = []; - for (const key in attributes) { + const booleanList = []; + const otherList = []; + Object.keys(attributes).forEach((key) => { const value = attributes[key]; const type = getAttributeType(value); if (type === 'boolean') { @@ -33,14 +60,14 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { } else { otherList.push({ key, value, type }); } - } + }); return otherList.concat(booleanList); - } + }; const handleAddResult = (definition) => { setAddDialogShown(false); if (definition) { - switch(definition.type) { + switch (definition.type) { case 'number': updateAttribute(definition.key, 0); break; @@ -48,37 +75,10 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { updateAttribute(definition.key, false); break; default: - updateAttribute(definition.key, ""); + updateAttribute(definition.key, ''); break; } } - } - - const updateAttribute = (key, value) => { - let updatedAttributes = {...attributes}; - updatedAttributes[key] = value; - setAttributes(updatedAttributes); - }; - - const deleteAttribute = (key) => { - let updatedAttributes = {...attributes}; - delete updatedAttributes[key]; - setAttributes(updatedAttributes); - }; - - const getAttributeName = (key) => { - const definition = definitions[key]; - return definition ? definition.name : key; - }; - - const getAttributeType = (value) => { - if (typeof value === 'number') { - return 'number'; - } else if (typeof value === 'boolean') { - return 'boolean'; - } else { - return 'string'; - } }; return ( @@ -88,37 +88,37 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { return ( updateAttribute(key, e.target.checked)} - /> - } - label={getAttributeName(key)} /> + onChange={(e) => updateAttribute(key, e.target.checked)} + /> + )} + label={getAttributeName(key)} + /> deleteAttribute(key)}> ); - } else { - return ( - - {getAttributeName(key)} - updateAttribute(key, e.target.value)} - endAdornment={ - - deleteAttribute(key)}> - - - - } - /> - - ); } + return ( + + {getAttributeName(key)} + updateAttribute(key, e.target.value)} + endAdornment={( + + deleteAttribute(key)}> + + + + )} + /> + + ); })} + /> ); -} +}; export default EditAttributesView; diff --git a/modern/src/attributes/deviceAttributes.js b/modern/src/attributes/deviceAttributes.js index 891a225e..26e3d928 100644 --- a/modern/src/attributes/deviceAttributes.js +++ b/modern/src/attributes/deviceAttributes.js @@ -1,7 +1,7 @@ -import t from '../common/localization' +import t from '../common/localization'; export default { - 'speedLimit': { + speedLimit: { name: t('attributeSpeedLimit'), type: 'string', }, diff --git a/modern/src/attributes/geofenceAttributes.js b/modern/src/attributes/geofenceAttributes.js index 4b9c9e63..59a8869d 100644 --- a/modern/src/attributes/geofenceAttributes.js +++ b/modern/src/attributes/geofenceAttributes.js @@ -1,7 +1,7 @@ -import t from '../common/localization' +import t from '../common/localization'; export default { - 'speedLimit': { + speedLimit: { name: t('attributeSpeedLimit'), type: 'string', }, diff --git a/modern/src/attributes/positionAttributes.js b/modern/src/attributes/positionAttributes.js index b1efe3df..94f396a1 100644 --- a/modern/src/attributes/positionAttributes.js +++ b/modern/src/attributes/positionAttributes.js @@ -1,19 +1,19 @@ -import t from '../common/localization' +import t from '../common/localization'; export default { - 'raw': { + raw: { name: t('positionRaw'), type: 'string', }, - 'index': { + index: { name: t('positionIndex'), type: 'number', }, - 'ignition': { + ignition: { name: t('positionIgnition'), type: 'boolean', }, - 'odometer': { + odometer: { name: t('positionOdometer'), type: 'number', dataType: 'distance', diff --git a/modern/src/attributes/userAttributes.js b/modern/src/attributes/userAttributes.js index bcec29f2..6f842f91 100644 --- a/modern/src/attributes/userAttributes.js +++ b/modern/src/attributes/userAttributes.js @@ -1,7 +1,7 @@ -import t from '../common/localization' +import t from '../common/localization'; export default { - 'notificationTokens': { + notificationTokens: { name: t('attributeNotificationTokens'), type: 'string', }, @@ -49,19 +49,19 @@ export default { name: t('attributeUiHidePositionAttributes'), type: 'string', }, - 'distanceUnit': { + distanceUnit: { name: t('settingsDistanceUnit'), type: 'string', }, - 'speedUnit': { + speedUnit: { name: t('settingsSpeedUnit'), type: 'string', }, - 'volumeUnit': { + volumeUnit: { name: t('settingsVolumeUnit'), type: 'string', }, - 'timezone': { + timezone: { name: t('sharedTimezone'), type: 'string', }, diff --git a/modern/src/common/converter.js b/modern/src/common/converter.js index 9cdc6239..d45db5d6 100644 --- a/modern/src/common/converter.js +++ b/modern/src/common/converter.js @@ -1,4 +1,4 @@ -const speedConverter = unit => { +const speedConverter = (unit) => { switch (unit) { case 'kmh': return 1.852; @@ -10,7 +10,7 @@ const speedConverter = unit => { } }; -const distanceConverter = unit => { +const distanceConverter = (unit) => { switch (unit) { case 'mi': return 0.000621371; @@ -19,21 +19,13 @@ const distanceConverter = unit => { case 'km': default: return 0.001; - } -} + } +}; -export const speedFromKnots = (value, unit) => { - return value * speedConverter(unit); -} +export const speedFromKnots = (value, unit) => value * speedConverter(unit); -export const speedToKnots = (value, unit) => { - return value / speedConverter(unit); -} +export const speedToKnots = (value, unit) => value / speedConverter(unit); -export const distanceFromMeters = (value, unit) => { - return value * distanceConverter(unit); -} +export const distanceFromMeters = (value, unit) => value * distanceConverter(unit); -export const distanceToMeters = (value, unit) => { - return value / distanceConverter(unit); -} +export const distanceToMeters = (value, unit) => value / distanceConverter(unit); diff --git a/modern/src/common/deviceCategories.js b/modern/src/common/deviceCategories.js index 1eda1902..f5d749aa 100644 --- a/modern/src/common/deviceCategories.js +++ b/modern/src/common/deviceCategories.js @@ -1,4 +1,4 @@ -export const deviceCategories = [ +export default [ 'default', 'animal', 'bicycle', diff --git a/modern/src/common/formatter.js b/modern/src/common/formatter.js index 0d8ac4c9..3c0341b7 100644 --- a/modern/src/common/formatter.js +++ b/modern/src/common/formatter.js @@ -1,5 +1,11 @@ import moment from 'moment'; -import t from '../common/localization'; +import t from './localization'; + +export const formatBoolean = (value) => (value ? t('sharedYes') : t('sharedNo')); + +export const formatNumber = (value, precision = 1) => Number(value.toFixed(precision)); + +export const formatDate = (value, format = 'YYYY-MM-DD HH:mm') => moment(value).format(format); export const formatPosition = (value, key) => { if (value != null && typeof value === 'object') { @@ -18,30 +24,17 @@ export const formatPosition = (value, key) => { case 'course': return value.toFixed(1); case 'batteryLevel': - return value + '%'; + return `${value}%`; default: if (typeof value === 'number') { return formatNumber(value); - } else if (typeof value === 'boolean') { + } if (typeof value === 'boolean') { return formatBoolean(value); - } else { - return value; } + return value; } }; -export const formatBoolean = (value) => { - return value ? t('sharedYes') : t('sharedNo'); -}; - -export const formatNumber = (value, precision = 1) => { - return Number(value.toFixed(precision)); -}; - -export const formatDate = (value, format = 'YYYY-MM-DD HH:mm') => { - return moment(value).format(format); -}; - export const formatDistance = (value, unit) => { switch (unit) { case 'mi': @@ -50,7 +43,7 @@ export const formatDistance = (value, unit) => { return `${(value * 0.000539957).toFixed(2)} ${t('sharedNmi')}`; case 'km': default: - return `${(value * 0.001).toFixed(2)} ${t('sharedKm')}`; + return `${(value * 0.001).toFixed(2)} ${t('sharedKm')}`; } }; @@ -62,8 +55,8 @@ export const formatSpeed = (value, unit) => { return `${(value * 1.15078).toFixed(2)} ${t('sharedMph')}`; case 'kn': default: - return `${(value * 1).toFixed(2)} ${t('sharedKn')}`; - } + return `${(value * 1).toFixed(2)} ${t('sharedKn')}`; + } }; export const formatVolume = (value, unit) => { @@ -74,16 +67,15 @@ export const formatVolume = (value, unit) => { return `${(value / 3.785).toFixed(2)} ${t('sharedGallonAbbreviation')}`; case 'ltr': default: - return `${(value / 1).toFixed(2)} ${t('sharedLiterAbbreviation')}`; - } -} - -export const formatHours = (value) => { - return moment.duration(value).humanize(); + return `${(value / 1).toFixed(2)} ${t('sharedLiterAbbreviation')}`; + } }; +export const formatHours = (value) => moment.duration(value).humanize(); + export const formatCoordinate = (key, value, unit) => { - var hemisphere, degrees, minutes, seconds; + let hemisphere; let degrees; let minutes; let + seconds; if (key === 'latitude') { hemisphere = value >= 0 ? 'N' : 'S'; } else { @@ -95,14 +87,14 @@ export const formatCoordinate = (key, value, unit) => { value = Math.abs(value); degrees = Math.floor(value); minutes = (value - degrees) * 60; - return degrees + '° ' + minutes.toFixed(6) + '\' ' + hemisphere; + return `${degrees}° ${minutes.toFixed(6)}' ${hemisphere}`; case 'dms': value = Math.abs(value); degrees = Math.floor(value); minutes = Math.floor((value - degrees) * 60); seconds = Math.round((value - degrees - minutes / 60) * 3600); - return degrees + '° ' + minutes + '\' ' + seconds + '" ' + hemisphere; + return `${degrees}° ${minutes}' ${seconds}" ${hemisphere}`; default: - return value.toFixed(6) + '°'; - } + return `${value.toFixed(6)}°`; + } }; diff --git a/modern/src/common/localization.js b/modern/src/common/localization.js index cb1420bb..9e2123a9 100644 --- a/modern/src/common/localization.js +++ b/modern/src/common/localization.js @@ -54,60 +54,60 @@ import zh from '../../../web/l10n/zh.json'; import zh_TW from '../../../web/l10n/zh_TW.json'; const supportedLanguages = { - 'af': { data: af, name: 'Afrikaans' }, - 'ar': { data: ar, name: 'العربية' }, - 'az': { data: az, name: 'Azərbaycanca' }, - 'bg': { data: bg, name: 'Български' }, - 'bn': { data: bn, name: 'বাংলা' }, - 'cs': { data: cs, name: 'Čeština' }, - 'de': { data: de, name: 'Deutsch' }, - 'da': { data: da, name: 'Dansk' }, - 'el': { data: el, name: 'Ελληνικά' }, - 'en': { data: en, name: 'English' }, - 'es': { data: es, name: 'Español' }, - 'fa': { data: fa, name: 'فارسی' }, - 'fi': { data: fi, name: 'Suomi' }, - 'fr': { data: fr, name: 'Français' }, - 'he': { data: he, name: 'עברית' }, - 'hi': { data: hi, name: 'हिन्दी' }, - 'hr': { data: hr, name: 'Hrvatski' }, - 'hu': { data: hu, name: 'Magyar' }, - 'id': { data: id, name: 'Bahasa Indonesia' }, - 'it': { data: it, name: 'Italiano' }, - 'ja': { data: ja, name: '日本語' }, - 'ka': { data: ka, name: 'ქართული' }, - 'kk': { data: kk, name: 'Қазақша' }, - 'ko': { data: ko, name: '한국어' }, - 'km': { data: km, name: 'ភាសាខ្មែរ' }, - 'lo': { data: lo, name: 'ລາວ' }, - 'lt': { data: lt, name: 'Lietuvių' }, - 'lv': { data: lv, name: 'Latviešu' }, - 'ml': { data: ml, name: 'മലയാളം' }, - 'mn': { data: mn, name: 'Монгол хэл' }, - 'ms': { data: ms, name: 'بهاس ملايو' }, - 'nb': { data: nb, name: 'Norsk bokmål' }, - 'ne': { data: ne, name: 'नेपाली' }, - 'nl': { data: nl, name: 'Nederlands' }, - 'nn': { data: nn, name: 'Norsk nynorsk' }, - 'pl': { data: pl, name: 'Polski' }, - 'pt': { data: pt, name: 'Português' }, - 'pt_BR': { data: pt_BR, name: 'Português (Brasil)' }, - 'ro': { data: ro, name: 'Română' }, - 'ru': { data: ru, name: 'Русский' }, - 'si': { data: si, name: 'සිංහල' }, - 'sk': { data: sk, name: 'Slovenčina' }, - 'sl': { data: sl, name: 'Slovenščina' }, - 'sq': { data: sq, name: 'Shqipëria' }, - 'sr': { data: sr, name: 'Srpski' }, - 'sv': { data: sv, name: 'Svenska' }, - 'ta': { data: ta, name: 'தமிழ்' }, - 'th': { data: th, name: 'ไทย' }, - 'tr': { data: tr, name: 'Türkçe' }, - 'uk': { data: uk, name: 'Українська' }, - 'uz': { data: uz, name: 'Oʻzbekcha' }, - 'vi': { data: vi, name: 'Tiếng Việt' }, - 'zh': { data: zh, name: '中文' }, - 'zh_TW': { data: zh_TW, name: '中文 (Taiwan)' } + af: { data: af, name: 'Afrikaans' }, + ar: { data: ar, name: 'العربية' }, + az: { data: az, name: 'Azərbaycanca' }, + bg: { data: bg, name: 'Български' }, + bn: { data: bn, name: 'বাংলা' }, + cs: { data: cs, name: 'Čeština' }, + de: { data: de, name: 'Deutsch' }, + da: { data: da, name: 'Dansk' }, + el: { data: el, name: 'Ελληνικά' }, + en: { data: en, name: 'English' }, + es: { data: es, name: 'Español' }, + fa: { data: fa, name: 'فارسی' }, + fi: { data: fi, name: 'Suomi' }, + fr: { data: fr, name: 'Français' }, + he: { data: he, name: 'עברית' }, + hi: { data: hi, name: 'हिन्दी' }, + hr: { data: hr, name: 'Hrvatski' }, + hu: { data: hu, name: 'Magyar' }, + id: { data: id, name: 'Bahasa Indonesia' }, + it: { data: it, name: 'Italiano' }, + ja: { data: ja, name: '日本語' }, + ka: { data: ka, name: 'ქართული' }, + kk: { data: kk, name: 'Қазақша' }, + ko: { data: ko, name: '한국어' }, + km: { data: km, name: 'ភាសាខ្មែរ' }, + lo: { data: lo, name: 'ລາວ' }, + lt: { data: lt, name: 'Lietuvių' }, + lv: { data: lv, name: 'Latviešu' }, + ml: { data: ml, name: 'മലയാളം' }, + mn: { data: mn, name: 'Монгол хэл' }, + ms: { data: ms, name: 'بهاس ملايو' }, + nb: { data: nb, name: 'Norsk bokmål' }, + ne: { data: ne, name: 'नेपाली' }, + nl: { data: nl, name: 'Nederlands' }, + nn: { data: nn, name: 'Norsk nynorsk' }, + pl: { data: pl, name: 'Polski' }, + pt: { data: pt, name: 'Português' }, + pt_BR: { data: pt_BR, name: 'Português (Brasil)' }, + ro: { data: ro, name: 'Română' }, + ru: { data: ru, name: 'Русский' }, + si: { data: si, name: 'සිංහල' }, + sk: { data: sk, name: 'Slovenčina' }, + sl: { data: sl, name: 'Slovenščina' }, + sq: { data: sq, name: 'Shqipëria' }, + sr: { data: sr, name: 'Srpski' }, + sv: { data: sv, name: 'Svenska' }, + ta: { data: ta, name: 'தமிழ்' }, + th: { data: th, name: 'ไทย' }, + tr: { data: tr, name: 'Türkçe' }, + uk: { data: uk, name: 'Українська' }, + uz: { data: uz, name: 'Oʻzbekcha' }, + vi: { data: vi, name: 'Tiếng Việt' }, + zh: { data: zh, name: '中文' }, + zh_TW: { data: zh_TW, name: '中文 (Taiwan)' }, }; const languages = window.navigator.languages !== undefined ? window.navigator.languages.slice() : []; @@ -130,10 +130,6 @@ for (let i = 0; i < languages.length; i++) { const selectedLanguage = supportedLanguages[language]; -export const findStringKeys = (predicate) => { - return Object.keys(selectedLanguage.data).filter(predicate); -} +export const findStringKeys = (predicate) => Object.keys(selectedLanguage.data).filter(predicate); -export default key => { - return selectedLanguage.data[key]; -}; +export default (key) => selectedLanguage.data[key]; diff --git a/modern/src/common/preferences.js b/modern/src/common/preferences.js index 24fe389a..aba3c82c 100644 --- a/modern/src/common/preferences.js +++ b/modern/src/common/preferences.js @@ -1,21 +1,15 @@ import { useSelector } from 'react-redux'; -export const usePreference = (key, defaultValue) => { - return useSelector(state => { - if (state.session.server.forceSettings) { - return state.session.server[key] || state.session.user[key] || defaultValue; - } else { - return state.session.user[key] || state.session.server[key] || defaultValue; - } - }); -}; +export const usePreference = (key, defaultValue) => useSelector((state) => { + if (state.session.server.forceSettings) { + return state.session.server[key] || state.session.user[key] || defaultValue; + } + return state.session.user[key] || state.session.server[key] || defaultValue; +}); -export const useAttributePreference = (key, defaultValue) => { - return useSelector(state => { - if (state.session.server.forceSettings) { - return state.session.server.attributes[key] || state.session.user.attributes[key] || defaultValue; - } else { - return state.session.user.attributes[key] || state.session.server.attributes[key] || defaultValue; - } - }); -}; +export const useAttributePreference = (key, defaultValue) => useSelector((state) => { + if (state.session.server.forceSettings) { + return state.session.server.attributes[key] || state.session.user.attributes[key] || defaultValue; + } + return state.session.user.attributes[key] || state.session.server.attributes[key] || defaultValue; +}); diff --git a/modern/src/common/stringUtils.js b/modern/src/common/stringUtils.js index 7bd68c85..fc997fe0 100644 --- a/modern/src/common/stringUtils.js +++ b/modern/src/common/stringUtils.js @@ -1,7 +1,3 @@ -export const prefixString = (prefix, value) => { - return prefix + value.charAt(0).toUpperCase() + value.slice(1); -} +export const prefixString = (prefix, value) => prefix + value.charAt(0).toUpperCase() + value.slice(1); -export const unprefixString = (prefix, value) => { - return value.charAt(prefix.length).toLowerCase() + value.slice(prefix.length + 1); -} +export const unprefixString = (prefix, value) => value.charAt(prefix.length).toLowerCase() + value.slice(prefix.length + 1); diff --git a/modern/src/components/registration/LoginForm.js b/modern/src/components/registration/LoginForm.js index 382b4fdc..e083541c 100644 --- a/modern/src/components/registration/LoginForm.js +++ b/modern/src/components/registration/LoginForm.js @@ -1,24 +1,25 @@ import React, { useState } from 'react'; -import { Grid, useMediaQuery, makeStyles, InputLabel, Select, MenuItem, FormControl, Button, TextField, Link } from '@material-ui/core'; +import { + Grid, useMediaQuery, makeStyles, InputLabel, Select, MenuItem, FormControl, Button, TextField, Link, +} from '@material-ui/core'; import { useTheme } from '@material-ui/core/styles'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { sessionActions } from '../../store'; import t from '../../common/localization'; -import StartPage from './../../StartPage'; +import StartPage from '../../StartPage'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ logoContainer: { textAlign: 'center', color: theme.palette.primary.main, }, resetPassword: { cursor: 'pointer', - } + }, })); const LoginForm = () => { - const classes = useStyles(); const dispatch = useDispatch(); const history = useHistory(); @@ -27,16 +28,16 @@ const LoginForm = () => { const [failed, setFailed] = useState(false); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const registrationEnabled = useSelector(state => state.session.server ? state.session.server['registration'] : false); - const emailEnabled = useSelector(state => state.session.server ? state.session.server['emailEnabled'] : false); + const registrationEnabled = useSelector((state) => (state.session.server ? state.session.server.registration : false)); + const emailEnabled = useSelector((state) => (state.session.server ? state.session.server.emailEnabled : false)); const handleEmailChange = (event) => { setEmail(event.target.value); - } + }; const handlePasswordChange = (event) => { setPassword(event.target.value); - } + }; const handleLogin = async (event) => { event.preventDefault(); @@ -49,38 +50,40 @@ const LoginForm = () => { setFailed(true); setPassword(''); } - } + }; - const handleSpecialKey = e => { + const handleSpecialKey = (e) => { if (e.keyCode === 13 && email && password) { handleLogin(e); } - } + }; return ( - - {useMediaQuery(theme.breakpoints.down('md')) && + + {useMediaQuery(theme.breakpoints.down('md')) + && ( - + - } + )} + variant="filled" + /> { fullWidth error={failed} label={t('userPassword')} - name='password' + name="password" value={password} - type='password' - autoComplete='current-password' + type="password" + autoComplete="current-password" onChange={handlePasswordChange} onKeyUp={handleSpecialKey} - variant='filled' /> + variant="filled" + /> - @@ -122,14 +127,16 @@ const LoginForm = () => { - {emailEnabled && + {emailEnabled && ( + - history.push('/reset-password')} className={classes.resetPassword} underline="none">{t('loginReset')} + history.push('/reset-password')} className={classes.resetPassword} underline="none">{t('loginReset')} - } + + )} - ) -} + ); +}; export default LoginForm; diff --git a/modern/src/components/registration/RegisterForm.js b/modern/src/components/registration/RegisterForm.js index b2a8222a..06f53551 100644 --- a/modern/src/components/registration/RegisterForm.js +++ b/modern/src/components/registration/RegisterForm.js @@ -1,27 +1,28 @@ import React, { useState } from 'react'; -import { Grid, Button, TextField, Typography, Link, makeStyles, Snackbar } from '@material-ui/core'; +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'; -import StartPage from './../../StartPage'; -import t from './../../common/localization'; +import StartPage from '../../StartPage'; +import t from '../../common/localization'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ register: { fontSize: theme.spacing(3), fontWeight: 500, marginLeft: theme.spacing(2), - textTransform: "uppercase" + textTransform: 'uppercase', }, link: { fontSize: theme.spacing(3), fontWeight: 500, marginTop: theme.spacing(0.5), - cursor: 'pointer' - } + cursor: 'pointer', + }, })); const RegisterForm = () => { - const classes = useStyles(); const history = useHistory(); @@ -30,21 +31,19 @@ const RegisterForm = () => { const [password, setPassword] = useState(''); const [snackbarOpen, setSnackbarOpen] = useState(false); - const submitDisabled = () => { - return !name || !/(.+)@(.+)\.(.{2,})/.test(email) || !password; - } + const submitDisabled = () => !name || !/(.+)@(.+)\.(.{2,})/.test(email) || !password; const handleRegister = async () => { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({name, email, password}) + body: JSON.stringify({ name, email, password }), }); if (response.ok) { setSnackbarOpen(true); - } - } + } + }; return ( @@ -53,18 +52,19 @@ const RegisterForm = () => { open={snackbarOpen} onClose={() => history.push('/login')} autoHideDuration={6000} - message={t('loginCreated')} /> - + message={t('loginCreated')} + /> + - + history.push('/login')}> - + {t('loginRegister')} @@ -74,50 +74,54 @@ const RegisterForm = () => { required fullWidth label={t('sharedName')} - name='name' + name="name" value={name || ''} - autoComplete='name' + autoComplete="name" autoFocus - onChange={event => setName(event.target.value)} - variant='filled' /> + onChange={(event) => setName(event.target.value)} + variant="filled" + /> setEmail(event.target.value)} - variant='filled' /> + autoComplete="email" + onChange={(event) => setEmail(event.target.value)} + variant="filled" + /> setPassword(event.target.value)} - variant='filled' /> + type="password" + autoComplete="current-password" + onChange={(event) => setPassword(event.target.value)} + variant="filled" + /> - ) -} + ); +}; export default RegisterForm; diff --git a/modern/src/components/registration/ResetPasswordForm.js b/modern/src/components/registration/ResetPasswordForm.js index c268f808..f4dd2e4d 100644 --- a/modern/src/components/registration/ResetPasswordForm.js +++ b/modern/src/components/registration/ResetPasswordForm.js @@ -1,12 +1,9 @@ import React from 'react'; -const ResetPasswordForm = () => { - - return ( - <> -
Reset Password Comming Soon!!!
- - ) -} +const ResetPasswordForm = () => ( + <> +
Reset Password Comming Soon!!!
+ +); export default ResetPasswordForm; diff --git a/modern/src/components/reports/ReportNavbar.js b/modern/src/components/reports/ReportNavbar.js index ac01fad9..16807e89 100644 --- a/modern/src/components/reports/ReportNavbar.js +++ b/modern/src/components/reports/ReportNavbar.js @@ -1,26 +1,28 @@ import React from 'react'; -import { AppBar, Toolbar, Typography, IconButton } from '@material-ui/core'; +import { + AppBar, Toolbar, Typography, IconButton, +} from '@material-ui/core'; import MenuIcon from '@material-ui/icons/Menu'; import t from '../../common/localization'; -const ReportNavbar = ({ setOpenDrawer, reportTitle }) => { - - return ( - - - setOpenDrawer(true)}> - - - - {t('reportTitle')} {` / ${reportTitle}`} - - - - ) -} +const ReportNavbar = ({ setOpenDrawer, reportTitle }) => ( + + + setOpenDrawer(true)} + > + + + + {t('reportTitle')} + {' '} + {` / ${reportTitle}`} + + + +); export default ReportNavbar; diff --git a/modern/src/components/reports/ReportSidebar.js b/modern/src/components/reports/ReportSidebar.js index 90e20c05..686fc040 100644 --- a/modern/src/components/reports/ReportSidebar.js +++ b/modern/src/components/reports/ReportSidebar.js @@ -1,21 +1,23 @@ import React from 'react'; -import { List, ListItem, ListItemText, ListItemIcon } from '@material-ui/core'; +import { + List, ListItem, ListItemText, ListItemIcon, +} from '@material-ui/core'; import { Link, useLocation } from 'react-router-dom'; const ReportSidebar = ({ routes }) => { - const location = useLocation(); return ( - - {routes.map((route, index) => ( + + {routes.map((route) => ( + selected={route.href === location.pathname} + > {route.icon} @@ -23,7 +25,7 @@ const ReportSidebar = ({ routes }) => { ))} - ) -} + ); +}; export default ReportSidebar; diff --git a/modern/src/form/LinkField.js b/modern/src/form/LinkField.js index b228fb34..81467a1b 100644 --- a/modern/src/form/LinkField.js +++ b/modern/src/form/LinkField.js @@ -1,4 +1,6 @@ -import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; +import { + FormControl, InputLabel, MenuItem, Select, +} from '@material-ui/core'; import React, { useState } from 'react'; import { useEffectAsync } from '../reactHelper'; @@ -11,8 +13,8 @@ const LinkField = ({ baseId, keyBase, keyLink, - keyGetter = item => item.id, - titleGetter = item => item.name, + keyGetter = (item) => item.id, + titleGetter = (item) => item.name, }) => { const [items, setItems] = useState(); const [linked, setLinked] = useState(); @@ -28,34 +30,36 @@ const LinkField = ({ const response = await fetch(endpointLinked); if (response.ok) { const data = await response.json(); - setLinked(data.map(it => it.id)); + setLinked(data.map((it) => it.id)); } }, []); - const createBody = linkId => { + const createBody = (linkId) => { const body = {}; body[keyBase] = baseId; body[keyLink] = linkId; return body; - } + }; - const onChange = async event => { + const onChange = async (event) => { const oldValue = linked; const newValue = event.target.value; - for (const added of newValue.filter(it => !oldValue.includes(it))) { - await fetch('/api/permissions', { + const results = []; + newValue.filter((it) => !oldValue.includes(it)).forEach((added) => { + results.push(fetch('/api/permissions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(createBody(added)), - }); - } - for (const removed of oldValue.filter(it => !newValue.includes(it))) { - await fetch('/api/permissions', { + })); + }); + oldValue.filter((it) => !newValue.includes(it)).forEach((removed) => { + results.push(fetch('/api/permissions', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(createBody(removed)), - }); - } + })); + }); + await Promise.all(results); setLinked(newValue); }; @@ -66,16 +70,16 @@ const LinkField = ({ ); - } else { - return null; } -} + return null; +}; export default LinkField; diff --git a/modern/src/form/SelectField.js b/modern/src/form/SelectField.js index b179c58e..303d203c 100644 --- a/modern/src/form/SelectField.js +++ b/modern/src/form/SelectField.js @@ -1,4 +1,6 @@ -import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core'; +import { + FormControl, InputLabel, MenuItem, Select, +} from '@material-ui/core'; import React, { useState } from 'react'; import { useEffectAsync } from '../reactHelper'; @@ -12,8 +14,8 @@ const SelectField = ({ onChange, endpoint, data, - keyGetter = item => item.id, - titleGetter = item => item.name, + keyGetter = (item) => item.id, + titleGetter = (item) => item.name, }) => { const [items, setItems] = useState(data); @@ -33,19 +35,18 @@ const SelectField = ({ ); - } else { - return null; } -} + return null; +}; export default SelectField; diff --git a/modern/src/index.js b/modern/src/index.js index 82ddef95..155bf623 100644 --- a/modern/src/index.js +++ b/modern/src/index.js @@ -1,7 +1,7 @@ -import 'typeface-roboto' +import 'typeface-roboto'; import React from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter } from 'react-router-dom' +import { HashRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import App from './App'; diff --git a/modern/src/map/AccuracyMap.js b/modern/src/map/AccuracyMap.js index e81fc8fb..4baa1054 100644 --- a/modern/src/map/AccuracyMap.js +++ b/modern/src/map/AccuracyMap.js @@ -7,33 +7,31 @@ import { map } from './Map'; const AccuracyMap = () => { const id = 'accuracy'; - const positions = useSelector(state => ({ + const positions = useSelector((state) => ({ type: 'FeatureCollection', - features: Object.values(state.positions.items).filter(position => position.accuracy > 0).map(position => - circle([position.longitude, position.latitude], position.accuracy * 0.001) - ), + features: Object.values(state.positions.items).filter((position) => position.accuracy > 0).map((position) => circle([position.longitude, position.latitude], position.accuracy * 0.001)), })); useEffect(() => { map.addSource(id, { - 'type': 'geojson', - 'data': { + type: 'geojson', + data: { type: 'FeatureCollection', - features: [] - } + features: [], + }, }); map.addLayer({ - 'source': id, - 'id': id, - 'type': 'fill', - 'filter': [ - 'all', - ['==', '$type', 'Polygon'], + source: id, + id, + type: 'fill', + filter: [ + 'all', + ['==', '$type', 'Polygon'], ], - 'paint': { - 'fill-color':'#3bb2d0', - 'fill-outline-color':'#3bb2d0', - 'fill-opacity':0.25, + paint: { + 'fill-color': '#3bb2d0', + 'fill-outline-color': '#3bb2d0', + 'fill-opacity': 0.25, }, }); @@ -48,6 +46,6 @@ const AccuracyMap = () => { }, [positions]); return null; -} +}; export default AccuracyMap; diff --git a/modern/src/map/CurrentLocationMap.js b/modern/src/map/CurrentLocationMap.js index 06aaad0f..69724ce1 100644 --- a/modern/src/map/CurrentLocationMap.js +++ b/modern/src/map/CurrentLocationMap.js @@ -16,6 +16,6 @@ const CurrentLocationMap = () => { }, []); return null; -} +}; export default CurrentLocationMap; diff --git a/modern/src/map/CurrentPositionsMap.js b/modern/src/map/CurrentPositionsMap.js index 0bfe4fcd..9e00774a 100644 --- a/modern/src/map/CurrentPositionsMap.js +++ b/modern/src/map/CurrentPositionsMap.js @@ -1,11 +1,11 @@ -import React, { } from 'react'; +import React, { } from 'react'; import { useSelector } from 'react-redux'; import PositionsMap from './PositionsMap'; const CurrentPositionsMap = () => { - const positions = useSelector(state => Object.values(state.positions.items)); + const positions = useSelector((state) => Object.values(state.positions.items)); return (); -} +}; export default CurrentPositionsMap; diff --git a/modern/src/map/GeofenceEditMap.js b/modern/src/map/GeofenceEditMap.js index fe843382..d639c192 100644 --- a/modern/src/map/GeofenceEditMap.js +++ b/modern/src/map/GeofenceEditMap.js @@ -1,13 +1,13 @@ -import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css' +import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; import MapboxDraw from '@mapbox/mapbox-gl-draw'; 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 { map } from './Map'; import { geofenceToFeature, geometryToArea } from './mapUtil'; -import { useDispatch, useSelector } from 'react-redux'; import { geofencesActions } from '../store'; -import { useHistory } from 'react-router-dom'; const draw = new MapboxDraw({ displayControlsDefault: false, @@ -17,15 +17,15 @@ const draw = new MapboxDraw({ }, userProperties: true, styles: [...theme, { - 'id': 'gl-draw-title', - 'type': 'symbol', - 'filter': ['all'], - 'layout': { + id: 'gl-draw-title', + type: 'symbol', + filter: ['all'], + layout: { 'text-field': '{user_name}', 'text-font': ['Roboto Regular'], 'text-size': 12, }, - 'paint': { + paint: { 'text-halo-color': 'white', 'text-halo-width': 1, }, @@ -36,25 +36,25 @@ const GeofenceEditMap = () => { const dispatch = useDispatch(); const history = useHistory(); - const geofences = useSelector(state => Object.values(state.geofences.items)); + const geofences = useSelector((state) => Object.values(state.geofences.items)); const refreshGeofences = async () => { const response = await fetch('/api/geofences'); if (response.ok) { dispatch(geofencesActions.refresh(await response.json())); } - } + }; useEffect(() => { refreshGeofences(); map.addControl(draw, 'top-left'); - map.on('draw.create', async event => { + map.on('draw.create', async (event) => { const feature = event.features[0]; const newItem = { name: '', area: geometryToArea(feature.geometry) }; draw.delete(feature.id); - const response = await fetch(`/api/geofences`, { + const response = await fetch('/api/geofences', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newItem), @@ -65,7 +65,7 @@ const GeofenceEditMap = () => { } }); - map.on('draw.delete', async event => { + map.on('draw.delete', async (event) => { const feature = event.features[0]; const response = await fetch(`/api/geofences/${feature.id}`, { method: 'DELETE' }); if (response.ok) { @@ -73,9 +73,9 @@ const GeofenceEditMap = () => { } }); - map.on('draw.update', async event => { + map.on('draw.update', async (event) => { const feature = event.features[0]; - const item = geofences.find(i => i.id === feature.id); + const item = geofences.find((i) => i.id === feature.id); if (item) { const updatedItem = { ...item, area: geometryToArea(feature.geometry) }; const response = await fetch(`/api/geofences/${feature.id}`, { @@ -94,12 +94,12 @@ const GeofenceEditMap = () => { useEffect(() => { draw.deleteAll(); - for (const geofence of geofences) { + geofences.forEach((geofence) => { draw.add(geofenceToFeature(geofence)); - } + }); }, [geofences]); return null; -} +}; export default GeofenceEditMap; diff --git a/modern/src/map/GeofenceMap.js b/modern/src/map/GeofenceMap.js index 8db175a2..d00cbb18 100644 --- a/modern/src/map/GeofenceMap.js +++ b/modern/src/map/GeofenceMap.js @@ -7,49 +7,49 @@ import { geofenceToFeature } from './mapUtil'; const GeofenceMap = () => { const id = 'geofences'; - const geofences = useSelector(state => Object.values(state.geofences.items)); + const geofences = useSelector((state) => Object.values(state.geofences.items)); useEffect(() => { map.addSource(id, { - 'type': 'geojson', - 'data': { + type: 'geojson', + data: { type: 'FeatureCollection', - features: [] - } + features: [], + }, }); map.addLayer({ - 'source': id, - 'id': 'geofences-fill', - 'type': 'fill', - 'filter': [ - 'all', - ['==', '$type', 'Polygon'], + source: id, + id: 'geofences-fill', + type: 'fill', + filter: [ + 'all', + ['==', '$type', 'Polygon'], ], - 'paint': { - 'fill-color':'#3bb2d0', - 'fill-outline-color':'#3bb2d0', - 'fill-opacity':0.1, + paint: { + 'fill-color': '#3bb2d0', + 'fill-outline-color': '#3bb2d0', + 'fill-opacity': 0.1, }, }); map.addLayer({ - 'source': id, - 'id': 'geofences-line', - 'type': 'line', - 'paint': { - 'line-color': '#3bb2d0', - 'line-width': 2, + source: id, + id: 'geofences-line', + type: 'line', + paint: { + 'line-color': '#3bb2d0', + 'line-width': 2, }, }); map.addLayer({ - 'source': id, - 'id': 'geofences-title', - 'type': 'symbol', - 'layout': { + source: id, + id: 'geofences-title', + type: 'symbol', + layout: { 'text-field': '{name}', 'text-font': ['Roboto Regular'], 'text-size': 12, }, - 'paint': { + paint: { 'text-halo-color': 'white', 'text-halo-width': 1, }, @@ -66,11 +66,11 @@ const GeofenceMap = () => { useEffect(() => { map.getSource(id).setData({ type: 'FeatureCollection', - features: geofences.map(geofenceToFeature) + features: geofences.map(geofenceToFeature), }); }, [geofences]); return null; -} +}; export default GeofenceMap; diff --git a/modern/src/map/Map.js b/modern/src/map/Map.js index 46c59d2d..7dd1c2a8 100644 --- a/modern/src/map/Map.js +++ b/modern/src/map/Map.js @@ -1,9 +1,11 @@ import 'maplibre-gl/dist/maplibre-gl.css'; import './switcher/switcher.css'; import maplibregl from 'maplibre-gl'; +import React, { + useRef, useLayoutEffect, useEffect, useState, +} from 'react'; import { SwitcherControl } from './switcher/switcher'; -import React, { useRef, useLayoutEffect, useEffect, useState } from 'react'; -import { deviceCategories } from '../common/deviceCategories'; +import deviceCategories from '../common/deviceCategories'; import { prepareIcon, loadImage } from './mapUtil'; import { styleCarto, styleMapbox, styleOsm } from './mapStyles'; import t from '../common/localization'; @@ -22,18 +24,18 @@ export const map = new maplibregl.Map({ let ready = false; const readyListeners = new Set(); -const addReadyListener = listener => { +const addReadyListener = (listener) => { readyListeners.add(listener); listener(ready); }; -const removeReadyListener = listener => { +const removeReadyListener = (listener) => { readyListeners.delete(listener); }; -const updateReadyValue = value => { +const updateReadyValue = (value) => { ready = value; - readyListeners.forEach(listener => listener(value)); + readyListeners.forEach((listener) => listener(value)); }; const initMap = async () => { @@ -42,13 +44,16 @@ const initMap = async () => { map.addImage('background', await prepareIcon(background), { pixelRatio: window.devicePixelRatio, }); - await Promise.all(deviceCategories.map(async category => { - for (const color of ['green', 'red', 'gray']) { - const icon = await loadImage(`images/icon/${category}.svg`); - map.addImage(`${category}-${color}`, prepareIcon(background, icon, palette.common[color]), { - pixelRatio: window.devicePixelRatio, - }); - } + await Promise.all(deviceCategories.map(async (category) => { + const results = []; + ['green', 'red', 'gray'].forEach((color) => { + results.push(loadImage(`images/icon/${category}.svg`).then((icon) => { + map.addImage(`${category}-${color}`, prepareIcon(background, icon, palette.common[color]), { + pixelRatio: window.devicePixelRatio, + }); + })); + }); + await Promise.all(results); })); updateReadyValue(true); }; @@ -93,7 +98,7 @@ const Map = ({ children }) => { }, [mapboxAccessToken]); useEffect(() => { - const listener = ready => setMapReady(ready); + const listener = (ready) => setMapReady(ready); addReadyListener(listener); return () => { removeReadyListener(listener); diff --git a/modern/src/map/PositionsMap.js b/modern/src/map/PositionsMap.js index 35d6d926..2de01d2c 100644 --- a/modern/src/map/PositionsMap.js +++ b/modern/src/map/PositionsMap.js @@ -3,9 +3,9 @@ import ReactDOM from 'react-dom'; import maplibregl from 'maplibre-gl'; import { Provider, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; import { map } from './Map'; import store from '../store'; -import { useHistory } from 'react-router-dom'; import StatusView from './StatusView'; const PositionsMap = ({ positions }) => { @@ -13,9 +13,9 @@ const PositionsMap = ({ positions }) => { const clusters = `${id}-clusters`; const history = useHistory(); - const devices = useSelector(state => state.devices.items); + const devices = useSelector((state) => state.devices.items); - const deviceColor = device => { + const deviceColor = (device) => { switch (device.status) { case 'online': return 'green'; @@ -33,15 +33,15 @@ const PositionsMap = ({ positions }) => { name: device.name, category: device.category || 'default', color: deviceColor(device), - } + }; }; const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer'; const onMouseLeave = () => map.getCanvas().style.cursor = ''; - const onMarkerClick = useCallback(event => { + const onMarkerClick = useCallback((event) => { const feature = event.features[0]; - let coordinates = feature.geometry.coordinates.slice(); + const coordinates = feature.geometry.coordinates.slice(); while (Math.abs(event.lngLat.lng - coordinates[0]) > 180) { coordinates[0] += event.lngLat.lng > coordinates[0] ? 360 : -360; } @@ -49,50 +49,52 @@ const PositionsMap = ({ positions }) => { const placeholder = document.createElement('div'); ReactDOM.render( - history.push(`/position/${positionId}`)} /> + history.push(`/position/${positionId}`)} /> , - placeholder + placeholder, ); new maplibregl.Popup({ offset: 25, - anchor: 'top' + anchor: 'top', }) .setDOMContent(placeholder) .setLngLat(coordinates) .addTo(map); }, [history]); - const onClusterClick = event => { + const onClusterClick = (event) => { const features = map.queryRenderedFeatures(event.point, { layers: [clusters], }); const clusterId = features[0].properties.cluster_id; map.getSource(id).getClusterExpansionZoom(clusterId, (error, zoom) => { - if (!error) map.easeTo({ - center: features[0].geometry.coordinates, - zoom: zoom, - }); + if (!error) { + map.easeTo({ + center: features[0].geometry.coordinates, + zoom, + }); + } }); }; useEffect(() => { map.addSource(id, { - 'type': 'geojson', - 'data': { + type: 'geojson', + data: { type: 'FeatureCollection', features: [], }, - 'cluster': true, - 'clusterMaxZoom': 14, - 'clusterRadius': 50, + cluster: true, + clusterMaxZoom: 14, + clusterRadius: 50, }); map.addLayer({ - 'id': id, - 'type': 'symbol', - 'source': id, - 'filter': ['!', ['has', 'point_count']], - 'layout': { + id, + type: 'symbol', + source: id, + filter: ['!', ['has', 'point_count']], + layout: { 'icon-image': '{category}-{color}', 'icon-allow-overlap': true, 'text-field': '{name}', @@ -102,17 +104,17 @@ const PositionsMap = ({ positions }) => { 'text-font': ['Roboto Regular'], 'text-size': 12, }, - 'paint': { + paint: { 'text-halo-color': 'white', 'text-halo-width': 1, }, }); map.addLayer({ - 'id': clusters, - 'type': 'symbol', - 'source': id, - 'filter': ['has', 'point_count'], - 'layout': { + id: clusters, + type: 'symbol', + source: id, + filter: ['has', 'point_count'], + layout: { 'icon-image': 'background', 'text-field': '{point_count_abbreviated}', 'text-font': ['Roboto Regular'], @@ -120,7 +122,6 @@ const PositionsMap = ({ positions }) => { }, }); - map.on('mouseenter', id, onMouseEnter); map.on('mouseleave', id, onMouseLeave); map.on('mouseenter', clusters, onMouseEnter); @@ -129,7 +130,7 @@ const PositionsMap = ({ positions }) => { map.on('click', clusters, onClusterClick); return () => { - Array.from(map.getContainer().getElementsByClassName('maplibregl-popup')).forEach(el => el.remove()); + Array.from(map.getContainer().getElementsByClassName('maplibregl-popup')).forEach((el) => el.remove()); map.off('mouseenter', id, onMouseEnter); map.off('mouseleave', id, onMouseLeave); @@ -147,18 +148,18 @@ const PositionsMap = ({ positions }) => { useEffect(() => { map.getSource(id).setData({ type: 'FeatureCollection', - features: positions.filter(it => devices.hasOwnProperty(it.deviceId)).map(position => ({ + features: positions.filter((it) => devices.hasOwnProperty(it.deviceId)).map((position) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [position.longitude, position.latitude], }, properties: createFeature(devices, position), - })) + })), }); }, [devices, positions]); return null; -} +}; export default PositionsMap; diff --git a/modern/src/map/ReplayPathMap.js b/modern/src/map/ReplayPathMap.js index b40aa690..62b3f279 100644 --- a/modern/src/map/ReplayPathMap.js +++ b/modern/src/map/ReplayPathMap.js @@ -7,8 +7,8 @@ const ReplayPathMap = ({ positions }) => { useEffect(() => { map.addSource(id, { - 'type': 'geojson', - 'data': { + type: 'geojson', + data: { type: 'Feature', geometry: { type: 'LineString', @@ -17,16 +17,16 @@ const ReplayPathMap = ({ positions }) => { }, }); map.addLayer({ - 'source': id, - 'id': id, - 'type': 'line', - 'layout': { + source: id, + id, + type: 'line', + layout: { 'line-join': 'round', 'line-cap': 'round', }, - 'paint': { - 'line-color': '#333', - 'line-width': 5, + paint: { + 'line-color': '#333', + 'line-width': 5, }, }); @@ -37,23 +37,25 @@ const ReplayPathMap = ({ positions }) => { }, []); useEffect(() => { - const coordinates = positions.map(item => [item.longitude, item.latitude]); + const coordinates = positions.map((item) => [item.longitude, item.latitude]); map.getSource(id).setData({ type: 'Feature', geometry: { type: 'LineString', - coordinates: coordinates, + coordinates, }, }); if (coordinates.length) { const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[0])); map.fitBounds(bounds, { - padding: { top: 50, bottom: 250, left: 25, right: 25 }, + padding: { + top: 50, bottom: 250, left: 25, right: 25, + }, }); } }, [positions]); return null; -} +}; export default ReplayPathMap; diff --git a/modern/src/map/SelectedDeviceMap.js b/modern/src/map/SelectedDeviceMap.js index 655fca98..e6c5f58f 100644 --- a/modern/src/map/SelectedDeviceMap.js +++ b/modern/src/map/SelectedDeviceMap.js @@ -4,7 +4,7 @@ import { useSelector } from 'react-redux'; import { map } from './Map'; const SelectedDeviceMap = () => { - const mapCenter = useSelector(state => { + const mapCenter = useSelector((state) => { if (state.devices.selectedId) { const position = state.positions.items[state.devices.selectedId] || null; if (position) { @@ -19,6 +19,6 @@ const SelectedDeviceMap = () => { }, [mapCenter]); return null; -} +}; export default SelectedDeviceMap; diff --git a/modern/src/map/StatusView.js b/modern/src/map/StatusView.js index ae049af1..20e5b749 100644 --- a/modern/src/map/StatusView.js +++ b/modern/src/map/StatusView.js @@ -1,27 +1,68 @@ -import t from '../common/localization' import React from 'react'; import { useSelector } from 'react-redux'; +import t from '../common/localization'; import { formatPosition } from '../common/formatter'; const StatusView = ({ deviceId, onShowDetails }) => { - const device = useSelector(state => state.devices.items[deviceId]); - const position = useSelector(state => state.positions.items[deviceId]); + const device = useSelector((state) => state.devices.items[deviceId]); + const position = useSelector((state) => state.positions.items[deviceId]); - const handleClick = e => { + const handleClick = (e) => { e.preventDefault(); onShowDetails(position.id); }; return ( <> - {t('deviceStatus')}: {formatPosition(device.status, 'status')}
- {t('sharedLocation')}: {formatPosition(position, 'latitude')} {formatPosition(position, 'longitude')}
- {t('positionSpeed')}: {formatPosition(position.speed, 'speed')}
- {t('positionCourse')}: {formatPosition(position.course, 'course')}
- {t('positionDistance')}: {formatPosition(position.attributes.totalDistance, 'distance')}
- {position.attributes.batteryLevel && - <>{t('positionBattery')}: {formatPosition(position.attributes.batteryLevel, 'batteryLevel')}
- } + + {t('deviceStatus')} + : + + {' '} + {formatPosition(device.status, 'status')} +
+ + {t('sharedLocation')} + : + + {' '} + {formatPosition(position, 'latitude')} + {' '} + {formatPosition(position, 'longitude')} +
+ + {t('positionSpeed')} + : + + {' '} + {formatPosition(position.speed, 'speed')} +
+ + {t('positionCourse')} + : + + {' '} + {formatPosition(position.course, 'course')} +
+ + {t('positionDistance')} + : + + {' '} + {formatPosition(position.attributes.totalDistance, 'distance')} +
+ {position.attributes.batteryLevel + && ( + <> + + {t('positionBattery')} + : + + {' '} + {formatPosition(position.attributes.batteryLevel, 'batteryLevel')} +
+ + )} {t('sharedShowDetails')} ); diff --git a/modern/src/map/mapStyles.js b/modern/src/map/mapStyles.js index 00a8666d..9650ead5 100644 --- a/modern/src/map/mapStyles.js +++ b/modern/src/map/mapStyles.js @@ -4,7 +4,7 @@ export const styleCustom = (url, attribution) => ({ osm: { type: 'raster', tiles: [url], - attribution: attribution, + attribution, tileSize: 256, }, }, @@ -22,30 +22,30 @@ export const styleOsm = () => styleCustom( ); export const styleCarto = () => ({ - 'version': 8, - 'sources': { + version: 8, + sources: { 'raster-tiles': { - 'type': 'raster', - 'tiles': [ - 'https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', - 'https://b.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', - 'https://c.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', - 'https://d.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png' + type: 'raster', + tiles: [ + 'https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', + 'https://b.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', + 'https://c.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', + 'https://d.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', ], - 'tileSize': 256, - 'attribution': '© OpenStreetMap contributors, © CARTO' - } + tileSize: 256, + attribution: '© OpenStreetMap contributors, © CARTO', + }, }, - 'glyphs': 'https://cdn.traccar.com/map/fonts/{fontstack}/{range}.pbf', - 'layers': [ + glyphs: 'https://cdn.traccar.com/map/fonts/{fontstack}/{range}.pbf', + layers: [ { - 'id': 'simple-tiles', - 'type': 'raster', - 'source': 'raster-tiles', - 'minzoom': 0, - 'maxzoom': 22, - } - ] + id: 'simple-tiles', + type: 'raster', + source: 'raster-tiles', + minzoom: 0, + maxzoom: 22, + }, + ], }); export const styleMapbox = (style) => `mapbox://styles/mapbox/${style}`; diff --git a/modern/src/map/mapUtil.js b/modern/src/map/mapUtil.js index 67ce345e..e3c32f46 100644 --- a/modern/src/map/mapUtil.js +++ b/modern/src/map/mapUtil.js @@ -2,13 +2,11 @@ import { parse, stringify } from 'wellknown'; import canvasTintImage from 'canvas-tint-image'; import circle from '@turf/circle'; -export const loadImage = (url) => { - return new Promise(imageLoaded => { - const image = new Image(); - image.onload = () => imageLoaded(image); - image.src = url; - }); -}; +export const loadImage = (url) => new Promise((imageLoaded) => { + const image = new Image(); + image.onload = () => imageLoaded(image); + image.src = url; +}); export const prepareIcon = (background, icon, color) => { const pixelRatio = window.devicePixelRatio; @@ -32,44 +30,39 @@ export const prepareIcon = (background, icon, color) => { return context.getImageData(0, 0, canvas.width, canvas.height); }; -export const reverseCoordinates = it => { +export const reverseCoordinates = (it) => { if (!it) { return it; - } else if (Array.isArray(it)) { + } if (Array.isArray(it)) { if (it.length === 2 && !Number.isNaN(it[0]) && !Number.isNaN(it[1])) { return [it[1], it[0]]; - } else { - return it.map(it => reverseCoordinates(it)); - } - } else { - return { - ...it, - coordinates: reverseCoordinates(it.coordinates), } + return it.map((it) => reverseCoordinates(it)); } -} + return { + ...it, + coordinates: reverseCoordinates(it.coordinates), + }; +}; export const geofenceToFeature = (item) => { if (item.area.indexOf('CIRCLE') > -1) { - let coordinates = item.area.replace(/CIRCLE|\(|\)|,/g, " ").trim().split(/ +/); - var options = { steps: 32, units: 'meters' }; - let polygon = circle([Number(coordinates[1]), Number(coordinates[0])], Number(coordinates[2]), options); + const coordinates = item.area.replace(/CIRCLE|\(|\)|,/g, ' ').trim().split(/ +/); + const options = { steps: 32, units: 'meters' }; + const polygon = circle([Number(coordinates[1]), Number(coordinates[0])], Number(coordinates[2]), options); return { id: item.id, type: 'Feature', geometry: polygon.geometry, - properties: { name: item.name } - }; - } else { - return { - id: item.id, - type: 'Feature', - geometry: reverseCoordinates(parse(item.area)), - properties: { name: item.name } + properties: { name: item.name }, }; } -} + return { + id: item.id, + type: 'Feature', + geometry: reverseCoordinates(parse(item.area)), + properties: { name: item.name }, + }; +}; -export const geometryToArea = (geometry) => { - return stringify(reverseCoordinates(geometry)); -} +export const geometryToArea = (geometry) => stringify(reverseCoordinates(geometry)); diff --git a/modern/src/map/switcher/switcher.js b/modern/src/map/switcher/switcher.js index a755645b..e9076aa6 100644 --- a/modern/src/map/switcher/switcher.js +++ b/modern/src/map/switcher/switcher.js @@ -1,5 +1,4 @@ export class SwitcherControl { - constructor(styles, defaultStyle, beforeSwitch, afterSwitch) { this.styles = styles; this.defaultStyle = defaultStyle; @@ -27,8 +26,8 @@ export class SwitcherControl { styleElement.innerText = style.title; styleElement.classList.add(style.title.replace(/[^a-z0-9-]/gi, '_')); styleElement.dataset.uri = JSON.stringify(style.uri); - styleElement.addEventListener('click', event => { - const srcElement = event.srcElement; + styleElement.addEventListener('click', (event) => { + const { srcElement } = event; if (srcElement.classList.contains('active')) { return; } diff --git a/modern/src/reactHelper.js b/modern/src/reactHelper.js index b0eb0169..f3ef78dd 100644 --- a/modern/src/reactHelper.js +++ b/modern/src/reactHelper.js @@ -1,7 +1,6 @@ - import { useRef, useEffect } from 'react'; -export const usePrevious = value => { +export const usePrevious = (value) => { const ref = useRef(); useEffect(() => { ref.current = value; @@ -12,5 +11,5 @@ export const usePrevious = value => { export const useEffectAsync = (effect, deps) => { useEffect(() => { effect(); - }, deps); // eslint-disable-line react-hooks/exhaustive-deps + }, deps); }; diff --git a/modern/src/reports/ChartReportPage.js b/modern/src/reports/ChartReportPage.js index 0a5c8e18..70ce780f 100644 --- a/modern/src/reports/ChartReportPage.js +++ b/modern/src/reports/ChartReportPage.js @@ -1,5 +1,7 @@ import React, { useState } from 'react'; -import { Grid, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; +import { + Grid, FormControl, InputLabel, Select, MenuItem, +} from '@material-ui/core'; import ReportLayoutPage from './ReportLayoutPage'; import ReportFilter from './ReportFilter'; import Graph from './Graph'; @@ -9,66 +11,61 @@ import { speedFromKnots } from '../common/converter'; import t from '../common/localization'; const Filter = ({ children, setItems }) => { - const speedUnit = useAttributePreference('speedUnit'); const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, mail }); + const query = new URLSearchParams({ + deviceId, from, to, mail, + }); const response = await fetch(`/api/reports/route?${query.toString()}`, { headers }); if (response.ok) { const positions = await response.json(); - let formattedPositions = positions.map(position => { - return { - speed: Number(speedFromKnots(position.speed, speedUnit)), - altitude: position.altitude, - accuracy: position.accuracy, - fixTime: formatDate(position.fixTime) - } - }); + const formattedPositions = positions.map((position) => ({ + speed: Number(speedFromKnots(position.speed, speedUnit)), + altitude: position.altitude, + accuracy: position.accuracy, + fixTime: formatDate(position.fixTime), + })); setItems(formattedPositions); } - } + }; return ( <> {children} - ) -} - -const ChartType = ({ type, setType }) => { + ); +}; - return ( - - - - {t('reportChartType')} - - - +const ChartType = ({ type, setType }) => ( + + + + {t('reportChartType')} + + - ) -} - + +); const ChartReportPage = () => { - const [items, setItems] = useState([]); const [type, setType] = useState('speed'); return ( - - }> + )} + > - ) -} + ); +}; export default ChartReportPage; diff --git a/modern/src/reports/EventReportPage.js b/modern/src/reports/EventReportPage.js index 6d80860c..89efcf76 100644 --- a/modern/src/reports/EventReportPage.js +++ b/modern/src/reports/EventReportPage.js @@ -1,7 +1,9 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; -import { Grid, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; -import { useTheme } from "@material-ui/core/styles"; +import { + Grid, FormControl, InputLabel, Select, MenuItem, +} from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { useSelector } from 'react-redux'; import { formatDate } from '../common/formatter'; import ReportFilter from './ReportFilter'; @@ -10,12 +12,13 @@ import { prefixString } from '../common/stringUtils'; import t from '../common/localization'; const Filter = ({ setItems }) => { - const [eventTypes, setEventTypes] = useState(['allEvents']); const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, mail }); - eventTypes.forEach(it => query.append('type', it)); + const query = new URLSearchParams({ + deviceId, from, to, mail, + }); + eventTypes.forEach((it) => query.append('type', it)); const response = await fetch(`/api/reports/events?${query.toString()}`, { headers }); if (response.ok) { const contentType = response.headers.get('content-type'); @@ -34,7 +37,7 @@ const Filter = ({ setItems }) => { {t('reportEventTypes')} - setEventTypes(e.target.value)} multiple> {t('eventAll')} {t('eventDeviceOnline')} {t('eventDeviceUnknown')} @@ -58,21 +61,20 @@ const Filter = ({ setItems }) => { ); -} +}; const EventReportPage = () => { - const theme = useTheme(); - const geofences = useSelector(state => state.geofences.items); + const geofences = useSelector((state) => state.geofences.items); const [items, setItems] = useState([]); - const formatGeofence = value => { + const formatGeofence = (value) => { if (value > 0) { - const geofence = geofences[value]; - return geofence ? geofence.name : ''; + const geofence = geofences[value]; + return geofence ? geofence.name : ''; } return null; - } + }; const columns = [{ headerName: t('positionFixTime'), @@ -101,12 +103,13 @@ const EventReportPage = () => { return ( }> + rows={items} + columns={columns} + hideFooter + autoHeight + /> ); -} +}; export default EventReportPage; diff --git a/modern/src/reports/Graph.js b/modern/src/reports/Graph.js index 990eb5d5..63d24eee 100644 --- a/modern/src/reports/Graph.js +++ b/modern/src/reports/Graph.js @@ -1,9 +1,11 @@ import React from 'react'; import { withWidth } from '@material-ui/core'; -import {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import { + LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, +} from 'recharts'; -const CustomizedAxisTick = ({ x, y, payload }) =>{ - if(!payload.value) { +const CustomizedAxisTick = ({ x, y, payload }) => { + if (!payload.value) { return payload.value; } const parts = payload.value.split(' '); @@ -13,22 +15,19 @@ const CustomizedAxisTick = ({ x, y, payload }) =>{ {parts[1]} ); -} +}; -const Graph = ({ type, items }) => { - - return ( - - - } height={60} /> - - - - - - - - ); -} +const Graph = ({ type, items }) => ( + + + } height={60} /> + + + + + + + +); export default withWidth()(Graph); diff --git a/modern/src/reports/ReplayPage.js b/modern/src/reports/ReplayPage.js index dfa99997..12bbd351 100644 --- a/modern/src/reports/ReplayPage.js +++ b/modern/src/reports/ReplayPage.js @@ -1,5 +1,7 @@ import React, { useState } from 'react'; -import { Accordion, AccordionDetails, AccordionSummary, Container, makeStyles, Paper, Slider, Tooltip, Typography } from '@material-ui/core'; +import { + Accordion, AccordionDetails, AccordionSummary, Container, makeStyles, Paper, Slider, Tooltip, Typography, +} from '@material-ui/core'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import MainToolbar from '../MainToolbar'; import Map from '../map/Map'; @@ -9,7 +11,7 @@ import PositionsMap from '../map/PositionsMap'; import { formatPosition } from '../common/formatter'; import ReportFilter from './ReportFilter'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { height: '100%', display: 'flex', @@ -31,13 +33,11 @@ const useStyles = makeStyles(theme => ({ }, })); -const TimeLabel = ({ children, open, value }) => { - return ( - - {children} - - ); -}; +const TimeLabel = ({ children, open, value }) => ( + + {children} + +); const ReplayPage = () => { const classes = useStyles(); @@ -61,12 +61,12 @@ const ReplayPage = () => { - {index < positions.length && - - } + {index < positions.length + && } - {!!positions.length && + {!!positions.length + && ( { value={index} onChange={(_, index) => setIndex(index)} valueLabelDisplay="auto" - valueLabelFormat={i => i < positions.length ? formatPosition(positions[i], 'fixTime') : ''} + valueLabelFormat={(i) => (i < positions.length ? formatPosition(positions[i], 'fixTime') : '')} ValueLabelComponent={TimeLabel} - /> + /> - } + )}
setExpanded(!expanded)}> }> - + {t('reportConfigure')} @@ -95,6 +95,6 @@ const ReplayPage = () => {
); -} +}; export default ReplayPage; diff --git a/modern/src/reports/ReportFilter.js b/modern/src/reports/ReportFilter.js index c92741eb..1e2eb887 100644 --- a/modern/src/reports/ReportFilter.js +++ b/modern/src/reports/ReportFilter.js @@ -1,11 +1,13 @@ import React, { useState } from 'react'; -import { FormControl, InputLabel, Select, MenuItem, Button, TextField, Grid, Typography } from '@material-ui/core'; +import { + FormControl, InputLabel, Select, MenuItem, Button, TextField, Grid, Typography, +} from '@material-ui/core'; import { useSelector } from 'react-redux'; import moment from 'moment'; import t from '../common/localization'; const ReportFilter = ({ children, handleSubmit, showOnly }) => { - const devices = useSelector(state => Object.values(state.devices.items)); + const devices = useSelector((state) => Object.values(state.devices.items)); const [deviceId, setDeviceId] = useState(); const [period, setPeriod] = useState('today'); const [from, setFrom] = useState(moment().subtract(1, 'hour')); @@ -51,9 +53,9 @@ const ReportFilter = ({ children, handleSubmit, showOnly }) => { selectedFrom.toISOString(), selectedTo.toISOString(), mail, - { Accept: accept } + { Accept: accept }, ); - } + }; return ( @@ -81,56 +83,69 @@ const ReportFilter = ({ children, handleSubmit, showOnly }) => { - {period === 'custom' && + {period === 'custom' && ( + setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} - fullWidth /> - } - {period === 'custom' && + onChange={(e) => setFrom(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth + /> + + )} + {period === 'custom' && ( + setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} - fullWidth /> - } + onChange={(e) => setTo(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL))} + fullWidth + /> + + )} {children} - - {!showOnly && + {!showOnly + && ( - - } - {!showOnly && + + + )} + {!showOnly + && ( - - } + + + )} ); -} +}; export default ReportFilter; diff --git a/modern/src/reports/ReportLayoutPage.js b/modern/src/reports/ReportLayoutPage.js index 6bab67c6..d25a8876 100644 --- a/modern/src/reports/ReportLayoutPage.js +++ b/modern/src/reports/ReportLayoutPage.js @@ -1,6 +1,8 @@ import React, { useState, useEffect } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { Grid, Typography, Divider, Drawer, makeStyles, IconButton, Hidden } from '@material-ui/core'; +import { + Grid, Typography, Divider, Drawer, makeStyles, IconButton, Hidden, +} from '@material-ui/core'; import TimelineIcon from '@material-ui/icons/Timeline'; import PauseCircleFilledIcon from '@material-ui/icons/PauseCircleFilled'; import PlayCircleFilledIcon from '@material-ui/icons/PlayCircleFilled'; @@ -9,11 +11,11 @@ import FormatListBulletedIcon from '@material-ui/icons/FormatListBulleted'; import TrendingUpIcon from '@material-ui/icons/TrendingUp'; import ArrowBackIcon from '@material-ui/icons/ArrowBack'; -import ReportSidebar from '../components/reports/ReportSidebar' -import ReportNavbar from '../components/reports/ReportNavbar' +import ReportSidebar from '../components/reports/ReportSidebar'; +import ReportNavbar from '../components/reports/ReportNavbar'; import t from '../common/localization'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ root: { display: 'flex', height: '100%', @@ -23,10 +25,10 @@ const useStyles = makeStyles(theme => ({ }, drawer: { width: theme.dimensions.drawerWidthDesktop, - [theme.breakpoints.down("md")]: { + [theme.breakpoints.down('md')]: { width: theme.dimensions.drawerWidthTablet, - } - }, + }, + }, content: { flex: 1, padding: theme.spacing(5, 3, 3, 3), @@ -39,13 +41,13 @@ const useStyles = makeStyles(theme => ({ }, backArrowIconContainer: { '&:hover': { - backgroundColor:"transparent" - } + backgroundColor: 'transparent', + }, }, toolbar: { - [theme.breakpoints.down("md")]: { + [theme.breakpoints.down('md')]: { ...theme.mixins.toolbar, - } + }, }, })); @@ -58,7 +60,7 @@ const routes = [ { name: t('reportChart'), href: '/reports/chart', icon: }, ]; -const ReportLayoutPage = ({ children, filter, }) => { +const ReportLayoutPage = ({ children, filter }) => { const classes = useStyles(); const history = useHistory(); const location = useLocation(); @@ -66,7 +68,7 @@ const ReportLayoutPage = ({ children, filter, }) => { const [reportTitle, setReportTitle] = useState(); useEffect(() => { - routes.forEach(route => { + routes.forEach((route) => { switch (location.pathname) { case `${route.href}`: setReportTitle(route.name); @@ -85,7 +87,8 @@ const ReportLayoutPage = ({ children, filter, }) => { variant="temporary" open={openDrawer} onClose={() => setOpenDrawer(!openDrawer)} - classes={{paper: classes.drawer}}> + classes={{ paper: classes.drawer }} + > @@ -93,17 +96,19 @@ const ReportLayoutPage = ({ children, filter, }) => {
+ classes={{ paper: classes.drawer }} + >
history.push('/')} - className={classes.backArrowIconContainer} - disableRipple> + className={classes.backArrowIconContainer} + disableRipple + > {t('reportTitle')} - +
@@ -116,9 +121,9 @@ const ReportLayoutPage = ({ children, filter, }) => { {filter} {children} -
+
); -} +}; export default ReportLayoutPage; diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js index 04b513e9..dce08cb1 100644 --- a/modern/src/reports/RouteReportPage.js +++ b/modern/src/reports/RouteReportPage.js @@ -1,17 +1,20 @@ import React, { useState } from 'react'; import { Paper } from '@material-ui/core'; import { DataGrid } from '@material-ui/data-grid'; -import { useTheme } from "@material-ui/core/styles"; -import { formatDistance, formatSpeed, formatBoolean, formatDate, formatCoordinate } from '../common/formatter'; +import { useTheme } from '@material-ui/core/styles'; +import { + formatDistance, formatSpeed, formatBoolean, formatDate, formatCoordinate, +} from '../common/formatter'; import ReportFilter from './ReportFilter'; import ReportLayoutPage from './ReportLayoutPage'; import { useAttributePreference, usePreference } from '../common/preferences'; import t from '../common/localization'; const Filter = ({ setItems }) => { - const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, mail }); + const query = new URLSearchParams({ + deviceId, from, to, mail, + }); const response = await fetch(`/api/reports/route?${query.toString()}`, { headers }); if (response.ok) { const contentType = response.headers.get('content-type'); @@ -23,7 +26,7 @@ const Filter = ({ setItems }) => { } } } - } + }; return ; }; @@ -78,7 +81,7 @@ const RouteReportPage = () => { width: theme.dimensions.columnWidthNumber, valueGetter: ({ row }) => row.attributes.totalDistance, valueFormatter: ({ value }) => formatDistance(value, distanceUnit), - }] + }]; const [items, setItems] = useState([]); @@ -86,10 +89,11 @@ const RouteReportPage = () => { }> + rows={items} + columns={columns} + hideFooter + autoHeight + /> ); diff --git a/modern/src/reports/StopReportPage.js b/modern/src/reports/StopReportPage.js index 6953c464..078a5e5e 100644 --- a/modern/src/reports/StopReportPage.js +++ b/modern/src/reports/StopReportPage.js @@ -1,16 +1,19 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; -import { useTheme } from "@material-ui/core/styles"; -import { formatDistance, formatHours, formatDate, formatVolume } from '../common/formatter'; +import { useTheme } from '@material-ui/core/styles'; +import { + formatDistance, formatHours, formatDate, formatVolume, +} from '../common/formatter'; import ReportFilter from './ReportFilter'; import ReportLayoutPage from './ReportLayoutPage'; import { useAttributePreference } from '../common/preferences'; import t from '../common/localization'; const Filter = ({ setItems }) => { - const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, mail }); + const query = new URLSearchParams({ + deviceId, from, to, mail, + }); const response = await fetch(`/api/reports/stops?${query.toString()}`, { headers }); if (response.ok) { const contentType = response.headers.get('content-type'); @@ -22,13 +25,12 @@ const Filter = ({ setItems }) => { } } } - } + }; return ; }; const StopReportPage = () => { - const theme = useTheme(); const distanceUnit = useAttributePreference('distanceUnit'); @@ -41,7 +43,7 @@ const StopReportPage = () => { field: 'startTime', type: 'dateTime', width: theme.dimensions.columnWidthDate, - valueFormatter: ({ value }) => formatDate(value), + valueFormatter: ({ value }) => formatDate(value), }, { headerName: t('positionOdometer'), field: 'startOdometer', @@ -53,7 +55,7 @@ const StopReportPage = () => { field: 'address', type: 'string', hide: true, - width: theme.dimensions.columnWidthString, + width: theme.dimensions.columnWidthString, }, { headerName: t('reportEndTime'), field: 'endTime', @@ -78,17 +80,18 @@ const StopReportPage = () => { type: 'number', width: theme.dimensions.columnWidthNumber, hide: true, - valueFormatter: ({ value }) => formatVolume(value, volumeUnit), - }] - + valueFormatter: ({ value }) => formatVolume(value, volumeUnit), + }]; + return ( }> Math.random()} /> + getRowId={() => Math.random()} + /> ); }; diff --git a/modern/src/reports/SummaryReportPage.js b/modern/src/reports/SummaryReportPage.js index e3819a53..0e88705f 100644 --- a/modern/src/reports/SummaryReportPage.js +++ b/modern/src/reports/SummaryReportPage.js @@ -1,19 +1,22 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; import { Grid, FormControlLabel, Checkbox } from '@material-ui/core'; -import { useTheme } from "@material-ui/core/styles"; -import { formatDistance, formatHours, formatDate, formatSpeed, formatVolume } from '../common/formatter'; +import { useTheme } from '@material-ui/core/styles'; +import { + formatDistance, formatHours, formatDate, formatSpeed, formatVolume, +} from '../common/formatter'; import ReportFilter from './ReportFilter'; import ReportLayoutPage from './ReportLayoutPage'; import { useAttributePreference } from '../common/preferences'; import t from '../common/localization'; const Filter = ({ setItems }) => { - const [daily, setDaily] = useState(false); const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, daily, mail }); + const query = new URLSearchParams({ + deviceId, from, to, daily, mail, + }); const response = await fetch(`/api/reports/summary?${query.toString()}`, { headers }); if (response.ok) { const contentType = response.headers.get('content-type'); @@ -25,21 +28,21 @@ const Filter = ({ setItems }) => { } } } - } + }; return ( setDaily(e.target.checked)} />} - label={t('reportDaily')} /> + control={ setDaily(e.target.checked)} />} + label={t('reportDaily')} + /> ); -} +}; const SummaryReportPage = () => { - const theme = useTheme(); const distanceUnit = useAttributePreference('distanceUnit'); @@ -96,19 +99,20 @@ const SummaryReportPage = () => { type: 'number', width: theme.dimensions.columnWidthNumber, hide: true, - valueFormatter: ({ value }) => formatVolume(value, volumeUnit), - }] - + valueFormatter: ({ value }) => formatVolume(value, volumeUnit), + }]; + return ( }> Math.random()} /> + getRowId={() => Math.random()} + /> ); -} +}; export default SummaryReportPage; diff --git a/modern/src/reports/TripReportPage.js b/modern/src/reports/TripReportPage.js index 5f414f44..c10e6b1a 100644 --- a/modern/src/reports/TripReportPage.js +++ b/modern/src/reports/TripReportPage.js @@ -1,16 +1,19 @@ import React, { useState } from 'react'; import { DataGrid } from '@material-ui/data-grid'; -import { useTheme } from "@material-ui/core/styles"; -import { formatDistance, formatSpeed, formatHours, formatDate, formatVolume } from '../common/formatter'; +import { useTheme } from '@material-ui/core/styles'; +import { + formatDistance, formatSpeed, formatHours, formatDate, formatVolume, +} from '../common/formatter'; import ReportFilter from './ReportFilter'; import ReportLayoutPage from './ReportLayoutPage'; import { useAttributePreference } from '../common/preferences'; import t from '../common/localization'; const Filter = ({ setItems }) => { - const handleSubmit = async (deviceId, from, to, mail, headers) => { - const query = new URLSearchParams({ deviceId, from, to, mail }); + const query = new URLSearchParams({ + deviceId, from, to, mail, + }); const response = await fetch(`/api/reports/trips?${query.toString()}`, { headers }); if (response.ok) { const contentType = response.headers.get('content-type'); @@ -22,15 +25,14 @@ const Filter = ({ setItems }) => { } } } - } + }; return ; -} +}; const TripReportPage = () => { - const theme = useTheme(); - + const distanceUnit = useAttributePreference('distanceUnit'); const speedUnit = useAttributePreference('speedUnit'); const volumeUnit = useAttributePreference('volumeUnit'); @@ -109,19 +111,20 @@ const TripReportPage = () => { field: 'driverName', type: 'string', width: theme.dimensions.columnWidthString, - hide: true - }] + hide: true, + }]; return ( }> Math.random()} /> + getRowId={() => Math.random()} + /> ); -} +}; export default TripReportPage; diff --git a/modern/src/serviceWorker.js b/modern/src/serviceWorker.js index f59f1997..a0344303 100644 --- a/modern/src/serviceWorker.js +++ b/modern/src/serviceWorker.js @@ -11,13 +11,13 @@ // opt-in, read https://bit.ly/CRA-PWA const isLocalhost = Boolean( - window.location.hostname === 'localhost' || + window.location.hostname === 'localhost' // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || + || window.location.hostname === '[::1]' // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) + || window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, + ), ); export function register(config) { @@ -42,8 +42,8 @@ export function register(config) { // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://bit.ly/CRA-PWA' + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA', ); }); } else { @@ -57,7 +57,7 @@ export function register(config) { function registerValidSW(swUrl, config) { navigator.serviceWorker .register(swUrl) - .then(registration => { + .then((registration) => { registration.onupdatefound = () => { const installingWorker = registration.installing; if (installingWorker == null) { @@ -70,8 +70,8 @@ function registerValidSW(swUrl, config) { // but the previous service worker will still serve the older // content until all client tabs are closed. console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.', ); // Execute callback @@ -93,7 +93,7 @@ function registerValidSW(swUrl, config) { }; }; }) - .catch(error => { + .catch((error) => { console.error('Error during service worker registration:', error); }); } @@ -103,15 +103,15 @@ function checkValidServiceWorker(swUrl, config) { fetch(swUrl, { headers: { 'Service-Worker': 'script' }, }) - .then(response => { + .then((response) => { // Ensure service worker exists, and that we really are getting a JS file. const contentType = response.headers.get('content-type'); if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) + response.status === 404 + || (contentType != null && contentType.indexOf('javascript') === -1) ) { // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { + navigator.serviceWorker.ready.then((registration) => { registration.unregister().then(() => { window.location.reload(); }); @@ -123,7 +123,7 @@ function checkValidServiceWorker(swUrl, config) { }) .catch(() => { console.log( - 'No internet connection found. App is running in offline mode.' + 'No internet connection found. App is running in offline mode.', ); }); } @@ -131,11 +131,11 @@ function checkValidServiceWorker(swUrl, config) { export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready - .then(registration => { + .then((registration) => { registration.unregister(); }) - .catch(error => { + .catch((error) => { console.error(error.message); }); } -} \ No newline at end of file +} diff --git a/modern/src/settings/ComputedAttributePage.js b/modern/src/settings/ComputedAttributePage.js index 73759fab..fea613a9 100644 --- a/modern/src/settings/ComputedAttributePage.js +++ b/modern/src/settings/ComputedAttributePage.js @@ -1,19 +1,19 @@ import React, { useState } from 'react'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, MenuItem, Select, TextField } from "@material-ui/core"; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, MenuItem, Select, TextField, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from '../common/localization'; import EditItemView from '../EditItemView'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import positionAttributes from '../attributes/positionAttributes'; - const useStyles = makeStyles(() => ({ details: { flexDirection: 'column', }, })); -const ComputedAttributePage =() => { - +const ComputedAttributePage = () => { const classes = useStyles(); const [item, setItem] = useState(); const [key, setKey] = useState(); @@ -24,20 +24,21 @@ const ComputedAttributePage =() => { type: value.type, })); - const handleChange = event => { + const handleChange = (event) => { const newValue = event.target.value; setKey(newValue); const positionAttribute = positionAttributes[newValue]; - if(positionAttribute && positionAttribute.type) { - setItem({...item, attribute: newValue, type: positionAttribute.type}); + if (positionAttribute && positionAttribute.type) { + setItem({ ...item, attribute: newValue, type: positionAttribute.type }); } else { - setItem({...item, attribute: newValue}); + setItem({ ...item, attribute: newValue }); } - } + }; return ( - {item && + {item + && ( }> @@ -48,46 +49,51 @@ const ComputedAttributePage =() => { setItem({...item, description: event.target.value})} + onChange={(event) => setItem({ ...item, description: event.target.value })} label={t('sharedDescription')} - variant="filled" /> + variant="filled" + /> {t('sharedAttribute')} - {options.map((option) => ( {option.name} ))} - + setItem({...item, expression: event.target.value})} + onChange={(event) => setItem({ ...item, expression: event.target.value })} label={t('sharedExpression')} multiline rows={4} - variant="filled" /> + variant="filled" + /> + disabled={key in positionAttributes} + > {t('sharedType')} - } + )} - ) -} + ); +}; export default ComputedAttributePage; diff --git a/modern/src/settings/ComputedAttributesPage.js b/modern/src/settings/ComputedAttributesPage.js index 1a6feab5..f555259b 100644 --- a/modern/src/settings/ComputedAttributesPage.js +++ b/modern/src/settings/ComputedAttributesPage.js @@ -1,13 +1,15 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; import { useSelector } from 'react-redux'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -18,7 +20,7 @@ const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => { const classes = useStyles(); const [items, setItems] = useState([]); - const adminEnabled = useSelector(state => state.session.user && state.session.user.administrator); + const adminEnabled = useSelector((state) => state.session.user && state.session.user.administrator); useEffectAsync(async () => { const response = await fetch('/api/attributes/computed'); @@ -29,45 +31,44 @@ const ComputedAttributeView = ({ updateTimestamp, onMenuClick }) => { return ( - - - - {adminEnabled && } - {t('sharedDescription')} - {t('sharedAttribute')} - {t('sharedExpression')} - {t('sharedType')} - - - - {items.map((item) => ( - - {adminEnabled && +
+ + + {adminEnabled && } + {t('sharedDescription')} + {t('sharedAttribute')} + {t('sharedExpression')} + {t('sharedType')} + + + + {items.map((item) => ( + + {adminEnabled + && ( onMenuClick(event.currentTarget, item.id)}> - } - {item.description} - {item.attribute} - {item.expression} - {item.type} - - ))} - -
+ )} + {item.description} + {item.attribute} + {item.expression} + {item.type} + + ))} + +
); -} +}; -const ComputedAttributesPage = () => { - return ( - <> - - - - ); -} +const ComputedAttributesPage = () => ( + <> + + + +); export default ComputedAttributesPage; diff --git a/modern/src/settings/DriverPage.js b/modern/src/settings/DriverPage.js index 86feab84..01400c5c 100644 --- a/modern/src/settings/DriverPage.js +++ b/modern/src/settings/DriverPage.js @@ -1,9 +1,11 @@ import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from '../common/localization'; import EditItemView from '../EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import EditAttributesView from '../attributes/EditAttributesView'; const useStyles = makeStyles(() => ({ @@ -19,7 +21,8 @@ const DriverPage = () => { return ( - {item && + {item + && ( <> }> @@ -31,15 +34,17 @@ const DriverPage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> setItem({...item, uniqueId: event.target.value})} + onChange={(event) => setItem({ ...item, uniqueId: event.target.value })} label={t('deviceIdentifier')} - variant="filled" /> + variant="filled" + /> @@ -51,15 +56,15 @@ const DriverPage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={{}} - /> + /> - } + )} ); -} +}; export default DriverPage; diff --git a/modern/src/settings/DriversPage.js b/modern/src/settings/DriversPage.js index 957e2250..36fc12d6 100644 --- a/modern/src/settings/DriversPage.js +++ b/modern/src/settings/DriversPage.js @@ -1,12 +1,14 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -27,39 +29,37 @@ const DriversView = ({ updateTimestamp, onMenuClick }) => { return ( - - - - - {t('sharedName')} - {t('deviceIdentifier')} - - - - {items.map((item) => ( - - - onMenuClick(event.currentTarget, item.id)}> - - - - {item.name} - {item.uniqueId} +
+ + + + {t('sharedName')} + {t('deviceIdentifier')} - ))} - -
+ + + {items.map((item) => ( + + + onMenuClick(event.currentTarget, item.id)}> + + + + {item.name} + {item.uniqueId} + + ))} + +
); -} +}; -const DriversPage = () => { - return ( - <> - - - - ); -} +const DriversPage = () => ( + <> + + + +); export default DriversPage; diff --git a/modern/src/settings/GroupPage.js b/modern/src/settings/GroupPage.js index b9fa8716..f9af0f8b 100644 --- a/modern/src/settings/GroupPage.js +++ b/modern/src/settings/GroupPage.js @@ -1,10 +1,12 @@ import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t from '../common/localization'; import EditItemView from '../EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import EditAttributesView from '../attributes/EditAttributesView'; import deviceAttributes from '../attributes/deviceAttributes'; import SelectField from '../form/SelectField'; @@ -22,7 +24,8 @@ const GroupPage = () => { return ( - {item && + {item + && ( <> }> @@ -34,9 +37,10 @@ const GroupPage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> @@ -49,10 +53,11 @@ const GroupPage = () => { setItem({...item, groupId: Number(event.target.value)})} + onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })} endpoint="/api/groups" label={t('groupParent')} - variant="filled" /> + variant="filled" + /> @@ -64,15 +69,15 @@ const GroupPage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={deviceAttributes} - /> + /> - } + )} ); -} +}; export default GroupPage; diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js index e2740627..d5921844 100644 --- a/modern/src/settings/GroupsPage.js +++ b/modern/src/settings/GroupsPage.js @@ -1,12 +1,14 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -27,37 +29,35 @@ const GroupsView = ({ updateTimestamp, onMenuClick }) => { return ( - - - - - {t('sharedName')} - - - - {items.map((item) => ( - - - onMenuClick(event.currentTarget, item.id)}> - - - - {item.name} +
+ + + + {t('sharedName')} - ))} - -
+ + + {items.map((item) => ( + + + onMenuClick(event.currentTarget, item.id)}> + + + + {item.name} + + ))} + +
); -} +}; -const GroupsPage = () => { - return ( - <> - - - - ); -} +const GroupsPage = () => ( + <> + + + +); export default GroupsPage; diff --git a/modern/src/settings/MaintenancePage.js b/modern/src/settings/MaintenancePage.js index 3b4fde54..89ebaa12 100644 --- a/modern/src/settings/MaintenancePage.js +++ b/modern/src/settings/MaintenancePage.js @@ -1,14 +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'; import t from '../common/localization'; import { prefixString } from '../common/stringUtils'; import EditItemView from '../EditItemView'; -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'; import EditAttributesView from '../attributes/EditAttributesView'; import positionAttributes from '../attributes/positionAttributes'; import { useAttributePreference } from '../common/preferences'; -import { speedFromKnots, speedToKnots, distanceFromMeters, distanceToMeters } from '../common/converter'; +import { + speedFromKnots, speedToKnots, distanceFromMeters, distanceToMeters, +} from '../common/converter'; const useStyles = makeStyles(() => ({ details: { @@ -17,46 +21,46 @@ const useStyles = makeStyles(() => ({ })); const MaintenancePage = () => { - const classes = useStyles(); const [item, setItem] = useState(); - const [labels, setLabels] = useState({start: '', period: ''}); + const [labels, setLabels] = useState({ start: '', period: '' }); const speedUnit = useAttributePreference('speedUnit'); const distanceUnit = useAttributePreference('distanceUnit'); const convertToList = (attributes) => { - let otherList = []; - for (const key in attributes) { + const otherList = []; + Object.keys(attributes).forEach((key) => { const value = attributes[key]; if (value.type === 'number') { - otherList.push({key, name: value.name, type: value.type}); + otherList.push({ key, name: value.name, type: value.type }); } - } + }); return otherList; - } + }; - const onMaintenanceTypeChange = event => { + const onMaintenanceTypeChange = (event) => { const newValue = event.target.value; - setItem({ ...item, type: newValue, start: 0, period: 0 }); + setItem({ + ...item, type: newValue, start: 0, period: 0, + }); const attribute = positionAttributes[newValue]; if (attribute && attribute.dataType) { switch (attribute.dataType) { case 'distance': - setLabels({ ...labels, start: t(prefixString('shared', distanceUnit)), period: t(prefixString('shared', distanceUnit))}); + setLabels({ ...labels, start: t(prefixString('shared', distanceUnit)), period: t(prefixString('shared', distanceUnit)) }); break; case 'speed': - setLabels({ ...labels, start: t(prefixString('shared', speedUnit)), period: t(prefixString('shared', speedUnit))}); + setLabels({ ...labels, start: t(prefixString('shared', speedUnit)), period: t(prefixString('shared', speedUnit)) }); break; default: break; } } - } - - const rawToValue = value => { + }; + const rawToValue = (value) => { const attribute = positionAttributes[item.type]; if (attribute && attribute.dataType) { switch (attribute.dataType) { @@ -69,10 +73,9 @@ const MaintenancePage = () => { } } return value; - } - - const valueToRaw = value => { + }; + const valueToRaw = (value) => { const attribute = positionAttributes[item.type]; if (attribute && attribute.dataType) { switch (attribute.dataType) { @@ -85,11 +88,12 @@ const MaintenancePage = () => { } } return value; - } + }; return ( - {item && + {item + && ( <> }> @@ -101,39 +105,43 @@ const MaintenancePage = () => { setItem({...item, name: event.target.value})} + onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} - variant="filled" /> + variant="filled" + /> {t('sharedType')} - + {convertToList(positionAttributes).map(({ key, name }) => ( + {name} + ))} - + setItem({...item, start: valueToRaw(event.target.value)})} + onChange={(event) => setItem({ ...item, start: valueToRaw(event.target.value) })} label={t('maintenanceStart')} variant="filled" InputProps={{ - endAdornment: {labels.start}, - }} /> + endAdornment: {labels.start}, + }} + /> setItem({...item, period: valueToRaw(event.target.value)})} + onChange={(event) => setItem({ ...item, period: valueToRaw(event.target.value) })} label={t('maintenancePeriod')} variant="filled" InputProps={{ - endAdornment: {labels.period}, - }} /> + endAdornment: {labels.period}, + }} + /> @@ -145,15 +153,15 @@ const MaintenancePage = () => { setItem({...item, attributes})} + setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={{}} - /> + /> - + - } + )} ); -} +}; export default MaintenancePage; diff --git a/modern/src/settings/MaintenancesPage.js b/modern/src/settings/MaintenancesPage.js index 7ba4bd29..e598bc90 100644 --- a/modern/src/settings/MaintenancesPage.js +++ b/modern/src/settings/MaintenancesPage.js @@ -1,7 +1,9 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; @@ -10,7 +12,7 @@ import positionAttributes from '../attributes/positionAttributes'; import { formatDistance, formatSpeed } from '../common/formatter'; import { useAttributePreference } from '../common/preferences'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -37,7 +39,7 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => { switch (attribute.dataType) { case 'speed': return formatSpeed(value, speedUnit); - case 'distance': + case 'distance': return formatDistance(value, distanceUnit); default: return value; @@ -45,47 +47,45 @@ const MaintenancesView = ({ updateTimestamp, onMenuClick }) => { } return value; - } + }; return ( - - - - - {t('sharedName')} - {t('sharedType')} - {t('maintenanceStart')} - {t('maintenancePeriod')} - - - - {items.map(item => ( - - - onMenuClick(event.currentTarget, item.id)}> - - - - {item.name} - {item.type} - {convertAttribute(item.type, item.start)} - {convertAttribute(item.type, item.period)} +
+ + + + {t('sharedName')} + {t('sharedType')} + {t('maintenanceStart')} + {t('maintenancePeriod')} - ))} - -
+ + + {items.map((item) => ( + + + onMenuClick(event.currentTarget, item.id)}> + + + + {item.name} + {item.type} + {convertAttribute(item.type, item.start)} + {convertAttribute(item.type, item.period)} + + ))} + +
); -} +}; -const MaintenacesPage = () => { - return ( - <> - - - - ); -} +const MaintenacesPage = () => ( + <> + + + +); export default MaintenacesPage; diff --git a/modern/src/settings/NotificationPage.js b/modern/src/settings/NotificationPage.js index 33904e7f..11c427fe 100644 --- a/modern/src/settings/NotificationPage.js +++ b/modern/src/settings/NotificationPage.js @@ -1,9 +1,11 @@ import React, { useState } from 'react'; +import { + Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import t, { findStringKeys } from '../common/localization'; import EditItemView from '../EditItemView'; -import { Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControlLabel, Checkbox } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { prefixString, unprefixString } from '../common/stringUtils'; import SelectField from '../form/SelectField'; @@ -18,14 +20,15 @@ const NotificationPage = () => { const [item, setItem] = useState(); - const alarms = findStringKeys(it => it.startsWith('alarm')).map(it => ({ + const alarms = findStringKeys((it) => it.startsWith('alarm')).map((it) => ({ key: unprefixString('alarm', it), name: t(it), })); return ( - {item && + {item + && ( <> }> @@ -38,47 +41,52 @@ const NotificationPage = () => { margin="normal" value={item.type || 'alarm'} emptyValue={null} - onChange={e => setItem({...item, type: e.target.value})} + onChange={(e) => setItem({ ...item, type: e.target.value })} endpoint="/api/notifications/types" - keyGetter={it => it.type} - titleGetter={it => t(prefixString('event', it.type))} + keyGetter={(it) => it.type} + titleGetter={(it) => t(prefixString('event', it.type))} label={t('sharedType')} - variant="filled" /> + variant="filled" + /> setItem({...item, notificators: e.target.value.join()})} + onChange={(e) => setItem({ ...item, notificators: e.target.value.join() })} endpoint="/api/notifications/notificators" - keyGetter={it => it.type} - titleGetter={it => t(prefixString('notificator', it.type))} + keyGetter={(it) => it.type} + titleGetter={(it) => t(prefixString('notificator', it.type))} label={t('notificationNotificators')} - variant="filled" /> - {(!item.type || item.type === 'alarm') && + variant="filled" + /> + {(!item.type || item.type === 'alarm') + && ( setItem({...item, attributes: {...item.attributes, alarms: e.target.value.join()}})} + onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, alarms: e.target.value.join() } })} data={alarms} - keyGetter={it => it.key} + keyGetter={(it) => it.key} label={t('sharedAlarms')} - variant="filled" /> - } + variant="filled" + /> + )} setItem({...item, always: event.target.checked})} - /> - } - label={t('notificationAlways')} /> + onChange={(event) => setItem({ ...item, always: event.target.checked })} + /> + )} + label={t('notificationAlways')} + /> - } + )} ); -} +}; export default NotificationPage; diff --git a/modern/src/settings/NotificationsPage.js b/modern/src/settings/NotificationsPage.js index 15da0de7..4e052927 100644 --- a/modern/src/settings/NotificationsPage.js +++ b/modern/src/settings/NotificationsPage.js @@ -1,14 +1,16 @@ import React, { useState } from 'react'; -import MainToolbar from '../MainToolbar'; -import { TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton } from '@material-ui/core'; +import { + TableContainer, Table, TableRow, TableCell, TableHead, TableBody, makeStyles, IconButton, +} from '@material-ui/core'; import MoreVertIcon from '@material-ui/icons/MoreVert'; +import MainToolbar from '../MainToolbar'; import t from '../common/localization'; import { useEffectAsync } from '../reactHelper'; import EditCollectionView from '../EditCollectionView'; import { prefixString } from '../common/stringUtils'; import { formatBoolean } from '../common/formatter'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles((theme) => ({ columnAction: { width: theme.spacing(1), padding: theme.spacing(0, 1), @@ -32,7 +34,7 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => { return value .split(/[, ]+/) .filter(Boolean) - .map(it => t(prefixString(prefix, it))) + .map((it) => t(prefixString(prefix, it))) .join(', '); } return ''; @@ -40,43 +42,41 @@ const NotificationsView = ({ updateTimestamp, onMenuClick }) => { return ( - - - - - {t('notificationType')} - {t('notificationAlways')} - {t('sharedAlarms')} - {t('notificationNotificators')} - - - - {items.map(item => ( - - - onMenuClick(event.currentTarget, item.id)}> - - - - {t(prefixString('event', item.type))} - {formatBoolean(item.always)} - {formatList('alarm', item.attributes.alarms)} - {formatList('notificator', item.notificators)} +
+ + + + {t('notificationType')} + {t('notificationAlways')} + {t('sharedAlarms')} + {t('notificationNotificators')} - ))} - -
+ + + {items.map((item) => ( + + + onMenuClick(event.currentTarget, item.id)}> + + + + {t(prefixString('event', item.type))} + {formatBoolean(item.always)} + {formatList('alarm', item.attributes.alarms)} + {formatList('notificator', item.notificators)} + + ))} + +
); -} +}; -const NotificationsPage = () => { - return ( - <> - - - - ); -} +const NotificationsPage = () => ( + <> + + + +); export default NotificationsPage; diff --git a/modern/src/setupProxy.js b/modern/src/setupProxy.js index c16655c8..3ab7643e 100644 --- a/modern/src/setupProxy.js +++ b/modern/src/setupProxy.js @@ -1,6 +1,8 @@ +/* eslint-disable import/no-extraneous-dependencies */ + const proxy = require('http-proxy-middleware'); -module.exports = function (app) { - app.use(proxy('/api/socket', { target: 'ws://' + process.env.REACT_APP_URL_NAME, ws: true })); - app.use(proxy('/api', { target: 'http://' + process.env.REACT_APP_URL_NAME })); +export default (app) => { + app.use(proxy('/api/socket', { target: `ws://${process.env.REACT_APP_URL_NAME}`, ws: true })); + app.use(proxy('/api', { target: `http://${process.env.REACT_APP_URL_NAME}` })); }; diff --git a/modern/src/store/devices.js b/modern/src/store/devices.js index 66c1607f..cca23cb9 100644 --- a/modern/src/store/devices.js +++ b/modern/src/store/devices.js @@ -4,15 +4,15 @@ const { reducer, actions } = createSlice({ name: 'devices', initialState: { items: {}, - selectedId: null + selectedId: null, }, reducers: { refresh(state, action) { state.items = {}; - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, update(state, action) { - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, select(state, action) { state.selectedId = action.payload.id; @@ -20,7 +20,7 @@ const { reducer, actions } = createSlice({ remove(state, action) { delete state.items[action.payload]; }, - } + }, }); export { actions as devicesActions }; diff --git a/modern/src/store/drivers.js b/modern/src/store/drivers.js index 63522d78..38933d84 100644 --- a/modern/src/store/drivers.js +++ b/modern/src/store/drivers.js @@ -7,9 +7,9 @@ const { reducer, actions } = createSlice({ }, reducers: { update(state, action) { - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, - } + }, }); export { actions as driversActions }; diff --git a/modern/src/store/geofences.js b/modern/src/store/geofences.js index 504b7d02..f2b7666a 100644 --- a/modern/src/store/geofences.js +++ b/modern/src/store/geofences.js @@ -8,12 +8,12 @@ const { reducer, actions } = createSlice({ reducers: { refresh(state, action) { state.items = {}; - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, update(state, action) { - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, - } + }, }); export { actions as geofencesActions }; diff --git a/modern/src/store/groups.js b/modern/src/store/groups.js index 483323f3..11fc5dbf 100644 --- a/modern/src/store/groups.js +++ b/modern/src/store/groups.js @@ -7,9 +7,9 @@ const { reducer, actions } = createSlice({ }, reducers: { update(state, action) { - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, - } + }, }); export { actions as groupsActions }; diff --git a/modern/src/store/maintenances.js b/modern/src/store/maintenances.js index 0813f6b6..08b2adb6 100644 --- a/modern/src/store/maintenances.js +++ b/modern/src/store/maintenances.js @@ -7,9 +7,9 @@ const { reducer, actions } = createSlice({ }, reducers: { update(state, action) { - action.payload.forEach(item => state.items[item['id']] = item); + action.payload.forEach((item) => state.items[item.id] = item); }, - } + }, }); export { actions as maintenancesActions }; diff --git a/modern/src/store/positions.js b/modern/src/store/positions.js index 1df468cd..8858298d 100644 --- a/modern/src/store/positions.js +++ b/modern/src/store/positions.js @@ -7,9 +7,9 @@ const { reducer, actions } = createSlice({ }, reducers: { update(state, action) { - action.payload.forEach(item => state.items[item['deviceId']] = item); + action.payload.forEach((item) => state.items[item.deviceId] = item); }, - } + }, }); export { actions as positionsActions }; diff --git a/modern/src/theme/dimensions.js b/modern/src/theme/dimensions.js index a2403abe..fcdbaee5 100644 --- a/modern/src/theme/dimensions.js +++ b/modern/src/theme/dimensions.js @@ -8,5 +8,5 @@ export default { columnWidthDate: 160, columnWidthNumber: 130, columnWidthString: 160, - columnWidthBoolean: 130 + columnWidthBoolean: 130, }; diff --git a/modern/src/theme/index.js b/modern/src/theme/index.js index 5a3b2a9c..dc0a35bf 100644 --- a/modern/src/theme/index.js +++ b/modern/src/theme/index.js @@ -6,7 +6,7 @@ import dimensions from './dimensions'; const theme = createMuiTheme({ palette, overrides, - dimensions + dimensions, }); export default theme; diff --git a/modern/src/theme/overrides.js b/modern/src/theme/overrides.js index cd6c5a5a..a6d08cf1 100644 --- a/modern/src/theme/overrides.js +++ b/modern/src/theme/overrides.js @@ -4,17 +4,17 @@ export default { MuiFormControl: { root: { marginTop: 5, - marginBottom: 5 - } + marginBottom: 5, + }, }, MuiInputLabel: { filled: { transform: 'translate(12px, 14px) scale(1)', - '&$shrink' :{ - transform: 'translate(12px, -14px) scale(0.72)' - } + '&$shrink': { + transform: 'translate(12px, -14px) scale(0.72)', + }, }, - }, + }, MuiFilledInput: { root: { height: dimensions.inputHeight, @@ -28,19 +28,19 @@ export default { boxSizing: 'border-box', '&:-webkit-autofill': { WebkitBoxShadow: '0 0 0 100px #eeeeee inset', - }, + }, }, underline: { - "&:before": { + '&:before': { borderBottom: 'none', }, - "&:after": { + '&:after': { borderBottom: 'none', }, - "&:hover:before": { + '&:hover:before': { borderBottom: 'none', - }, - } + }, + }, }, MuiButton: { root: { @@ -48,31 +48,31 @@ export default { marginTop: 5, marginBottom: 5, '&$disabled': { - opacity: .4, + opacity: 0.4, color: undefined, - } + }, }, contained: { '&$disabled': { - opacity: .4, + opacity: 0.4, color: undefined, - backgroundColor: undefined - } - } + backgroundColor: undefined, + }, + }, }, MuiFormHelperText: { root: { - marginBottom: -10 + marginBottom: -10, }, contained: { - marginLeft: 12 - } + marginLeft: 12, + }, }, MuiAutocomplete: { inputRoot: { '&.MuiFilledInput-root': { - paddingTop: 0 - } - } - } + paddingTop: 0, + }, + }, + }, }; -- cgit v1.2.3 From e0a5e650ba33f4add5413124c84d340916065f3c Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 10 Jul 2021 15:31:39 -0700 Subject: Enable new app linting --- .github/workflows/new.yml | 20 ++++++++++++++++++++ .github/workflows/node.js.yml | 18 ------------------ .github/workflows/old.yml | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/new.yml delete mode 100644 .github/workflows/node.js.yml create mode 100644 .github/workflows/old.yml diff --git a/.github/workflows/new.yml b/.github/workflows/new.yml new file mode 100644 index 00000000..58956106 --- /dev/null +++ b/.github/workflows/new.yml @@ -0,0 +1,20 @@ +name: New App Lint + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + - run: npm install + working-directory: modern + - run: npm run lint + working-directory: modern diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index a4ba2ea9..00000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Node.js CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - - run: npm install - - run: npm run lint diff --git a/.github/workflows/old.yml b/.github/workflows/old.yml new file mode 100644 index 00000000..2f9f39f0 --- /dev/null +++ b/.github/workflows/old.yml @@ -0,0 +1,18 @@ +name: Old App Lint + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + - run: npm install + - run: npm run lint -- cgit v1.2.3 From 702e674ecce7de1a4f4318ef8f30c572264a4560 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 10 Jul 2021 15:40:42 -0700 Subject: Fix --- modern/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/modern/package.json b/modern/package.json index f986b314..365d9865 100644 --- a/modern/package.json +++ b/modern/package.json @@ -51,6 +51,7 @@ "devDependencies": { "eslint": "^6.8.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" } -- cgit v1.2.3