diff options
-rw-r--r-- | modern/src/common/util/permissions.js | 2 | ||||
-rw-r--r-- | modern/src/settings/UserPage.js | 218 | ||||
-rw-r--r-- | modern/src/settings/components/EditItemView.js | 4 |
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]); |