aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2023-03-26 06:54:28 -0700
committerAnton Tananaev <anton@traccar.org>2023-03-26 06:54:28 -0700
commit4e733fb2d637300196665b377850e7fdbd5ce8eb (patch)
treecc74e994c0e2f92a467f7dc89e5051fcd28ca2b9
parente06c03ae5b4bac3a1fea26f1cdf67e510a2f873b (diff)
downloadtrackermap-web-4e733fb2d637300196665b377850e7fdbd5ce8eb.tar.gz
trackermap-web-4e733fb2d637300196665b377850e7fdbd5ce8eb.tar.bz2
trackermap-web-4e733fb2d637300196665b377850e7fdbd5ce8eb.zip
Implement group commands
-rw-r--r--modern/src/Navigation.js6
-rw-r--r--modern/src/common/components/StatusCard.js2
-rw-r--r--modern/src/settings/CommandDevicePage.js (renamed from modern/src/settings/CommandSendPage.js)12
-rw-r--r--modern/src/settings/CommandGroupPage.js121
-rw-r--r--modern/src/settings/GroupsPage.js13
-rw-r--r--modern/src/settings/components/SettingsMenu.js2
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')}
/>
</>
)}