diff options
-rw-r--r-- | modern/src/Navigation.js | 6 | ||||
-rw-r--r-- | modern/src/common/components/StatusCard.js | 2 | ||||
-rw-r--r-- | modern/src/settings/CommandDevicePage.js (renamed from modern/src/settings/CommandSendPage.js) | 12 | ||||
-rw-r--r-- | modern/src/settings/CommandGroupPage.js | 121 | ||||
-rw-r--r-- | modern/src/settings/GroupsPage.js | 13 | ||||
-rw-r--r-- | modern/src/settings/components/SettingsMenu.js | 2 |
6 files changed, 145 insertions, 11 deletions
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 = () => { <Route path="commands" element={<CommandsPage />} /> <Route path="command/:id" element={<CommandPage />} /> <Route path="command" element={<CommandPage />} /> - <Route path="command-send/:deviceId" element={<CommandSendPage />} /> <Route path="attributes" element={<ComputedAttributesPage />} /> <Route path="attribute/:id" element={<ComputedAttributePage />} /> <Route path="attribute" element={<ComputedAttributePage />} /> <Route path="devices" element={<DevicesPage />} /> <Route path="device/:id/connections" element={<DeviceConnectionsPage />} /> + <Route path="device/:id/command" element={<CommandDevicePage />} /> <Route path="device/:id" element={<DevicePage />} /> <Route path="device" element={<DevicePage />} /> <Route path="drivers" element={<DriversPage />} /> @@ -129,6 +130,7 @@ const Navigation = () => { <Route path="geofence" element={<GeofencePage />} /> <Route path="groups" element={<GroupsPage />} /> <Route path="group/:id/connections" element={<GroupConnectionsPage />} /> + <Route path="group/:id/command" element={<CommandGroupPage />} /> <Route path="group/:id" element={<GroupPage />} /> <Route path="group" element={<GroupPage />} /> <Route path="maintenances" element={<MaintenancesPage />} /> 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 <ReplayIcon /> </IconButton> <IconButton - onClick={() => navigate(`/settings/command-send/${deviceId}`)} + onClick={() => navigate(`/settings/device/${deviceId}/command`)} disabled={disableActions} > <PublishIcon /> diff --git a/modern/src/settings/CommandSendPage.js b/modern/src/settings/CommandDevicePage.js index 4afc6992..ed802bfa 100644 --- a/modern/src/settings/CommandSendPage.js +++ b/modern/src/settings/CommandDevicePage.js @@ -39,12 +39,12 @@ const useStyles = makeStyles((theme) => ({ }, })); -const CommandSendPage = () => { +const CommandDevicePage = () => { const navigate = useNavigate(); const classes = useStyles(); const t = useTranslation(); - const { deviceId } = useParams(); + const { id } = useParams(); const [savedId, setSavedId] = useState(0); const [item, setItem] = useState({}); @@ -64,7 +64,7 @@ const CommandSendPage = () => { command = item; } - command.deviceId = parseInt(deviceId, 10); + command.deviceId = parseInt(id, 10); const response = await fetch('/api/commands/send', { method: 'POST', @@ -96,12 +96,12 @@ const CommandSendPage = () => { emptyValue={limitCommands ? null : 0} emptyTitle={t('sharedNew')} onChange={(e) => setSavedId(e.target.value)} - endpoint={`/api/commands/send?deviceId=${deviceId}`} + endpoint={`/api/commands/send?deviceId=${id}`} titleGetter={(it) => it.description} label={t('sharedSavedCommand')} /> {!limitCommands && !savedId && ( - <BaseCommandView deviceId={deviceId} item={item} setItem={setItem} /> + <BaseCommandView deviceId={id} item={item} setItem={setItem} /> )} </AccordionDetails> </Accordion> @@ -129,4 +129,4 @@ const CommandSendPage = () => { ); }; -export default CommandSendPage; +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 ( + <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'deviceCommand']}> + <Container maxWidth="xs" className={classes.container}> + <Accordion defaultExpanded> + <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <Typography variant="subtitle1"> + {t('sharedRequired')} + </Typography> + </AccordionSummary> + <AccordionDetails className={classes.details}> + <FormControl fullWidth> + <InputLabel>{t('sharedType')}</InputLabel> + <Select label={t('sharedType')} value="custom" disabled> + <MenuItem value="custom">{t('commandCustom')}</MenuItem> + </Select> + </FormControl> + <TextField + value={item.attributes.data} + onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, data: e.target.value } })} + label={t('commandData')} + /> + <FormControlLabel + control={<Checkbox checked={item.textChannel} onChange={(event) => setItem({ ...item, textChannel: event.target.checked })} />} + label={t('commandSendSms')} + /> + </AccordionDetails> + </Accordion> + <div className={classes.buttons}> + <Button + type="button" + color="primary" + variant="outlined" + onClick={() => navigate(-1)} + > + {t('sharedCancel')} + </Button> + <Button + type="button" + color="primary" + variant="contained" + onClick={handleSend} + disabled={!item.attributes.data} + > + {t('commandSend')} + </Button> + </div> + </Container> + </PageLayout> + ); +}; + +export default CommandDevicePage; 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: <PublishIcon fontSize="small" />, + 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]} /> </TableCell> </TableRow> 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={<PublishIcon />} - selected={location.pathname.startsWith('/settings/command') && !location.pathname.startsWith('/settings/command-send')} + selected={location.pathname.startsWith('/settings/command')} /> </> )} |