aboutsummaryrefslogtreecommitdiff
path: root/modern/src
diff options
context:
space:
mode:
Diffstat (limited to 'modern/src')
-rw-r--r--modern/src/common/util/permissions.js2
-rw-r--r--modern/src/settings/UserPage.js218
-rw-r--r--modern/src/settings/components/EditItemView.js4
3 files changed, 220 insertions, 4 deletions
diff --git a/modern/src/common/util/permissions.js b/modern/src/common/util/permissions.js
index 72ca0b08..27a9ba4f 100644
--- a/modern/src/common/util/permissions.js
+++ b/modern/src/common/util/permissions.js
@@ -2,6 +2,8 @@ import { useSelector } from 'react-redux';
export const useAdministrator = () => useSelector((state) => state.session.user?.administrator);
+export const useManager = () => useSelector((state) => state.session.user?.administrator || (state.session.user?.userLimit || 0) > 0);
+
export const useReadonly = () => useSelector((state) => state.session.server?.readonly || state.session.user?.readonly);
export const useDeviceReadonly = () => useSelector((state) => state.session.server?.readonly || state.session.user?.readonly
diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js
index 10e49690..a4a5b0fb 100644
--- a/modern/src/settings/UserPage.js
+++ b/modern/src/settings/UserPage.js
@@ -2,9 +2,10 @@ import React, { useState } from 'react';
import TextField from '@material-ui/core/TextField';
import {
- Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, Select, MenuItem,
+ Accordion, AccordionSummary, AccordionDetails, makeStyles, Typography, FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox, InputAdornment, IconButton, FilledInput,
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import CachedIcon from '@material-ui/icons/Cached';
import { useDispatch, useSelector } from 'react-redux';
import EditItemView from './components/EditItemView';
import EditAttributesView from './components/EditAttributesView';
@@ -15,6 +16,9 @@ import { sessionActions } from '../store';
import SelectField from '../common/components/SelectField';
import SettingsMenu from './components/SettingsMenu';
import useCommonUserAttributes from '../common/attributes/useCommonUserAttributes';
+import { useAdministrator, useManager } from '../common/util/permissions';
+import { prefixString } from '../common/util/stringUtils';
+import moment from 'moment';
const useStyles = makeStyles(() => ({
details: {
@@ -27,6 +31,9 @@ const UserPage = () => {
const dispatch = useDispatch();
const t = useTranslation();
+ const admin = useAdministrator();
+ const manager = useManager();
+
const currentUserId = useSelector((state) => state.session.user.id);
const commonUserAttributes = useCommonUserAttributes(t);
@@ -47,6 +54,7 @@ const UserPage = () => {
endpoint="users"
item={item}
setItem={setItem}
+ defaultItem={{ deviceLimit: -1 }}
validate={validate}
onItemSaved={onItemSaved}
menu={<SettingsMenu />}
@@ -98,6 +106,41 @@ const UserPage = () => {
label={t('sharedPhone')}
variant="filled"
/>
+ <TextField
+ margin="normal"
+ type="number"
+ value={item.latitude || 0}
+ onChange={(event) => setItem({ ...item, latitude: Number(event.target.value) })}
+ label={t('positionLatitude')}
+ variant="filled"
+ />
+ <TextField
+ margin="normal"
+ type="number"
+ value={item.longitude || 0}
+ onChange={(event) => setItem({ ...item, longitude: Number(event.target.value) })}
+ label={t('positionLongitude')}
+ variant="filled"
+ />
+ <TextField
+ margin="normal"
+ type="number"
+ value={item.zoom || 0}
+ onChange={(event) => setItem({ ...item, zoom: Number(event.target.value) })}
+ label={t('serverZoom')}
+ variant="filled"
+ />
+ <FormControl variant="filled" margin="normal" fullWidth>
+ <InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
+ <Select
+ value={item.coordinateFormat || 'dd'}
+ onChange={(event) => setItem({ ...item, coordinateFormat: event.target.value })}
+ >
+ <MenuItem value="dd">{t('sharedDecimalDegrees')}</MenuItem>
+ <MenuItem value="ddm">{t('sharedDegreesDecimalMinutes')}</MenuItem>
+ <MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
+ </Select>
+ </FormControl>
<FormControl variant="filled" margin="normal" fullWidth>
<InputLabel>{t('settingsSpeedUnit')}</InputLabel>
<Select
@@ -149,6 +192,94 @@ const UserPage = () => {
label={t('mapPoiLayer')}
variant="filled"
/>
+ <FormControlLabel
+ control={<Checkbox checked={item.twelveHourFormat} onChange={(event) => setItem({ ...item, twelveHourFormat: event.target.checked })} />}
+ label={t('settingsTwelveHourFormat')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedPermissions')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <FormControl variant="filled" margin="normal">
+ <InputLabel>{t('userToken')}</InputLabel>
+ <FilledInput
+ type="text"
+ value={item.token || ''}
+ onChange={(e) => setItem({ ...item, token: e.target.value })}
+ endAdornment={(
+ <InputAdornment position="end">
+ <IconButton onClick={() => {
+ const token = [...Array(30)].map(() => Math.random().toString(36)[2]).join('');
+ setItem({ ...item, token });
+ }}>
+ <CachedIcon />
+ </IconButton>
+ </InputAdornment>
+ )}
+ />
+ </FormControl>
+ <TextField
+ margin="normal"
+ variant="filled"
+ label={t('userExpirationTime')}
+ type="date"
+ value={(item.expirationTime && item.expirationTime.format(moment.HTML5_FMT.DATE)) || '2999-01-01'}
+ onChange={(e) => setItem({ ...item, expirationTime: moment(e.target.value, moment.HTML5_FMT.DATE) })}
+ disabled={!manager}
+ />
+ <TextField
+ margin="normal"
+ type="number"
+ value={item.deviceLimit || 0}
+ onChange={(e) => setItem({ ...item, deviceLimit: Number(e.target.value) })}
+ label={t('userDeviceLimit')}
+ variant="filled"
+ disabled={!admin}
+ />
+ <TextField
+ margin="normal"
+ type="number"
+ value={item.userLimit || 0}
+ onChange={(e) => setItem({ ...item, userLimit: Number(e.target.value) })}
+ label={t('userUserLimit')}
+ variant="filled"
+ disabled={!admin}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.disabled} onChange={(e) => setItem({ ...item, disabled: e.target.checked })} />}
+ label={t('sharedDisabled')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.administrator} onChange={(e) => setItem({ ...item, administrator: e.target.checked })} />}
+ label={t('userAdmin')}
+ disabled={!admin}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.readonly} onChange={(e) => setItem({ ...item, readonly: e.target.checked })} />}
+ label={t('serverReadonly')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.deviceReadonly} onChange={(e) => setItem({ ...item, deviceReadonly: e.target.checked })} />}
+ label={t('userDeviceReadonly')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.limitCommands} onChange={(e) => setItem({ ...item, limitCommands: e.target.checked })} />}
+ label={t('userLimitCommands')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.disableReports} onChange={(e) => setItem({ ...item, disableReports: e.target.checked })} />}
+ label={t('userDisableReports')}
+ disabled={!manager}
+ />
</AccordionDetails>
</Accordion>
<Accordion>
@@ -165,7 +296,7 @@ const UserPage = () => {
/>
</AccordionDetails>
</Accordion>
- {item.id && (
+ {item.id && manager && (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="subtitle1">
@@ -193,6 +324,89 @@ const UserPage = () => {
label={t('settingsGroups')}
variant="filled"
/>
+ <LinkField
+ margin="normal"
+ endpointAll="/api/geofences?all=true"
+ endpointLinked={`/api/geofences?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="geofenceId"
+ label={t('sharedGeofences')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/notifications?all=true"
+ endpointLinked={`/api/notifications?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="notificationId"
+ titleGetter={(it) => t(prefixString('event', it.type))}
+ label={t('sharedNotifications')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/calendars?all=true"
+ endpointLinked={`/api/calendars?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="calendarId"
+ label={t('sharedCalendars')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/users?all=true"
+ endpointLinked={`/api/users?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="managedUserId"
+ label={t('settingsUsers')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/attributes/computed?all=true"
+ endpointLinked={`/api/attributes/computed?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="attributeId"
+ titleGetter={(it) => it.description}
+ label={t('sharedComputedAttributes')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/drivers?all=true"
+ endpointLinked={`/api/drivers?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="driverId"
+ label={t('sharedDrivers')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/commands?all=true"
+ endpointLinked={`/api/commands?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="commandId"
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommands')}
+ variant="filled"
+ />
+ <LinkField
+ margin="normal"
+ endpointAll="/api/maintenance?all=true"
+ endpointLinked={`/api/maintenance?userId=${item.id}`}
+ baseId={item.id}
+ keyBase="userId"
+ keyLink="maintenanceId"
+ label={t('sharedMaintenance')}
+ variant="filled"
+ />
</AccordionDetails>
</Accordion>
)}
diff --git a/modern/src/settings/components/EditItemView.js b/modern/src/settings/components/EditItemView.js
index 2283ebda..9b9918fe 100644
--- a/modern/src/settings/components/EditItemView.js
+++ b/modern/src/settings/components/EditItemView.js
@@ -23,7 +23,7 @@ const useStyles = makeStyles((theme) => ({
}));
const EditItemView = ({
- children, endpoint, item, setItem, validate, onItemSaved, menu, breadcrumbs,
+ children, endpoint, item, setItem, defaultItem, validate, onItemSaved, menu, breadcrumbs,
}) => {
const history = useHistory();
const classes = useStyles();
@@ -38,7 +38,7 @@ const EditItemView = ({
setItem(await response.json());
}
} else {
- setItem({});
+ setItem(defaultItem || {});
}
}, [id]);