diff options
author | Anton Tananaev <anton@traccar.org> | 2022-05-06 14:15:23 -0700 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2022-05-06 14:15:23 -0700 |
commit | 3812292cc4413efff1358eb34cc5bccab9d1cbae (patch) | |
tree | 8aa26184ec58a7f231869d427a28f8e999fa2093 | |
parent | 1b513d2cbf4dde335f9a2822beaaeeb171816c8d (diff) | |
download | trackermap-web-3812292cc4413efff1358eb34cc5bccab9d1cbae.tar.gz trackermap-web-3812292cc4413efff1358eb34cc5bccab9d1cbae.tar.bz2 trackermap-web-3812292cc4413efff1358eb34cc5bccab9d1cbae.zip |
Handle unit attributes
-rw-r--r-- | modern/src/DevicePage.js | 13 | ||||
-rw-r--r-- | modern/src/attributes/EditAttributesView.js | 77 | ||||
-rw-r--r-- | modern/src/attributes/useDeviceAttributes.js | 3 | ||||
-rw-r--r-- | modern/src/common/converter.js | 58 | ||||
-rw-r--r-- | modern/src/common/formatter.js | 37 |
5 files changed, 134 insertions, 54 deletions
diff --git a/modern/src/DevicePage.js b/modern/src/DevicePage.js index e4b853e7..560bbd20 100644 --- a/modern/src/DevicePage.js +++ b/modern/src/DevicePage.js @@ -13,6 +13,7 @@ import LinkField from './form/LinkField'; import { prefixString } from './common/stringUtils'; import { useTranslation } from './LocalizationProvider'; import useDeviceAttributes from './attributes/useDeviceAttributes'; +import { useAdministrator } from './common/permissions'; const useStyles = makeStyles(() => ({ details: { @@ -24,6 +25,8 @@ const DevicePage = () => { const classes = useStyles(); const t = useTranslation(); + const admin = useAdministrator(); + const deviceAttributes = useDeviceAttributes(t); const [item, setItem] = useState(); @@ -105,10 +108,12 @@ const DevicePage = () => { label={t('deviceCategory')} variant="filled" /> - <FormControlLabel - control={<Checkbox checked={item.disabled} onChange={(event) => setItem({ ...item, disabled: event.target.checked })} />} - label={t('sharedDisabled')} - /> + {admin && ( + <FormControlLabel + control={<Checkbox checked={item.disabled} onChange={(event) => setItem({ ...item, disabled: event.target.checked })} />} + label={t('sharedDisabled')} + /> + )} </AccordionDetails> </Accordion> <Accordion> diff --git a/modern/src/attributes/EditAttributesView.js b/modern/src/attributes/EditAttributesView.js index 3a18a9a3..56fdff5a 100644 --- a/modern/src/attributes/EditAttributesView.js +++ b/modern/src/attributes/EditAttributesView.js @@ -7,6 +7,8 @@ import CloseIcon from '@material-ui/icons/Close'; import AddIcon from '@material-ui/icons/Add'; import AddAttributeDialog from './AddAttributeDialog'; import { useTranslation } from '../LocalizationProvider'; +import { useAttributePreference } from '../common/preferences'; +import { distanceFromMeters, distanceToMeters, distanceUnitString, speedFromKnots, speedToKnots, speedUnitString, volumeFromLiters, volumeToLiters, volumeUnitString } from '../common/converter'; const useStyles = makeStyles((theme) => ({ addButton: { @@ -22,11 +24,28 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { const classes = useStyles(); const t = useTranslation(); + const speedUnit = useAttributePreference('speedUnit'); + const distanceUnit = useAttributePreference('distanceUnit'); + const volumeUnit = useAttributePreference('volumeUnit'); + const [addDialogShown, setAddDialogShown] = useState(false); - const updateAttribute = (key, value) => { + const updateAttribute = (key, value, type, subtype) => { const updatedAttributes = { ...attributes }; - updatedAttributes[key] = value; + switch (subtype) { + case 'speed': + updatedAttributes[key] = speedToKnots(Number(value), speedUnit); + break; + case 'distance': + updatedAttributes[key] = distanceToMeters(Number(value), distanceUnit); + break; + case 'volume': + updatedAttributes[key] = volumeToLiters(Number(value), volumeUnit); + break; + default: + updatedAttributes[key] = type === 'number' ? Number(value) : value; + break; + } setAttributes(updatedAttributes); }; @@ -36,9 +55,19 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { setAttributes(updatedAttributes); }; - const getAttributeName = (key) => { + const getAttributeName = (key, subtype) => { const definition = definitions[key]; - return definition ? definition.name : key; + const name = definition ? definition.name : key; + switch (subtype) { + case 'speed': + return `${name} (${speedUnitString(speedUnit, t)})`; + case 'distance': + return `${name} (${distanceUnitString(distanceUnit, t)})`; + case 'volume': + return `${name} (${volumeUnitString(volumeUnit, t)})`; + default: + return name; + } }; const getAttributeType = (value) => { @@ -50,19 +79,41 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { return 'string'; }; + const getAttributeSubtype = (key) => { + const definition = definitions[key]; + return definition && definition.subtype; + }; + + const getDisplayValue = (value, subtype) => { + if (value) { + switch (subtype) { + case 'speed': + return speedFromKnots(value, speedUnit); + case 'distance': + return distanceFromMeters(value, distanceUnit); + case 'volume': + return volumeFromLiters(value, volumeUnit); + default: + return value; + } + } + return ''; + } + const convertToList = (attributes) => { const booleanList = []; const otherList = []; Object.keys(attributes || []).forEach((key) => { const value = attributes[key]; const type = getAttributeType(value); + const subtype = getAttributeSubtype(key); if (type === 'boolean') { - booleanList.push({ key, value, type }); + booleanList.push({ key, value, type, subtype }); } else { - otherList.push({ key, value, type }); + otherList.push({ key, value, type, subtype }); } }); - return otherList.concat(booleanList); + return [...otherList, ...booleanList]; }; const handleAddResult = (definition) => { @@ -84,10 +135,10 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { return ( <> - {convertToList(attributes).map(({ key, value, type }) => { + {convertToList(attributes).map(({ key, value, type, subtype }) => { if (type === 'boolean') { return ( - <Grid container direction="row" justify="space-between"> + <Grid container direction="row" justify="space-between" key={key}> <FormControlLabel control={( <Checkbox @@ -95,7 +146,7 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { onChange={(e) => updateAttribute(key, e.target.checked)} /> )} - label={getAttributeName(key)} + label={getAttributeName(key, subtype)} /> <IconButton className={classes.removeButton} onClick={() => deleteAttribute(key)}> <CloseIcon /> @@ -105,11 +156,11 @@ const EditAttributesView = ({ attributes, setAttributes, definitions }) => { } return ( <FormControl variant="filled" margin="normal" key={key}> - <InputLabel>{getAttributeName(key)}</InputLabel> + <InputLabel>{getAttributeName(key, subtype)}</InputLabel> <FilledInput type={type === 'number' ? 'number' : 'text'} - value={value || ''} - onChange={(e) => updateAttribute(key, e.target.value)} + value={getDisplayValue(value, subtype)} + onChange={(e) => updateAttribute(key, e.target.value, type, subtype)} endAdornment={( <InputAdornment position="end"> <IconButton onClick={() => deleteAttribute(key)}> diff --git a/modern/src/attributes/useDeviceAttributes.js b/modern/src/attributes/useDeviceAttributes.js index f33fc065..8a4d886c 100644 --- a/modern/src/attributes/useDeviceAttributes.js +++ b/modern/src/attributes/useDeviceAttributes.js @@ -3,7 +3,8 @@ import { useMemo } from 'react'; export default (t) => useMemo(() => ({ speedLimit: { name: t('attributeSpeedLimit'), - type: 'string', + type: 'number', + subtype: 'speed', }, 'report.ignoreOdometer': { name: t('attributeReportIgnoreOdometer'), diff --git a/modern/src/common/converter.js b/modern/src/common/converter.js index d45db5d6..e70473ca 100644 --- a/modern/src/common/converter.js +++ b/modern/src/common/converter.js @@ -10,6 +10,22 @@ const speedConverter = (unit) => { } }; +export const speedUnitString = (unit, t) => { + switch (unit) { + case 'kmh': + return t('sharedKmh'); + case 'mph': + return t('sharedMph'); + case 'kn': + default: + return t('sharedKn'); + } +} + +export const speedFromKnots = (value, unit) => value * speedConverter(unit); + +export const speedToKnots = (value, unit) => value / speedConverter(unit); + const distanceConverter = (unit) => { switch (unit) { case 'mi': @@ -22,10 +38,46 @@ const distanceConverter = (unit) => { } }; -export const speedFromKnots = (value, unit) => value * speedConverter(unit); - -export const speedToKnots = (value, unit) => value / speedConverter(unit); +export const distanceUnitString = (unit, t) => { + switch (unit) { + case 'mi': + return t('sharedMi'); + case 'nmi': + return t('sharedNmi'); + case 'km': + default: + return t('sharedKm'); + } +}; export const distanceFromMeters = (value, unit) => value * distanceConverter(unit); export const distanceToMeters = (value, unit) => value / distanceConverter(unit); + +const volumeConverter = (unit) => { + switch (unit) { + case 'impGal': + return 4.546; + case 'usGal': + return 3.785; + case 'ltr': + default: + return 1; + } +}; + +export const volumeUnitString = (value, unit, t) => { + switch (unit) { + case 'impGal': + return t('sharedGallonAbbreviation'); + case 'usGal': + return t('sharedGallonAbbreviation'); + case 'ltr': + default: + return t('sharedLiterAbbreviation'); + } +}; + +export const volumeFromLiters = (value, unit) => value / volumeConverter(unit); + +export const volumeToLiters = (value, unit) => value * volumeConverter(unit); diff --git a/modern/src/common/formatter.js b/modern/src/common/formatter.js index bc3af6fb..06e3b12f 100644 --- a/modern/src/common/formatter.js +++ b/modern/src/common/formatter.js @@ -1,4 +1,5 @@ import moment from 'moment'; +import { distanceFromMeters, distanceUnitString, speedFromKnots, speedUnitString, volumeFromLiters, volumeUnitString } from './converter'; import { prefixString } from './stringUtils'; export const formatBoolean = (value, t) => (value ? t('sharedYes') : t('sharedNo')); @@ -18,41 +19,11 @@ export const formatCourse = (value) => { return courseValues[Math.floor(value / 45)]; }; -export const formatDistance = (value, unit, t) => { - switch (unit) { - case 'mi': - return `${(value * 0.000621371).toFixed(2)} ${t('sharedMi')}`; - case 'nmi': - return `${(value * 0.000539957).toFixed(2)} ${t('sharedNmi')}`; - case 'km': - default: - return `${(value * 0.001).toFixed(2)} ${t('sharedKm')}`; - } -}; +export const formatDistance = (value, unit, t) => `${distanceFromMeters(value, unit).toFixed(2)} ${distanceUnitString(unit, t)}`; -export const formatSpeed = (value, unit, t) => { - switch (unit) { - case 'kmh': - return `${(value * 1.852).toFixed(2)} ${t('sharedKmh')}`; - case 'mph': - return `${(value * 1.15078).toFixed(2)} ${t('sharedMph')}`; - case 'kn': - default: - return `${(value * 1).toFixed(2)} ${t('sharedKn')}`; - } -}; +export const formatSpeed = (value, unit, t) => `${speedFromKnots(value, unit).toFixed(2)} ${speedUnitString(unit, t)}`; -export const formatVolume = (value, unit, t) => { - switch (unit) { - case 'impGal': - return `${(value / 4.546).toFixed(2)} ${t('sharedGallonAbbreviation')}`; - case 'usGal': - return `${(value / 3.785).toFixed(2)} ${t('sharedGallonAbbreviation')}`; - case 'ltr': - default: - return `${(value / 1).toFixed(2)} ${t('sharedLiterAbbreviation')}`; - } -}; +export const formatVolume = (value, unit, t) => `${volumeFromLiters(value, unit).toFixed(2)} ${volumeUnitString(unit, t)}`; export const formatHours = (value) => moment.duration(value).humanize(); |