diff options
-rw-r--r-- | src/main/DeviceList.jsx | 17 | ||||
-rw-r--r-- | src/main/DeviceRow.jsx | 81 |
2 files changed, 88 insertions, 10 deletions
diff --git a/src/main/DeviceList.jsx b/src/main/DeviceList.jsx index eb51232f..3bb5f5e1 100644 --- a/src/main/DeviceList.jsx +++ b/src/main/DeviceList.jsx @@ -1,11 +1,12 @@ import React, { useEffect, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import makeStyles from '@mui/styles/makeStyles'; -import { FixedSizeList } from 'react-window'; +import { VariableSizeList } from 'react-window'; import AutoSizer from 'react-virtualized-auto-sizer'; import { devicesActions } from '../store'; import { useEffectAsync } from '../reactHelper'; import DeviceRow from './DeviceRow'; +import { useAttributePreference } from '../common/util/preferences'; const useStyles = makeStyles((theme) => ({ list: { @@ -28,6 +29,8 @@ const DeviceList = ({ devices }) => { const [, setTime] = useState(Date.now()); + const positionItems = useAttributePreference('positionItems', 'speed,address,totalDistance,course'); + useEffect(() => { const interval = setInterval(() => setTime(Date.now()), 60000); return () => { @@ -44,20 +47,26 @@ const DeviceList = ({ devices }) => { } }, []); + // RATIONALE: calculate row height to fit position attributes + const getItemSize = (index) => { + const item = devices[index]; + return 72 + (item.positionId && (positionItems.split(',').length * 20) || 0); + }; + return ( <AutoSizer className={classes.list}> {({ height, width }) => ( - <FixedSizeList + <VariableSizeList width={width} height={height} itemCount={devices.length} itemData={devices} - itemSize={72} + itemSize={getItemSize} overscanCount={10} innerRef={listInnerEl} > {DeviceRow} - </FixedSizeList> + </VariableSizeList> )} </AutoSizer> ); diff --git a/src/main/DeviceRow.jsx b/src/main/DeviceRow.jsx index d9c1a189..8399da22 100644 --- a/src/main/DeviceRow.jsx +++ b/src/main/DeviceRow.jsx @@ -2,14 +2,20 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import makeStyles from '@mui/styles/makeStyles'; import { - IconButton, Tooltip, Avatar, ListItemAvatar, ListItemText, ListItemButton, + IconButton, Tooltip, Avatar, ListItemAvatar, ListItemText, ListItemButton, Stack, } from '@mui/material'; +import LockIcon from '@mui/icons-material/Lock'; +import LockOpenIcon from '@mui/icons-material/LockOpen'; import BatteryFullIcon from '@mui/icons-material/BatteryFull'; import BatteryChargingFullIcon from '@mui/icons-material/BatteryChargingFull'; import Battery60Icon from '@mui/icons-material/Battery60'; import BatteryCharging60Icon from '@mui/icons-material/BatteryCharging60'; import Battery20Icon from '@mui/icons-material/Battery20'; import BatteryCharging20Icon from '@mui/icons-material/BatteryCharging20'; +import SpeedIcon from '@mui/icons-material/Speed'; +import ScheduleIcon from '@mui/icons-material/Schedule'; +import PlaceIcon from '@mui/icons-material/Place'; +import AvTimerIcon from '@mui/icons-material/AvTimer'; import ErrorIcon from '@mui/icons-material/Error'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; @@ -22,6 +28,8 @@ import { mapIconKey, mapIcons } from '../map/core/preloadImages'; import { useAdministrator } from '../common/util/permissions'; import EngineIcon from '../resources/images/data/engine.svg?react'; import { useAttributePreference } from '../common/util/preferences'; +import usePositionAttributes from '../common/attributes/usePositionAttributes'; +import PositionValue from '../common/components/PositionValue'; dayjs.extend(relativeTime); @@ -63,6 +71,9 @@ const DeviceRow = ({ data, index, style }) => { const devicePrimary = useAttributePreference('devicePrimary', 'name'); const deviceSecondary = useAttributePreference('deviceSecondary', ''); + const positionAttributes = usePositionAttributes(t); + const positionItems = useAttributePreference('positionItems', 'speed,address,totalDistance,course'); + const secondaryText = () => { let status; if (item.status === 'online' || !item.lastUpdate) { @@ -74,10 +85,45 @@ const DeviceRow = ({ data, index, style }) => { <> {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `} <span className={classes[getStatusColor(item.status)]}>{status}</span> + {/* RATIONALE: clients want more info in the list */} + {position && positionItems.split(',').filter((key) => position.hasOwnProperty(key) || position.attributes.hasOwnProperty(key)).map((key) => ( + <AttrItem + key={key} + attr={key} + name={positionAttributes[key]?.name || key} + content={( + <PositionValue + position={position} + property={position.hasOwnProperty(key) ? key : null} + attribute={position.hasOwnProperty(key) ? null : key} + /> + )} + /> + ))} </> ); }; + // RATIONALE: we connect output/out1 to engine lock + const positionLock = () => { + let lock = false; + if (position.attributes.output !== undefined) { + lock = position.attributes.output === 1; + } else if (position.attributes.out1 !== undefined) { + lock = position.attributes.out1; + } else { + return <></>; + } + + return ( + <IconButton size="small">{ + (lock) + ? <LockIcon fontSize="small" className={classes.error} /> + : <LockOpenIcon fontSize="small" height={20} className={classes.success} /> + }</IconButton> + ) + }; + return ( <div style={style}> <ListItemButton @@ -91,11 +137,11 @@ const DeviceRow = ({ data, index, style }) => { </Avatar> </ListItemAvatar> <ListItemText - primary={item[devicePrimary]} - primaryTypographyProps={{ noWrap: true }} - secondary={secondaryText()} - secondaryTypographyProps={{ noWrap: true }} - /> + primary={item[devicePrimary]} + primaryTypographyProps={{ noWrap: true }} + secondary={secondaryText()} + secondaryTypographyProps={{ noWrap: true }} + /> {position && ( <> {position.attributes.hasOwnProperty('alarm') && ( @@ -105,6 +151,8 @@ const DeviceRow = ({ data, index, style }) => { </IconButton> </Tooltip> )} + {/* RATIONALE: clients want engine lock at a glance */} + {positionLock()} {position.attributes.hasOwnProperty('ignition') && ( <Tooltip title={`${t('positionIgnition')}: ${formatBoolean(position.attributes.ignition, t)}`}> <IconButton size="small"> @@ -142,4 +190,25 @@ const DeviceRow = ({ data, index, style }) => { ); }; +const AttrItem = ({ attr, name, content }) => { + const attrIcon = () => { + switch(attr) { + case "address": return <PlaceIcon fontSize="inherit" />; + case "deviceTime": return <ScheduleIcon fontSize="inherit" />; + case "fixTime": return <ScheduleIcon fontSize="inherit" />; + case "hours": return <AvTimerIcon fontSize="inherit" />; + case "serverTime": return <ScheduleIcon fontSize="inherit" />; + case "speed": return <SpeedIcon fontSize="inherit" />; + default: return <b>{name}</b>; + } + }; + + return ( + <> + <br></br> + {attrIcon()} {content} + </> + ); +}; + export default DeviceRow; |