From 4e733fb2d637300196665b377850e7fdbd5ce8eb Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 26 Mar 2023 06:54:28 -0700 Subject: Implement group commands --- modern/src/Navigation.js | 6 +- modern/src/common/components/StatusCard.js | 2 +- modern/src/settings/CommandDevicePage.js | 132 +++++++++++++++++++++++++ modern/src/settings/CommandGroupPage.js | 121 +++++++++++++++++++++++ modern/src/settings/CommandSendPage.js | 132 ------------------------- modern/src/settings/GroupsPage.js | 13 ++- modern/src/settings/components/SettingsMenu.js | 2 +- 7 files changed, 271 insertions(+), 137 deletions(-) create mode 100644 modern/src/settings/CommandDevicePage.js create mode 100644 modern/src/settings/CommandGroupPage.js delete mode 100644 modern/src/settings/CommandSendPage.js (limited to 'modern/src') diff --git a/modern/src/Navigation.js b/modern/src/Navigation.js index 9a45b4dc..d9ca1817 100644 --- a/modern/src/Navigation.js +++ b/modern/src/Navigation.js @@ -45,7 +45,8 @@ import { devicesActions } from './store'; import EventPage from './other/EventPage'; import PreferencesPage from './settings/PreferencesPage'; import AccumulatorsPage from './settings/AccumulatorsPage'; -import CommandSendPage from './settings/CommandSendPage'; +import CommandDevicePage from './settings/CommandDevicePage'; +import CommandGroupPage from './settings/CommandGroupPage'; import App from './App'; import ChangeServerPage from './other/ChangeServerPage'; import DevicesPage from './settings/DevicesPage'; @@ -114,12 +115,12 @@ const Navigation = () => { } /> } /> } /> - } /> } /> } /> } /> } /> } /> + } /> } /> } /> } /> @@ -129,6 +130,7 @@ const Navigation = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/modern/src/common/components/StatusCard.js b/modern/src/common/components/StatusCard.js index 67d630e6..097b483a 100644 --- a/modern/src/common/components/StatusCard.js +++ b/modern/src/common/components/StatusCard.js @@ -238,7 +238,7 @@ const StatusCard = ({ deviceId, position, onClose, disableActions, desktopPaddin navigate(`/settings/command-send/${deviceId}`)} + onClick={() => navigate(`/settings/device/${deviceId}/command`)} disabled={disableActions} > diff --git a/modern/src/settings/CommandDevicePage.js b/modern/src/settings/CommandDevicePage.js new file mode 100644 index 00000000..ed802bfa --- /dev/null +++ b/modern/src/settings/CommandDevicePage.js @@ -0,0 +1,132 @@ +import React, { useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Typography, + Container, + Button, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import BaseCommandView from './components/BaseCommandView'; +import SelectField from '../common/components/SelectField'; +import PageLayout from '../common/components/PageLayout'; +import SettingsMenu from './components/SettingsMenu'; +import { useCatch } from '../reactHelper'; +import { useRestriction } from '../common/util/permissions'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(2), + }, + buttons: { + marginTop: theme.spacing(2), + marginBottom: theme.spacing(2), + display: 'flex', + justifyContent: 'space-evenly', + '& > *': { + flexBasis: '33%', + }, + }, + details: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + paddingBottom: theme.spacing(3), + }, +})); + +const CommandDevicePage = () => { + const navigate = useNavigate(); + const classes = useStyles(); + const t = useTranslation(); + + const { id } = useParams(); + + const [savedId, setSavedId] = useState(0); + const [item, setItem] = useState({}); + + const limitCommands = useRestriction('limitCommands'); + + const handleSend = useCatch(async () => { + let command; + if (savedId) { + const response = await fetch(`/api/commands/${savedId}`); + if (response.ok) { + command = await response.json(); + } else { + throw Error(await response.text()); + } + } else { + command = item; + } + + command.deviceId = parseInt(id, 10); + + const response = await fetch('/api/commands/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(command), + }); + + if (response.ok) { + navigate(-1); + } else { + throw Error(await response.text()); + } + }); + + const validate = () => savedId || (item && item.type); + + return ( + } breadcrumbs={['settingsTitle', 'deviceCommand']}> + + + }> + + {t('sharedRequired')} + + + + setSavedId(e.target.value)} + endpoint={`/api/commands/send?deviceId=${id}`} + titleGetter={(it) => it.description} + label={t('sharedSavedCommand')} + /> + {!limitCommands && !savedId && ( + + )} + + +
+ + +
+
+
+ ); +}; + +export default CommandDevicePage; diff --git a/modern/src/settings/CommandGroupPage.js b/modern/src/settings/CommandGroupPage.js new file mode 100644 index 00000000..b4852c26 --- /dev/null +++ b/modern/src/settings/CommandGroupPage.js @@ -0,0 +1,121 @@ +import React, { useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Typography, + Container, + Button, + FormControl, + InputLabel, + Select, + MenuItem, + FormControlLabel, + Checkbox, + TextField, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import PageLayout from '../common/components/PageLayout'; +import SettingsMenu from './components/SettingsMenu'; +import { useCatch } from '../reactHelper'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(2), + }, + buttons: { + marginTop: theme.spacing(2), + marginBottom: theme.spacing(2), + display: 'flex', + justifyContent: 'space-evenly', + '& > *': { + flexBasis: '33%', + }, + }, + details: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + paddingBottom: theme.spacing(3), + }, +})); + +const CommandDevicePage = () => { + const navigate = useNavigate(); + const classes = useStyles(); + const t = useTranslation(); + + const { id } = useParams(); + + const [item, setItem] = useState({ type: 'custom', attributes: {} }); + + const handleSend = useCatch(async () => { + const query = new URLSearchParams({ groupId: id }); + const response = await fetch(`/api/commands/send?${query.toString()}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(item), + }); + + if (response.ok) { + navigate(-1); + } else { + throw Error(await response.text()); + } + }); + + return ( + } breadcrumbs={['settingsTitle', 'deviceCommand']}> + + + }> + + {t('sharedRequired')} + + + + + {t('sharedType')} + + + setItem({ ...item, attributes: { ...item.attributes, data: e.target.value } })} + label={t('commandData')} + /> + setItem({ ...item, textChannel: event.target.checked })} />} + label={t('commandSendSms')} + /> + + +
+ + +
+
+
+ ); +}; + +export default CommandDevicePage; diff --git a/modern/src/settings/CommandSendPage.js b/modern/src/settings/CommandSendPage.js deleted file mode 100644 index 4afc6992..00000000 --- a/modern/src/settings/CommandSendPage.js +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Typography, - Container, - Button, -} from '@mui/material'; -import makeStyles from '@mui/styles/makeStyles'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import { useTranslation } from '../common/components/LocalizationProvider'; -import BaseCommandView from './components/BaseCommandView'; -import SelectField from '../common/components/SelectField'; -import PageLayout from '../common/components/PageLayout'; -import SettingsMenu from './components/SettingsMenu'; -import { useCatch } from '../reactHelper'; -import { useRestriction } from '../common/util/permissions'; - -const useStyles = makeStyles((theme) => ({ - container: { - marginTop: theme.spacing(2), - }, - buttons: { - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2), - display: 'flex', - justifyContent: 'space-evenly', - '& > *': { - flexBasis: '33%', - }, - }, - details: { - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), - paddingBottom: theme.spacing(3), - }, -})); - -const CommandSendPage = () => { - const navigate = useNavigate(); - const classes = useStyles(); - const t = useTranslation(); - - const { deviceId } = useParams(); - - const [savedId, setSavedId] = useState(0); - const [item, setItem] = useState({}); - - const limitCommands = useRestriction('limitCommands'); - - const handleSend = useCatch(async () => { - let command; - if (savedId) { - const response = await fetch(`/api/commands/${savedId}`); - if (response.ok) { - command = await response.json(); - } else { - throw Error(await response.text()); - } - } else { - command = item; - } - - command.deviceId = parseInt(deviceId, 10); - - const response = await fetch('/api/commands/send', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(command), - }); - - if (response.ok) { - navigate(-1); - } else { - throw Error(await response.text()); - } - }); - - const validate = () => savedId || (item && item.type); - - return ( - } breadcrumbs={['settingsTitle', 'deviceCommand']}> - - - }> - - {t('sharedRequired')} - - - - setSavedId(e.target.value)} - endpoint={`/api/commands/send?deviceId=${deviceId}`} - titleGetter={(it) => it.description} - label={t('sharedSavedCommand')} - /> - {!limitCommands && !savedId && ( - - )} - - -
- - -
-
-
- ); -}; - -export default CommandSendPage; diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js index 0624f7d1..9a909d07 100644 --- a/modern/src/settings/GroupsPage.js +++ b/modern/src/settings/GroupsPage.js @@ -4,6 +4,7 @@ import { Table, TableRow, TableCell, TableHead, TableBody, } from '@mui/material'; import LinkIcon from '@mui/icons-material/Link'; +import PublishIcon from '@mui/icons-material/Publish'; import makeStyles from '@mui/styles/makeStyles'; import { useEffectAsync } from '../reactHelper'; import { useTranslation } from '../common/components/LocalizationProvider'; @@ -13,6 +14,7 @@ import CollectionFab from './components/CollectionFab'; import CollectionActions from './components/CollectionActions'; import TableShimmer from '../common/components/TableShimmer'; import SearchHeader, { filterByKeyword } from './components/SearchHeader'; +import { useRestriction } from '../common/util/permissions'; const useStyles = makeStyles((theme) => ({ columnAction: { @@ -26,6 +28,8 @@ const GroupsPage = () => { const navigate = useNavigate(); const t = useTranslation(); + const limitCommands = useRestriction('limitCommands'); + const [timestamp, setTimestamp] = useState(Date.now()); const [items, setItems] = useState([]); const [searchKeyword, setSearchKeyword] = useState(''); @@ -45,6 +49,13 @@ const GroupsPage = () => { } }, [timestamp]); + const actionCommand = { + key: 'command', + title: t('loginLogin'), + icon: , + handler: (groupId) => navigate(`/settings/group/${groupId}/command`), + }; + const actionConnections = { key: 'connections', title: t('sharedConnections'), @@ -72,7 +83,7 @@ const GroupsPage = () => { editPath="/settings/group" endpoint="groups" setTimestamp={setTimestamp} - customActions={[actionConnections]} + customActions={limitCommands ? [actionConnections] : [actionConnections, actionCommand]} /> diff --git a/modern/src/settings/components/SettingsMenu.js b/modern/src/settings/components/SettingsMenu.js index eecdad79..96580974 100644 --- a/modern/src/settings/components/SettingsMenu.js +++ b/modern/src/settings/components/SettingsMenu.js @@ -120,7 +120,7 @@ const SettingsMenu = () => { title={t('sharedSavedCommands')} link="/settings/commands" icon={} - selected={location.pathname.startsWith('/settings/command') && !location.pathname.startsWith('/settings/command-send')} + selected={location.pathname.startsWith('/settings/command')} /> )} -- cgit v1.2.3