aboutsummaryrefslogtreecommitdiff
path: root/modern/src/settings/UserPage.jsx
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
committerAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
commitf418231b6b2f5e030a0d2dcc390c314602b1f740 (patch)
tree10326adf3792bc2697e06bb5f2b8ef2a8f7e55fe /modern/src/settings/UserPage.jsx
parentb392a4af78e01c8e0f50aad5468e9583675b24be (diff)
downloadtrackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.gz
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.bz2
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.zip
Move modern to the top
Diffstat (limited to 'modern/src/settings/UserPage.jsx')
-rw-r--r--modern/src/settings/UserPage.jsx428
1 files changed, 0 insertions, 428 deletions
diff --git a/modern/src/settings/UserPage.jsx b/modern/src/settings/UserPage.jsx
deleted file mode 100644
index 6748dd31..00000000
--- a/modern/src/settings/UserPage.jsx
+++ /dev/null
@@ -1,428 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import { useNavigate, useParams } from 'react-router-dom';
-import {
- Accordion,
- AccordionSummary,
- AccordionDetails,
- Typography,
- FormControl,
- InputLabel,
- Select,
- MenuItem,
- FormControlLabel,
- Checkbox,
- FormGroup,
- TextField,
- Button,
- InputAdornment,
- IconButton,
- OutlinedInput,
-} from '@mui/material';
-import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
-import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
-import CachedIcon from '@mui/icons-material/Cached';
-import CloseIcon from '@mui/icons-material/Close';
-import { useDispatch, useSelector } from 'react-redux';
-import dayjs from 'dayjs';
-import EditItemView from './components/EditItemView';
-import EditAttributesAccordion from './components/EditAttributesAccordion';
-import { useTranslation } from '../common/components/LocalizationProvider';
-import useUserAttributes from '../common/attributes/useUserAttributes';
-import { sessionActions } from '../store';
-import SelectField from '../common/components/SelectField';
-import SettingsMenu from './components/SettingsMenu';
-import useCommonUserAttributes from '../common/attributes/useCommonUserAttributes';
-import { useAdministrator, useRestriction, useManager } from '../common/util/permissions';
-import useQuery from '../common/util/useQuery';
-import { useCatch } from '../reactHelper';
-import useMapStyles from '../map/core/useMapStyles';
-import { map } from '../map/core/MapView';
-import useSettingsStyles from './common/useSettingsStyles';
-
-const UserPage = () => {
- const classes = useSettingsStyles();
- const navigate = useNavigate();
- const dispatch = useDispatch();
- const t = useTranslation();
-
- const admin = useAdministrator();
- const manager = useManager();
- const fixedEmail = useRestriction('fixedEmail');
-
- const currentUser = useSelector((state) => state.session.user);
- const registrationEnabled = useSelector((state) => state.session.server.registration);
- const openIdForced = useSelector((state) => state.session.server.openIdForce);
- const totpEnable = useSelector((state) => state.session.server.attributes.totpEnable);
- const totpForce = useSelector((state) => state.session.server.attributes.totpForce);
-
- const mapStyles = useMapStyles();
- const commonUserAttributes = useCommonUserAttributes(t);
- const userAttributes = useUserAttributes(t);
-
- const { id } = useParams();
- const [item, setItem] = useState(id === currentUser.id.toString() ? currentUser : null);
-
- const [deleteEmail, setDeleteEmail] = useState();
- const [deleteFailed, setDeleteFailed] = useState(false);
-
- const handleDelete = useCatch(async () => {
- if (deleteEmail === currentUser.email) {
- setDeleteFailed(false);
- const response = await fetch(`/api/users/${currentUser.id}`, { method: 'DELETE' });
- if (response.ok) {
- navigate('/login');
- dispatch(sessionActions.updateUser(null));
- } else {
- throw Error(await response.text());
- }
- } else {
- setDeleteFailed(true);
- }
- });
-
- const handleGenerateTotp = useCatch(async () => {
- const response = await fetch('/api/users/totp', { method: 'POST' });
- if (response.ok) {
- setItem({ ...item, totpKey: await response.text() });
- } else {
- throw Error(await response.text());
- }
- });
-
- const query = useQuery();
- const [queryHandled, setQueryHandled] = useState(false);
- const attribute = query.get('attribute');
-
- useEffect(() => {
- if (!queryHandled && item && attribute) {
- if (!item.attributes.hasOwnProperty('attribute')) {
- const updatedAttributes = { ...item.attributes };
- updatedAttributes[attribute] = '';
- setItem({ ...item, attributes: updatedAttributes });
- }
- setQueryHandled(true);
- }
- }, [item, queryHandled, setQueryHandled, attribute]);
-
- const onItemSaved = (result) => {
- if (result.id === currentUser.id) {
- dispatch(sessionActions.updateUser(result));
- }
- };
-
- const validate = () => item && item.name && item.email && (item.id || item.password) && (admin || !totpForce || item.totpKey);
-
- return (
- <EditItemView
- endpoint="users"
- item={item}
- setItem={setItem}
- defaultItem={admin ? { deviceLimit: -1 } : {}}
- validate={validate}
- onItemSaved={onItemSaved}
- menu={<SettingsMenu />}
- breadcrumbs={['settingsTitle', 'settingsUser']}
- >
- {item && (
- <>
- <Accordion defaultExpanded={!attribute}>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1">
- {t('sharedRequired')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <TextField
- value={item.name || ''}
- onChange={(e) => setItem({ ...item, name: e.target.value })}
- label={t('sharedName')}
- />
- <TextField
- value={item.email || ''}
- onChange={(e) => setItem({ ...item, email: e.target.value })}
- label={t('userEmail')}
- disabled={fixedEmail}
- />
- {!openIdForced && (
- <TextField
- type="password"
- onChange={(e) => setItem({ ...item, password: e.target.value })}
- label={t('userPassword')}
- />
- )}
- {totpEnable && (
- <FormControl>
- <InputLabel>{t('loginTotpKey')}</InputLabel>
- <OutlinedInput
- readOnly
- label={t('loginTotpKey')}
- value={item.totpKey || ''}
- endAdornment={(
- <InputAdornment position="end">
- <IconButton size="small" edge="end" onClick={handleGenerateTotp}>
- <CachedIcon fontSize="small" />
- </IconButton>
- <IconButton size="small" edge="end" onClick={() => setItem({ ...item, totpKey: null })}>
- <CloseIcon fontSize="small" />
- </IconButton>
- </InputAdornment>
- )}
- />
- </FormControl>
- )}
- </AccordionDetails>
- </Accordion>
- <Accordion>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1">
- {t('sharedPreferences')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <TextField
- value={item.phone || ''}
- onChange={(e) => setItem({ ...item, phone: e.target.value })}
- label={t('sharedPhone')}
- />
- <FormControl>
- <InputLabel>{t('mapDefault')}</InputLabel>
- <Select
- label={t('mapDefault')}
- value={item.map || 'locationIqStreets'}
- onChange={(e) => setItem({ ...item, map: e.target.value })}
- >
- {mapStyles.filter((style) => style.available).map((style) => (
- <MenuItem key={style.id} value={style.id}>
- <Typography component="span">{style.title}</Typography>
- </MenuItem>
- ))}
- </Select>
- </FormControl>
- <FormControl>
- <InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
- <Select
- label={t('settingsCoordinateFormat')}
- value={item.coordinateFormat || 'dd'}
- onChange={(e) => setItem({ ...item, coordinateFormat: e.target.value })}
- >
- <MenuItem value="dd">{t('sharedDecimalDegrees')}</MenuItem>
- <MenuItem value="ddm">{t('sharedDegreesDecimalMinutes')}</MenuItem>
- <MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
- </Select>
- </FormControl>
- <FormControl>
- <InputLabel>{t('settingsSpeedUnit')}</InputLabel>
- <Select
- label={t('settingsSpeedUnit')}
- value={(item.attributes && item.attributes.speedUnit) || 'kn'}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, speedUnit: e.target.value } })}
- >
- <MenuItem value="kn">{t('sharedKn')}</MenuItem>
- <MenuItem value="kmh">{t('sharedKmh')}</MenuItem>
- <MenuItem value="mph">{t('sharedMph')}</MenuItem>
- </Select>
- </FormControl>
- <FormControl>
- <InputLabel>{t('settingsDistanceUnit')}</InputLabel>
- <Select
- label={t('settingsDistanceUnit')}
- value={(item.attributes && item.attributes.distanceUnit) || 'km'}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, distanceUnit: e.target.value } })}
- >
- <MenuItem value="km">{t('sharedKm')}</MenuItem>
- <MenuItem value="mi">{t('sharedMi')}</MenuItem>
- <MenuItem value="nmi">{t('sharedNmi')}</MenuItem>
- </Select>
- </FormControl>
- <FormControl>
- <InputLabel>{t('settingsAltitudeUnit')}</InputLabel>
- <Select
- label={t('settingsAltitudeUnit')}
- value={(item.attributes && item.attributes.altitudeUnit) || 'm'}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, altitudeUnit: e.target.value } })}
- >
- <MenuItem value="m">{t('sharedMeters')}</MenuItem>
- <MenuItem value="ft">{t('sharedFeet')}</MenuItem>
- </Select>
- </FormControl>
- <FormControl>
- <InputLabel>{t('settingsVolumeUnit')}</InputLabel>
- <Select
- label={t('settingsVolumeUnit')}
- value={(item.attributes && item.attributes.volumeUnit) || 'ltr'}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, volumeUnit: e.target.value } })}
- >
- <MenuItem value="ltr">{t('sharedLiter')}</MenuItem>
- <MenuItem value="usGal">{t('sharedUsGallon')}</MenuItem>
- <MenuItem value="impGal">{t('sharedImpGallon')}</MenuItem>
- </Select>
- </FormControl>
- <SelectField
- value={item.attributes && item.attributes.timezone}
- onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, timezone: e.target.value } })}
- endpoint="/api/server/timezones"
- keyGetter={(it) => it}
- titleGetter={(it) => it}
- label={t('sharedTimezone')}
- />
- <TextField
- value={item.poiLayer || ''}
- onChange={(e) => setItem({ ...item, poiLayer: e.target.value })}
- label={t('mapPoiLayer')}
- />
- <FormGroup>
- <FormControlLabel
- control={<Checkbox checked={item.twelveHourFormat} onChange={(e) => setItem({ ...item, twelveHourFormat: e.target.checked })} />}
- label={t('settingsTwelveHourFormat')}
- />
- </FormGroup>
- </AccordionDetails>
- </Accordion>
- <Accordion>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1">
- {t('sharedLocation')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <TextField
- type="number"
- value={item.latitude || 0}
- onChange={(e) => setItem({ ...item, latitude: Number(e.target.value) })}
- label={t('positionLatitude')}
- />
- <TextField
- type="number"
- value={item.longitude || 0}
- onChange={(e) => setItem({ ...item, longitude: Number(e.target.value) })}
- label={t('positionLongitude')}
- />
- <TextField
- type="number"
- value={item.zoom || 0}
- onChange={(e) => setItem({ ...item, zoom: Number(e.target.value) })}
- label={t('serverZoom')}
- />
- <Button
- variant="outlined"
- color="primary"
- onClick={() => {
- const { lng, lat } = map.getCenter();
- setItem({
- ...item,
- latitude: Number(lat.toFixed(6)),
- longitude: Number(lng.toFixed(6)),
- zoom: Number(map.getZoom().toFixed(1)),
- });
- }}
- >
- {t('mapCurrentLocation')}
- </Button>
- </AccordionDetails>
- </Accordion>
- <Accordion>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1">
- {t('sharedPermissions')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <TextField
- label={t('userExpirationTime')}
- type="date"
- value={(item.expirationTime && dayjs(item.expirationTime).locale('en').format('YYYY-MM-DD')) || '2099-01-01'}
- onChange={(e) => setItem({ ...item, expirationTime: dayjs(e.target.value, 'YYYY-MM-DD').locale('en').format() })}
- disabled={!manager}
- />
- <TextField
- type="number"
- value={item.deviceLimit || 0}
- onChange={(e) => setItem({ ...item, deviceLimit: Number(e.target.value) })}
- label={t('userDeviceLimit')}
- disabled={!admin}
- />
- <TextField
- type="number"
- value={item.userLimit || 0}
- onChange={(e) => setItem({ ...item, userLimit: Number(e.target.value) })}
- label={t('userUserLimit')}
- disabled={!admin}
- />
- <FormGroup>
- <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}
- />
- <FormControlLabel
- control={<Checkbox checked={item.fixedEmail} onChange={(e) => setItem({ ...item, fixedEmail: e.target.checked })} />}
- label={t('userFixedEmail')}
- disabled={!manager}
- />
- </FormGroup>
- </AccordionDetails>
- </Accordion>
- <EditAttributesAccordion
- attribute={attribute}
- attributes={item.attributes}
- setAttributes={(attributes) => setItem({ ...item, attributes })}
- definitions={{ ...commonUserAttributes, ...userAttributes }}
- focusAttribute={attribute}
- />
- {registrationEnabled && item.id === currentUser.id && !manager && (
- <Accordion>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="subtitle1" color="error">
- {t('userDeleteAccount')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.details}>
- <TextField
- value={deleteEmail}
- onChange={(e) => setDeleteEmail(e.target.value)}
- label={t('userEmail')}
- error={deleteFailed}
- />
- <Button
- variant="outlined"
- color="error"
- onClick={handleDelete}
- startIcon={<DeleteForeverIcon />}
- >
- {t('userDeleteAccount')}
- </Button>
- </AccordionDetails>
- </Accordion>
- )}
- </>
- )}
- </EditItemView>
- );
-};
-
-export default UserPage;