From 71c5dfe153705bc6ee1931920f7713e73284115f Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 4 Jun 2022 09:13:49 -0700 Subject: Filtering and sorting (fix #952) --- modern/src/common/theme/dimensions.js | 6 -- modern/src/common/util/usePersistedState.js | 6 +- modern/src/main/DevicesList.js | 20 +---- modern/src/main/MainPage.js | 121 ++++++++++++++++++++++++---- web/l10n/en.json | 2 + 5 files changed, 118 insertions(+), 37 deletions(-) diff --git a/modern/src/common/theme/dimensions.js b/modern/src/common/theme/dimensions.js index d68a94f5..c5974ec3 100644 --- a/modern/src/common/theme/dimensions.js +++ b/modern/src/common/theme/dimensions.js @@ -1,6 +1,4 @@ export default { - inputHeight: '42px', - borderRadius: '4px', sidebarWidth: '28%', sidebarWidthTablet: '52px', drawerWidthDesktop: '360px', @@ -8,10 +6,6 @@ export default { drawerHeightPhone: '250px', filterFormWidth: '160px', bottomBarHeight: 56, - columnWidthDate: 160, - columnWidthNumber: 130, - columnWidthString: 160, - columnWidthBoolean: 130, popupMapOffset: 300, popupMaxWidth: 272, }; diff --git a/modern/src/common/util/usePersistedState.js b/modern/src/common/util/usePersistedState.js index 8bc4401f..70a652ad 100644 --- a/modern/src/common/util/usePersistedState.js +++ b/modern/src/common/util/usePersistedState.js @@ -11,7 +11,11 @@ export default (key, defaultValue) => { }); useEffect(() => { - savePersistedState(key, value); + if (value !== defaultValue) { + savePersistedState(key, value); + } else { + window.localStorage.removeItem(key); + } }, [key, value]); return [value, setValue]; diff --git a/modern/src/main/DevicesList.js b/modern/src/main/DevicesList.js index 108c3397..baf18dd8 100644 --- a/modern/src/main/DevicesList.js +++ b/modern/src/main/DevicesList.js @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import makeStyles from '@mui/styles/makeStyles'; import { IconButton, Tooltip } from '@mui/material'; @@ -142,23 +142,11 @@ const DeviceRow = ({ data, index, style }) => { ); }; -const DevicesList = ({ filter }) => { +const DevicesList = ({ devices }) => { const classes = useStyles(); const dispatch = useDispatch(); const listInnerEl = useRef(null); - const items = useSelector((state) => state.devices.items); - const [filteredItems, setFilteredItems] = useState(null); - - useEffect(() => { - const array = Object.values(items); - setFilteredItems( - filter.trim().length > 0 - ? array.filter((item) => `${item.name} ${item.uniqueId}`.toLowerCase().includes(filter?.toLowerCase())) - : array, - ); - }, [filter, items]); - if (listInnerEl.current) { listInnerEl.current.className = classes.listInner; } @@ -179,8 +167,8 @@ const DevicesList = ({ filter }) => { ({ zIndex: 4, width: theme.dimensions.drawerWidthDesktop, }, + filterPanel: { + display: 'flex', + flexDirection: 'column', + padding: theme.spacing(2), + gap: theme.spacing(2), + width: theme.dimensions.drawerWidthTablet, + }, })); const MainPage = () => { @@ -136,10 +143,23 @@ const MainPage = () => { const [mapLiveRoutes] = usePersistedState('mapLiveRoutes', false); const selectedDeviceId = useSelector((state) => state.devices.selectedId); - const positions = useSelector((state) => Object.values(state.positions.items)); - const selectedPosition = positions.find((position) => selectedDeviceId && position.deviceId === selectedDeviceId); + const positions = useSelector((state) => state.positions.items); + const [filteredPositions, setFilteredPositions] = useState([]); + const selectedPosition = filteredPositions.find((position) => selectedDeviceId && position.deviceId === selectedDeviceId); + + const groups = useSelector((state) => state.groups.items); + const devices = useSelector((state) => state.devices.items); + const [filteredDevices, setFilteredDevices] = useState([]); + + const [filterKeyword, setFilterKeyword] = useState(''); + const [filterStatuses, setFilterStatuses] = useState([]); + const [filterGroups, setFilterGroups] = useState([]); + const [filterSort, setFilterSort] = usePersistedState('filterSort', ''); + const [filterMap, setFilterMap] = usePersistedState('filterMap', false); + + const filterRef = useRef(); + const [filterAnchorEl, setFilterAnchorEl] = useState(null); - const [searchKeyword, setSearchKeyword] = useState(''); const [collapsed, setCollapsed] = useState(false); const handleClose = () => { @@ -158,13 +178,31 @@ const MainPage = () => { dispatch(devicesActions.select(deviceId)); }, [dispatch]); + useEffect(() => { + const filtered = Object.values(devices) + .filter((device) => !filterStatuses.length || filterStatuses.includes(device.status)) + .filter((device) => !filterGroups.length || filterGroups.includes(device.groupId)) + .filter((device) => `${device.name} ${device.uniqueId}`.toLowerCase().includes(filterKeyword.toLowerCase())); + if (filterSort === 'lastUpdate') { + filtered.sort((device1, device2) => { + const time1 = device1.lastUpdate ? moment(device1.lastUpdate).valueOf() : 0; + const time2 = device2.lastUpdate ? moment(device2.lastUpdate).valueOf() : 0; + return time2 - time1; + }); + } + setFilteredDevices(filtered); + setFilteredPositions(filterMap + ? filtered.map((device) => positions[device.id]).filter(Boolean) + : Object.values(positions)); + }, [devices, positions, filterKeyword, filterStatuses, filterGroups, filterSort, filterMap]); + return (
{mapLiveRoutes && } - + {selectedPosition && selectedPosition.course && ( )} @@ -194,12 +232,13 @@ const MainPage = () => { )} setSearchKeyword(event.target.value)} + value={filterKeyword} + onChange={(event) => setFilterKeyword(event.target.value)} endAdornment={( - {}}> + setFilterAnchorEl(filterRef.current)}> @@ -207,6 +246,60 @@ const MainPage = () => { size="small" fullWidth /> + setFilterAnchorEl(null)} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + > +
+ + {t('deviceStatus')} + + + + {t('settingsGroups')} + + + + {t('sharedSortBy')} + + + + setFilterMap(e.target.checked)} />} + label={t('sharedFilterMap')} + /> + +
+
navigate('/settings/device')} disabled={deviceReadonly}> @@ -218,7 +311,7 @@ const MainPage = () => {
- +
{desktop && ( diff --git a/web/l10n/en.json b/web/l10n/en.json index 17e00108..8d851911 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -62,6 +62,8 @@ "sharedCalendars": "Calendars", "sharedFile": "File", "sharedSearchDevices": "Search Devices", + "sharedSortBy": "Sort By", + "sharedFilterMap": "Filter on Map", "sharedSelectFile": "Select File", "sharedPhone": "Phone", "sharedRequired": "Required", -- cgit v1.2.3