aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2024-03-08 06:53:22 -0800
committerGitHub <noreply@github.com>2024-03-08 06:53:22 -0800
commit5b1c7664dd8e750125ba697694dd407223c7c0d1 (patch)
tree4af16db91131f2c1e1b715b054bb58df12960943
parentc9da10062998a231c038cd3a519f72128fcea2bb (diff)
parent3229441f4b254eb13266486ff5b29e3dd952357b (diff)
downloadtrackermap-web-5b1c7664dd8e750125ba697694dd407223c7c0d1.tar.gz
trackermap-web-5b1c7664dd8e750125ba697694dd407223c7c0d1.tar.bz2
trackermap-web-5b1c7664dd8e750125ba697694dd407223c7c0d1.zip
Merge pull request #1212 from jinzo/autocomplete-instead-of-single-select
Replace Select with Autocomplete with single Selects
-rw-r--r--modern/src/common/components/SelectField.jsx57
-rw-r--r--modern/src/reports/components/ReportFilter.jsx44
-rw-r--r--modern/src/settings/ComputedAttributePage.jsx2
-rw-r--r--modern/src/settings/DevicePage.jsx5
-rw-r--r--modern/src/settings/GeofencePage.jsx2
-rw-r--r--modern/src/settings/GroupPage.jsx2
-rw-r--r--modern/src/settings/NotificationPage.jsx5
-rw-r--r--modern/src/settings/PreferencesPage.jsx4
-rw-r--r--modern/src/settings/ServerPage.jsx3
-rw-r--r--modern/src/settings/UserPage.jsx3
-rw-r--r--modern/src/settings/components/BaseCommandView.jsx2
11 files changed, 67 insertions, 62 deletions
diff --git a/modern/src/common/components/SelectField.jsx b/modern/src/common/components/SelectField.jsx
index 35f817a0..db8c30b0 100644
--- a/modern/src/common/components/SelectField.jsx
+++ b/modern/src/common/components/SelectField.jsx
@@ -1,5 +1,5 @@
import {
- FormControl, InputLabel, MenuItem, Select,
+ FormControl, InputLabel, MenuItem, Select, Autocomplete, TextField,
} from '@mui/material';
import React, { useState } from 'react';
import { useEffectAsync } from '../../reactHelper';
@@ -8,9 +8,9 @@ const SelectField = ({
label,
fullWidth,
multiple,
- value,
- emptyValue = 0,
- emptyTitle = '\u00a0',
+ value = null,
+ emptyValue = null,
+ emptyTitle = '',
onChange,
endpoint,
data,
@@ -19,6 +19,13 @@ const SelectField = ({
}) => {
const [items, setItems] = useState(data);
+ const getOptionLabel = (option) => {
+ if (typeof option !== 'object') {
+ option = items.find((obj) => keyGetter(obj) === option);
+ }
+ return option ? titleGetter(option) : emptyTitle;
+ };
+
useEffectAsync(async () => {
if (endpoint) {
const response = await fetch(endpoint);
@@ -33,20 +40,34 @@ const SelectField = ({
if (items) {
return (
<FormControl fullWidth={fullWidth}>
- <InputLabel>{label}</InputLabel>
- <Select
- label={label}
- multiple={multiple}
- value={value}
- onChange={onChange}
- >
- {!multiple && emptyValue !== null && (
- <MenuItem value={emptyValue}>{emptyTitle}</MenuItem>
- )}
- {items.map((item) => (
- <MenuItem key={keyGetter(item)} value={keyGetter(item)}>{titleGetter(item)}</MenuItem>
- ))}
- </Select>
+ {multiple ? (
+ <>
+ <InputLabel>{label}</InputLabel>
+ <Select
+ label={label}
+ multiple
+ value={value}
+ onChange={onChange}
+ >
+ {items.map((item) => (
+ <MenuItem key={keyGetter(item)} value={keyGetter(item)}>{titleGetter(item)}</MenuItem>
+ ))}
+ </Select>
+ </>
+ ) : (
+ <Autocomplete
+ size="small"
+ options={items}
+ getOptionLabel={getOptionLabel}
+ renderOption={(props, option) => (
+ <MenuItem {...props} key={keyGetter(option)} value={keyGetter(option)}>{titleGetter(option)}</MenuItem>
+ )}
+ isOptionEqualToValue={(option, value) => keyGetter(option) === value}
+ value={value}
+ onChange={(_, value) => onChange({ target: { value: value ? keyGetter(value) : emptyValue } })}
+ renderInput={(params) => <TextField {...params} label={label} />}
+ />
+ )}
</FormControl>
);
}
diff --git a/modern/src/reports/components/ReportFilter.jsx b/modern/src/reports/components/ReportFilter.jsx
index eab7d0eb..e6e08f16 100644
--- a/modern/src/reports/components/ReportFilter.jsx
+++ b/modern/src/reports/components/ReportFilter.jsx
@@ -92,36 +92,26 @@ const ReportFilter = ({ children, handleSubmit, handleSchedule, showOnly, ignore
<div className={classes.filter}>
{!ignoreDevice && (
<div className={classes.filterItem}>
- <FormControl fullWidth>
- <InputLabel>{t(multiDevice ? 'deviceTitle' : 'reportDevice')}</InputLabel>
- <Select
- label={t(multiDevice ? 'deviceTitle' : 'reportDevice')}
- value={multiDevice ? deviceIds : deviceId || ''}
- onChange={(e) => dispatch(multiDevice ? devicesActions.selectIds(e.target.value) : devicesActions.selectId(e.target.value))}
- multiple={multiDevice}
- >
- {Object.values(devices).sort((a, b) => a.name.localeCompare(b.name)).map((device) => (
- <MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
+ <SelectField
+ label={t(multiDevice ? 'deviceTitle' : 'reportDevice')}
+ data={Object.values(devices).sort((a, b) => a.name.localeCompare(b.name))}
+ value={multiDevice ? deviceIds : deviceId}
+ onChange={(e) => dispatch(multiDevice ? devicesActions.selectIds(e.target.value) : devicesActions.selectId(e.target.value))}
+ multiple={multiDevice}
+ fullWidth
+ />
</div>
)}
{includeGroups && (
<div className={classes.filterItem}>
- <FormControl fullWidth>
- <InputLabel>{t('settingsGroups')}</InputLabel>
- <Select
- label={t('settingsGroups')}
- value={groupIds}
- onChange={(e) => dispatch(reportsActions.updateGroupIds(e.target.value))}
- multiple
- >
- {Object.values(groups).sort((a, b) => a.name.localeCompare(b.name)).map((group) => (
- <MenuItem key={group.id} value={group.id}>{group.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
+ <SelectField
+ label={t('settingsGroups')}
+ data={Object.values(groups).sort((a, b) => a.name.localeCompare(b.name))}
+ value={groupIds}
+ onChange={(e) => dispatch(reportsActions.updateGroupIds(e.target.value))}
+ multiple
+ fullWidth
+ />
</div>
)}
{button !== 'schedule' ? (
@@ -175,7 +165,7 @@ const ReportFilter = ({ children, handleSubmit, handleSchedule, showOnly, ignore
</div>
<div className={classes.filterItem}>
<SelectField
- value={calendarId || 0}
+ value={calendarId}
onChange={(event) => setCalendarId(Number(event.target.value))}
endpoint="/api/calendars"
label={t('sharedCalendar')}
diff --git a/modern/src/settings/ComputedAttributePage.jsx b/modern/src/settings/ComputedAttributePage.jsx
index f179a78d..1b19033c 100644
--- a/modern/src/settings/ComputedAttributePage.jsx
+++ b/modern/src/settings/ComputedAttributePage.jsx
@@ -147,7 +147,7 @@ const ComputedAttributePage = () => {
</AccordionSummary>
<AccordionDetails className={classes.details}>
<SelectField
- value={deviceId || 0}
+ value={deviceId}
onChange={(e) => setDeviceId(Number(e.target.value))}
endpoint="/api/devices"
label={t('sharedDevice')}
diff --git a/modern/src/settings/DevicePage.jsx b/modern/src/settings/DevicePage.jsx
index ae8c9bf3..0feb000b 100644
--- a/modern/src/settings/DevicePage.jsx
+++ b/modern/src/settings/DevicePage.jsx
@@ -94,7 +94,7 @@ const DevicePage = () => {
</AccordionSummary>
<AccordionDetails className={classes.details}>
<SelectField
- value={item.groupId || 0}
+ value={item.groupId}
onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
endpoint="/api/groups"
label={t('groupParent')}
@@ -116,7 +116,6 @@ const DevicePage = () => {
/>
<SelectField
value={item.category || 'default'}
- emptyValue={null}
onChange={(event) => setItem({ ...item, category: event.target.value })}
data={deviceCategories.map((category) => ({
id: category,
@@ -125,7 +124,7 @@ const DevicePage = () => {
label={t('deviceCategory')}
/>
<SelectField
- value={item.calendarId || 0}
+ value={item.calendarId}
onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
endpoint="/api/calendars"
label={t('sharedCalendar')}
diff --git a/modern/src/settings/GeofencePage.jsx b/modern/src/settings/GeofencePage.jsx
index 93b23c7e..c3c96ef8 100644
--- a/modern/src/settings/GeofencePage.jsx
+++ b/modern/src/settings/GeofencePage.jsx
@@ -67,7 +67,7 @@ const GeofencePage = () => {
label={t('sharedDescription')}
/>
<SelectField
- value={item.calendarId || 0}
+ value={item.calendarId}
onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
endpoint="/api/calendars"
label={t('sharedCalendar')}
diff --git a/modern/src/settings/GroupPage.jsx b/modern/src/settings/GroupPage.jsx
index d21d0e5a..ba1cbc76 100644
--- a/modern/src/settings/GroupPage.jsx
+++ b/modern/src/settings/GroupPage.jsx
@@ -72,7 +72,7 @@ const GroupPage = () => {
</AccordionSummary>
<AccordionDetails className={classes.details}>
<SelectField
- value={item.groupId || 0}
+ value={item.groupId}
onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
endpoint="/api/groups"
label={t('groupParent')}
diff --git a/modern/src/settings/NotificationPage.jsx b/modern/src/settings/NotificationPage.jsx
index 978d940c..63aa9b95 100644
--- a/modern/src/settings/NotificationPage.jsx
+++ b/modern/src/settings/NotificationPage.jsx
@@ -65,7 +65,6 @@ const NotificationPage = () => {
<AccordionDetails className={classes.details}>
<SelectField
value={item.type}
- emptyValue={null}
onChange={(e) => setItem({ ...item, type: e.target.value })}
endpoint="/api/notifications/types"
keyGetter={(it) => it.type}
@@ -93,7 +92,7 @@ const NotificationPage = () => {
/>
{item.notificators?.includes('command') && (
<SelectField
- value={item.commandId || 0}
+ value={item.commandId}
onChange={(event) => setItem({ ...item, commandId: Number(event.target.value) })}
endpoint="/api/commands"
titleGetter={(it) => it.description}
@@ -129,7 +128,7 @@ const NotificationPage = () => {
</AccordionSummary>
<AccordionDetails className={classes.details}>
<SelectField
- value={item.calendarId || 0}
+ value={item.calendarId}
onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
endpoint="/api/calendars"
label={t('sharedCalendar')}
diff --git a/modern/src/settings/PreferencesPage.jsx b/modern/src/settings/PreferencesPage.jsx
index 676bd58b..2d6df62f 100644
--- a/modern/src/settings/PreferencesPage.jsx
+++ b/modern/src/settings/PreferencesPage.jsx
@@ -239,7 +239,6 @@ const PreferencesPage = () => {
</AccordionSummary>
<AccordionDetails className={classes.details}>
<SelectField
- emptyValue={null}
value={attributes.devicePrimary || 'name'}
onChange={(e) => setAttributes({ ...attributes, devicePrimary: e.target.value })}
data={deviceFields}
@@ -247,8 +246,7 @@ const PreferencesPage = () => {
label={t('devicePrimaryInfo')}
/>
<SelectField
- emptyValue=""
- value={attributes.deviceSecondary || ''}
+ value={attributes.deviceSecondary}
onChange={(e) => setAttributes({ ...attributes, deviceSecondary: e.target.value })}
data={deviceFields}
titleGetter={(it) => t(it.name)}
diff --git a/modern/src/settings/ServerPage.jsx b/modern/src/settings/ServerPage.jsx
index 7a3a1bbe..0ac76334 100644
--- a/modern/src/settings/ServerPage.jsx
+++ b/modern/src/settings/ServerPage.jsx
@@ -172,8 +172,7 @@ const ServerPage = () => {
</Select>
</FormControl>
<SelectField
- value={item.attributes.timezone || ''}
- emptyValue=""
+ value={item.attributes.timezone}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, timezone: e.target.value } })}
endpoint="/api/server/timezones"
keyGetter={(it) => it}
diff --git a/modern/src/settings/UserPage.jsx b/modern/src/settings/UserPage.jsx
index 4e0cab2c..6748dd31 100644
--- a/modern/src/settings/UserPage.jsx
+++ b/modern/src/settings/UserPage.jsx
@@ -258,8 +258,7 @@ const UserPage = () => {
</Select>
</FormControl>
<SelectField
- value={(item.attributes && item.attributes.timezone) || ''}
- emptyValue=""
+ value={item.attributes && item.attributes.timezone}
onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, timezone: e.target.value } })}
endpoint="/api/server/timezones"
keyGetter={(it) => it}
diff --git a/modern/src/settings/components/BaseCommandView.jsx b/modern/src/settings/components/BaseCommandView.jsx
index acf39090..bb70c3b9 100644
--- a/modern/src/settings/components/BaseCommandView.jsx
+++ b/modern/src/settings/components/BaseCommandView.jsx
@@ -28,7 +28,7 @@ const BaseCommandView = ({ deviceId, item, setItem }) => {
return (
<>
<SelectField
- value={item.type || ''}
+ value={item.type}
onChange={(e) => setItem({ ...item, type: e.target.value, attributes: {} })}
endpoint={deviceId ? `/api/commands/types?${new URLSearchParams({ deviceId }).toString()}` : '/api/commands/types'}
keyGetter={(it) => it.type}