diff options
author | Anton Tananaev <anton@traccar.org> | 2023-02-23 11:48:06 -0800 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2023-02-23 11:48:06 -0800 |
commit | 5f65762c67aacc9a621983fcb06107b982792c3f (patch) | |
tree | b4c29451fbfb2fc0e090671efc76188725ac4d59 /modern | |
parent | d64115f88d84c85b149063cf8929d1f57c66935f (diff) | |
download | trackermap-web-5f65762c67aacc9a621983fcb06107b982792c3f.tar.gz trackermap-web-5f65762c67aacc9a621983fcb06107b982792c3f.tar.bz2 trackermap-web-5f65762c67aacc9a621983fcb06107b982792c3f.zip |
Separate connections from edit
Diffstat (limited to 'modern')
-rw-r--r-- | modern/src/Navigation.js | 6 | ||||
-rw-r--r-- | modern/src/settings/DeviceConnectionsPage.js | 116 | ||||
-rw-r--r-- | modern/src/settings/DevicePage.js | 75 | ||||
-rw-r--r-- | modern/src/settings/DevicesPage.js | 18 | ||||
-rw-r--r-- | modern/src/settings/GroupConnectionsPage.js | 116 | ||||
-rw-r--r-- | modern/src/settings/GroupPage.js | 73 | ||||
-rw-r--r-- | modern/src/settings/GroupsPage.js | 18 | ||||
-rw-r--r-- | modern/src/settings/UserConnectionsPage.js | 139 | ||||
-rw-r--r-- | modern/src/settings/UserPage.js | 96 | ||||
-rw-r--r-- | modern/src/settings/UsersPage.js | 17 | ||||
-rw-r--r-- | modern/src/settings/components/CollectionActions.js | 20 |
11 files changed, 436 insertions, 258 deletions
diff --git a/modern/src/Navigation.js b/modern/src/Navigation.js index f5b21dd9..c2aaa69f 100644 --- a/modern/src/Navigation.js +++ b/modern/src/Navigation.js @@ -49,6 +49,9 @@ import App from './App'; import ChangeServerPage from './other/ChangeServerPage'; import DevicesPage from './settings/DevicesPage'; import ScheduledPage from './reports/ScheduledPage'; +import DeviceConnectionsPage from './settings/DeviceConnectionsPage'; +import GroupConnectionsPage from './settings/GroupConnectionsPage'; +import UserConnectionsPage from './settings/UserConnectionsPage'; const Navigation = () => { const navigate = useNavigate(); @@ -115,6 +118,7 @@ const Navigation = () => { <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" element={<DevicePage />} /> <Route path="device" element={<DevicePage />} /> <Route path="drivers" element={<DriversPage />} /> @@ -123,6 +127,7 @@ const Navigation = () => { <Route path="geofence/:id" element={<GeofencePage />} /> <Route path="geofence" element={<GeofencePage />} /> <Route path="groups" element={<GroupsPage />} /> + <Route path="group/:id/connections" element={<GroupConnectionsPage />} /> <Route path="group/:id" element={<GroupPage />} /> <Route path="group" element={<GroupPage />} /> <Route path="maintenances" element={<MaintenancesPage />} /> @@ -134,6 +139,7 @@ const Navigation = () => { <Route path="preferences" element={<PreferencesPage />} /> <Route path="server" element={<ServerPage />} /> <Route path="users" element={<UsersPage />} /> + <Route path="user/:id/connections" element={<UserConnectionsPage />} /> <Route path="user/:id" element={<UserPage />} /> <Route path="user" element={<UserPage />} /> </Route> diff --git a/modern/src/settings/DeviceConnectionsPage.js b/modern/src/settings/DeviceConnectionsPage.js new file mode 100644 index 00000000..88d47872 --- /dev/null +++ b/modern/src/settings/DeviceConnectionsPage.js @@ -0,0 +1,116 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Typography, + Container, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import LinkField from '../common/components/LinkField'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import SettingsMenu from './components/SettingsMenu'; +import { formatNotificationTitle } from '../common/util/formatter'; +import PageLayout from '../common/components/PageLayout'; +import useFeatures from '../common/util/useFeatures'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(2), + }, + details: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + paddingBottom: theme.spacing(3), + }, +})); + +const DeviceConnectionsPage = () => { + const classes = useStyles(); + const t = useTranslation(); + + const { id } = useParams(); + + const features = useFeatures(); + + return ( + <PageLayout + menu={<SettingsMenu />} + breadcrumbs={['settingsTitle', 'sharedDevice', 'sharedConnections']} + > + <Container maxWidth="xs" className={classes.container}> + <Accordion defaultExpanded> + <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <Typography variant="subtitle1"> + {t('sharedConnections')} + </Typography> + </AccordionSummary> + <AccordionDetails className={classes.details}> + <LinkField + endpointAll="/api/geofences" + endpointLinked={`/api/geofences?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="geofenceId" + label={t('sharedGeofences')} + /> + <LinkField + endpointAll="/api/notifications" + endpointLinked={`/api/notifications?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="notificationId" + titleGetter={(it) => formatNotificationTitle(t, it)} + label={t('sharedNotifications')} + /> + {!features.disableDrivers && ( + <LinkField + endpointAll="/api/drivers" + endpointLinked={`/api/drivers?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="driverId" + label={t('sharedDrivers')} + /> + )} + {!features.disableComputedAttributes && ( + <LinkField + endpointAll="/api/attributes/computed" + endpointLinked={`/api/attributes/computed?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="attributeId" + titleGetter={(it) => it.description} + label={t('sharedComputedAttributes')} + /> + )} + <LinkField + endpointAll="/api/commands" + endpointLinked={`/api/commands?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="commandId" + titleGetter={(it) => it.description} + label={t('sharedSavedCommands')} + /> + {!features.disableMaintenance && ( + <LinkField + endpointAll="/api/maintenance" + endpointLinked={`/api/maintenance?deviceId=${id}`} + baseId={id} + keyBase="deviceId" + keyLink="maintenanceId" + label={t('sharedMaintenance')} + /> + )} + </AccordionDetails> + </Accordion> + </Container> + </PageLayout> + ); +}; + +export default DeviceConnectionsPage; diff --git a/modern/src/settings/DevicePage.js b/modern/src/settings/DevicePage.js index b080d8ce..a0a6c2b8 100644 --- a/modern/src/settings/DevicePage.js +++ b/modern/src/settings/DevicePage.js @@ -16,15 +16,12 @@ import EditItemView from './components/EditItemView'; import EditAttributesAccordion from './components/EditAttributesAccordion'; import SelectField from '../common/components/SelectField'; import deviceCategories from '../common/util/deviceCategories'; -import LinkField from '../common/components/LinkField'; import { useTranslation } from '../common/components/LocalizationProvider'; import useDeviceAttributes from '../common/attributes/useDeviceAttributes'; import { useAdministrator } from '../common/util/permissions'; import SettingsMenu from './components/SettingsMenu'; import useCommonDeviceAttributes from '../common/attributes/useCommonDeviceAttributes'; -import useFeatures from '../common/util/useFeatures'; import { useCatch } from '../reactHelper'; -import { formatNotificationTitle } from '../common/util/formatter'; const useStyles = makeStyles((theme) => ({ details: { @@ -44,8 +41,6 @@ const DevicePage = () => { const commonDeviceAttributes = useCommonDeviceAttributes(t); const deviceAttributes = useDeviceAttributes(t); - const features = useFeatures(); - const [item, setItem] = useState(); const handleFiles = useCatch(async (files) => { @@ -71,7 +66,7 @@ const DevicePage = () => { setItem={setItem} validate={validate} menu={<SettingsMenu />} - breadcrumbs={['sharedDevice']} + breadcrumbs={['settingsTitle', 'sharedDevice']} > {item && ( <> @@ -170,74 +165,6 @@ const DevicePage = () => { setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={{ ...commonDeviceAttributes, ...deviceAttributes }} /> - {item.id && ( - <Accordion> - <AccordionSummary expandIcon={<ExpandMoreIcon />}> - <Typography variant="subtitle1"> - {t('sharedConnections')} - </Typography> - </AccordionSummary> - <AccordionDetails className={classes.details}> - <LinkField - endpointAll="/api/geofences" - endpointLinked={`/api/geofences?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="geofenceId" - label={t('sharedGeofences')} - /> - <LinkField - endpointAll="/api/notifications" - endpointLinked={`/api/notifications?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="notificationId" - titleGetter={(it) => formatNotificationTitle(t, it)} - label={t('sharedNotifications')} - /> - {!features.disableDrivers && ( - <LinkField - endpointAll="/api/drivers" - endpointLinked={`/api/drivers?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="driverId" - label={t('sharedDrivers')} - /> - )} - {!features.disableComputedAttributes && ( - <LinkField - endpointAll="/api/attributes/computed" - endpointLinked={`/api/attributes/computed?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="attributeId" - titleGetter={(it) => it.description} - label={t('sharedComputedAttributes')} - /> - )} - <LinkField - endpointAll="/api/commands" - endpointLinked={`/api/commands?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="commandId" - titleGetter={(it) => it.description} - label={t('sharedSavedCommands')} - /> - {!features.disableMaintenance && ( - <LinkField - endpointAll="/api/maintenance" - endpointLinked={`/api/maintenance?deviceId=${item.id}`} - baseId={item.id} - keyBase="deviceId" - keyLink="maintenanceId" - label={t('sharedMaintenance')} - /> - )} - </AccordionDetails> - </Accordion> - )} </> )} </EditItemView> diff --git a/modern/src/settings/DevicesPage.js b/modern/src/settings/DevicesPage.js index 0ad76106..8f267a21 100644 --- a/modern/src/settings/DevicesPage.js +++ b/modern/src/settings/DevicesPage.js @@ -1,7 +1,9 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { Table, TableRow, TableCell, TableHead, TableBody, } from '@mui/material'; +import LinkIcon from '@mui/icons-material/Link'; import makeStyles from '@mui/styles/makeStyles'; import { useEffectAsync } from '../reactHelper'; import { useTranslation } from '../common/components/LocalizationProvider'; @@ -23,6 +25,7 @@ const useStyles = makeStyles((theme) => ({ const DevicesPage = () => { const classes = useStyles(); + const navigate = useNavigate(); const t = useTranslation(); const hours12 = usePreference('twelveHourFormat'); @@ -46,6 +49,13 @@ const DevicesPage = () => { } }, [timestamp]); + const actionConnections = { + key: 'connections', + title: t('sharedConnections'), + icon: <LinkIcon fontSize="small" />, + handler: (deviceId) => navigate(`/settings/device/${deviceId}/connections`), + }; + return ( <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}> <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} /> @@ -71,7 +81,13 @@ const DevicesPage = () => { <TableCell>{item.contact}</TableCell> <TableCell>{formatTime(item.expirationTime, 'date', hours12)}</TableCell> <TableCell className={classes.columnAction} padding="none"> - <CollectionActions itemId={item.id} editPath="/settings/device" endpoint="devices" setTimestamp={setTimestamp} /> + <CollectionActions + itemId={item.id} + editPath="/settings/device" + endpoint="devices" + setTimestamp={setTimestamp} + customActions={[actionConnections]} + /> </TableCell> </TableRow> )) : (<TableShimmer columns={6} endAction />)} diff --git a/modern/src/settings/GroupConnectionsPage.js b/modern/src/settings/GroupConnectionsPage.js new file mode 100644 index 00000000..8ea3b88e --- /dev/null +++ b/modern/src/settings/GroupConnectionsPage.js @@ -0,0 +1,116 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Typography, + Container, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import LinkField from '../common/components/LinkField'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import SettingsMenu from './components/SettingsMenu'; +import { formatNotificationTitle } from '../common/util/formatter'; +import PageLayout from '../common/components/PageLayout'; +import useFeatures from '../common/util/useFeatures'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(2), + }, + details: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + paddingBottom: theme.spacing(3), + }, +})); + +const GroupConnectionsPage = () => { + const classes = useStyles(); + const t = useTranslation(); + + const { id } = useParams(); + + const features = useFeatures(); + + return ( + <PageLayout + menu={<SettingsMenu />} + breadcrumbs={['settingsTitle', 'groupDialog', 'sharedConnections']} + > + <Container maxWidth="xs" className={classes.container}> + <Accordion defaultExpanded> + <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <Typography variant="subtitle1"> + {t('sharedConnections')} + </Typography> + </AccordionSummary> + <AccordionDetails className={classes.details}> + <LinkField + endpointAll="/api/geofences" + endpointLinked={`/api/geofences?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="geofenceId" + label={t('sharedGeofences')} + /> + <LinkField + endpointAll="/api/notifications" + endpointLinked={`/api/notifications?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="notificationId" + titleGetter={(it) => formatNotificationTitle(t, it)} + label={t('sharedNotifications')} + /> + {!features.disableDrivers && ( + <LinkField + endpointAll="/api/drivers" + endpointLinked={`/api/drivers?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="driverId" + label={t('sharedDrivers')} + /> + )} + {!features.disableComputedAttributes && ( + <LinkField + endpointAll="/api/attributes/computed" + endpointLinked={`/api/attributes/computed?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="attributeId" + titleGetter={(it) => it.description} + label={t('sharedComputedAttributes')} + /> + )} + <LinkField + endpointAll="/api/commands" + endpointLinked={`/api/commands?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="commandId" + titleGetter={(it) => it.description} + label={t('sharedSavedCommands')} + /> + {!features.disableMaintenance && ( + <LinkField + endpointAll="/api/maintenance" + endpointLinked={`/api/maintenance?groupId=${id}`} + baseId={id} + keyBase="groupId" + keyLink="maintenanceId" + label={t('sharedMaintenance')} + /> + )} + </AccordionDetails> + </Accordion> + </Container> + </PageLayout> + ); +}; + +export default GroupConnectionsPage; diff --git a/modern/src/settings/GroupPage.js b/modern/src/settings/GroupPage.js index b9ec36c9..51fbda0e 100644 --- a/modern/src/settings/GroupPage.js +++ b/modern/src/settings/GroupPage.js @@ -10,13 +10,10 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import EditItemView from './components/EditItemView'; import EditAttributesAccordion from './components/EditAttributesAccordion'; import SelectField from '../common/components/SelectField'; -import LinkField from '../common/components/LinkField'; import { useTranslation } from '../common/components/LocalizationProvider'; import SettingsMenu from './components/SettingsMenu'; import useCommonDeviceAttributes from '../common/attributes/useCommonDeviceAttributes'; import useGroupAttributes from '../common/attributes/useGroupAttributes'; -import useFeatures from '../common/util/useFeatures'; -import { formatNotificationTitle } from '../common/util/formatter'; import { useCatch } from '../reactHelper'; import { groupsActions } from '../store'; @@ -37,8 +34,6 @@ const GroupPage = () => { const commonDeviceAttributes = useCommonDeviceAttributes(t); const groupAttributes = useGroupAttributes(t); - const features = useFeatures(); - const [item, setItem] = useState(); const onItemSaved = useCatch(async () => { @@ -98,74 +93,6 @@ const GroupPage = () => { setAttributes={(attributes) => setItem({ ...item, attributes })} definitions={{ ...commonDeviceAttributes, ...groupAttributes }} /> - {item.id && ( - <Accordion> - <AccordionSummary expandIcon={<ExpandMoreIcon />}> - <Typography variant="subtitle1"> - {t('sharedConnections')} - </Typography> - </AccordionSummary> - <AccordionDetails className={classes.details}> - <LinkField - endpointAll="/api/geofences" - endpointLinked={`/api/geofences?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="geofenceId" - label={t('sharedGeofences')} - /> - <LinkField - endpointAll="/api/notifications" - endpointLinked={`/api/notifications?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="notificationId" - titleGetter={(it) => formatNotificationTitle(t, it)} - label={t('sharedNotifications')} - /> - {!features.disableDrivers && ( - <LinkField - endpointAll="/api/drivers" - endpointLinked={`/api/drivers?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="driverId" - label={t('sharedDrivers')} - /> - )} - {!features.disableComputedAttributes && ( - <LinkField - endpointAll="/api/attributes/computed" - endpointLinked={`/api/attributes/computed?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="attributeId" - titleGetter={(it) => it.description} - label={t('sharedComputedAttributes')} - /> - )} - <LinkField - endpointAll="/api/commands" - endpointLinked={`/api/commands?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="commandId" - titleGetter={(it) => it.description} - label={t('sharedSavedCommands')} - /> - {!features.disableMaintenance && ( - <LinkField - endpointAll="/api/maintenance" - endpointLinked={`/api/maintenance?groupId=${item.id}`} - baseId={item.id} - keyBase="groupId" - keyLink="maintenanceId" - label={t('sharedMaintenance')} - /> - )} - </AccordionDetails> - </Accordion> - )} </> )} </EditItemView> diff --git a/modern/src/settings/GroupsPage.js b/modern/src/settings/GroupsPage.js index 64ae1a1d..0624f7d1 100644 --- a/modern/src/settings/GroupsPage.js +++ b/modern/src/settings/GroupsPage.js @@ -1,7 +1,9 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { Table, TableRow, TableCell, TableHead, TableBody, } from '@mui/material'; +import LinkIcon from '@mui/icons-material/Link'; import makeStyles from '@mui/styles/makeStyles'; import { useEffectAsync } from '../reactHelper'; import { useTranslation } from '../common/components/LocalizationProvider'; @@ -21,6 +23,7 @@ const useStyles = makeStyles((theme) => ({ const GroupsPage = () => { const classes = useStyles(); + const navigate = useNavigate(); const t = useTranslation(); const [timestamp, setTimestamp] = useState(Date.now()); @@ -42,6 +45,13 @@ const GroupsPage = () => { } }, [timestamp]); + const actionConnections = { + key: 'connections', + title: t('sharedConnections'), + icon: <LinkIcon fontSize="small" />, + handler: (groupId) => navigate(`/settings/group/${groupId}/connections`), + }; + return ( <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsGroups']}> <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} /> @@ -57,7 +67,13 @@ const GroupsPage = () => { <TableRow key={item.id}> <TableCell>{item.name}</TableCell> <TableCell className={classes.columnAction} padding="none"> - <CollectionActions itemId={item.id} editPath="/settings/group" endpoint="groups" setTimestamp={setTimestamp} /> + <CollectionActions + itemId={item.id} + editPath="/settings/group" + endpoint="groups" + setTimestamp={setTimestamp} + customActions={[actionConnections]} + /> </TableCell> </TableRow> )) : (<TableShimmer columns={2} endAction />)} diff --git a/modern/src/settings/UserConnectionsPage.js b/modern/src/settings/UserConnectionsPage.js new file mode 100644 index 00000000..80de8835 --- /dev/null +++ b/modern/src/settings/UserConnectionsPage.js @@ -0,0 +1,139 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Typography, + Container, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import LinkField from '../common/components/LinkField'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import SettingsMenu from './components/SettingsMenu'; +import { formatNotificationTitle } from '../common/util/formatter'; +import PageLayout from '../common/components/PageLayout'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(2), + }, + details: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + paddingBottom: theme.spacing(3), + }, +})); + +const UserConnectionsPage = () => { + const classes = useStyles(); + const t = useTranslation(); + + const { id } = useParams(); + + return ( + <PageLayout + menu={<SettingsMenu />} + breadcrumbs={['settingsTitle', 'settingsUser', 'sharedConnections']} + > + <Container maxWidth="xs" className={classes.container}> + <Accordion defaultExpanded> + <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <Typography variant="subtitle1"> + {t('sharedConnections')} + </Typography> + </AccordionSummary> + <AccordionDetails className={classes.details}> + <LinkField + endpointAll="/api/devices?all=true" + endpointLinked={`/api/devices?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="deviceId" + label={t('deviceTitle')} + /> + <LinkField + endpointAll="/api/groups?all=true" + endpointLinked={`/api/groups?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="groupId" + label={t('settingsGroups')} + /> + <LinkField + endpointAll="/api/geofences?all=true" + endpointLinked={`/api/geofences?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="geofenceId" + label={t('sharedGeofences')} + /> + <LinkField + endpointAll="/api/notifications?all=true" + endpointLinked={`/api/notifications?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="notificationId" + titleGetter={(it) => formatNotificationTitle(t, it, true)} + label={t('sharedNotifications')} + /> + <LinkField + endpointAll="/api/calendars?all=true" + endpointLinked={`/api/calendars?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="calendarId" + label={t('sharedCalendars')} + /> + <LinkField + endpointAll="/api/users?all=true" + endpointLinked={`/api/users?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="managedUserId" + label={t('settingsUsers')} + /> + <LinkField + endpointAll="/api/attributes/computed?all=true" + endpointLinked={`/api/attributes/computed?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="attributeId" + titleGetter={(it) => it.description} + label={t('sharedComputedAttributes')} + /> + <LinkField + endpointAll="/api/drivers?all=true" + endpointLinked={`/api/drivers?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="driverId" + label={t('sharedDrivers')} + /> + <LinkField + endpointAll="/api/commands?all=true" + endpointLinked={`/api/commands?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="commandId" + titleGetter={(it) => it.description} + label={t('sharedSavedCommands')} + /> + <LinkField + endpointAll="/api/maintenance?all=true" + endpointLinked={`/api/maintenance?userId=${id}`} + baseId={id} + keyBase="userId" + keyLink="maintenanceId" + label={t('sharedMaintenance')} + /> + </AccordionDetails> + </Accordion> + </Container> + </PageLayout> + ); +}; + +export default UserConnectionsPage; diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js index 582e63eb..91500eb4 100644 --- a/modern/src/settings/UserPage.js +++ b/modern/src/settings/UserPage.js @@ -22,7 +22,6 @@ import { useDispatch, useSelector } from 'react-redux'; import moment from 'moment'; import EditItemView from './components/EditItemView'; import EditAttributesAccordion from './components/EditAttributesAccordion'; -import LinkField from '../common/components/LinkField'; import { useTranslation } from '../common/components/LocalizationProvider'; import useUserAttributes from '../common/attributes/useUserAttributes'; import { sessionActions } from '../store'; @@ -32,7 +31,6 @@ import useCommonUserAttributes from '../common/attributes/useCommonUserAttribute import { useAdministrator, useRestriction, useManager } from '../common/util/permissions'; import useQuery from '../common/util/useQuery'; import { useCatch } from '../reactHelper'; -import { formatNotificationTitle } from '../common/util/formatter'; import useMapStyles from '../map/core/useMapStyles'; import { map } from '../map/core/MapView'; @@ -392,100 +390,6 @@ const UserPage = () => { </AccordionDetails> </Accordion> )} - {item.id && manager && ( - <Accordion> - <AccordionSummary expandIcon={<ExpandMoreIcon />}> - <Typography variant="subtitle1"> - {t('sharedConnections')} - </Typography> - </AccordionSummary> - <AccordionDetails className={classes.details}> - <LinkField - endpointAll="/api/devices?all=true" - endpointLinked={`/api/devices?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="deviceId" - label={t('deviceTitle')} - /> - <LinkField - endpointAll="/api/groups?all=true" - endpointLinked={`/api/groups?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="groupId" - label={t('settingsGroups')} - /> - <LinkField - endpointAll="/api/geofences?all=true" - endpointLinked={`/api/geofences?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="geofenceId" - label={t('sharedGeofences')} - /> - <LinkField - endpointAll="/api/notifications?all=true" - endpointLinked={`/api/notifications?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="notificationId" - titleGetter={(it) => formatNotificationTitle(t, it, true)} - label={t('sharedNotifications')} - /> - <LinkField - endpointAll="/api/calendars?all=true" - endpointLinked={`/api/calendars?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="calendarId" - label={t('sharedCalendars')} - /> - <LinkField - endpointAll="/api/users?all=true" - endpointLinked={`/api/users?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="managedUserId" - label={t('settingsUsers')} - /> - <LinkField - endpointAll="/api/attributes/computed?all=true" - endpointLinked={`/api/attributes/computed?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="attributeId" - titleGetter={(it) => it.description} - label={t('sharedComputedAttributes')} - /> - <LinkField - endpointAll="/api/drivers?all=true" - endpointLinked={`/api/drivers?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="driverId" - label={t('sharedDrivers')} - /> - <LinkField - endpointAll="/api/commands?all=true" - endpointLinked={`/api/commands?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="commandId" - titleGetter={(it) => it.description} - label={t('sharedSavedCommands')} - /> - <LinkField - endpointAll="/api/maintenance?all=true" - endpointLinked={`/api/maintenance?userId=${item.id}`} - baseId={item.id} - keyBase="userId" - keyLink="maintenanceId" - label={t('sharedMaintenance')} - /> - </AccordionDetails> - </Accordion> - )} </> )} </EditItemView> diff --git a/modern/src/settings/UsersPage.js b/modern/src/settings/UsersPage.js index 6cb10d25..98674c9e 100644 --- a/modern/src/settings/UsersPage.js +++ b/modern/src/settings/UsersPage.js @@ -1,8 +1,10 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { Table, TableRow, TableCell, TableHead, TableBody, } from '@mui/material'; import LoginIcon from '@mui/icons-material/Login'; +import LinkIcon from '@mui/icons-material/Link'; import makeStyles from '@mui/styles/makeStyles'; import { useCatch, useEffectAsync } from '../reactHelper'; import { formatBoolean, formatTime } from '../common/util/formatter'; @@ -25,6 +27,7 @@ const useStyles = makeStyles((theme) => ({ const UsersPage = () => { const classes = useStyles(); + const navigate = useNavigate(); const t = useTranslation(); const admin = useAdministrator(); @@ -45,12 +48,20 @@ const UsersPage = () => { } }); - const loginAction = { + const actionLogin = { + key: 'login', title: t('loginLogin'), - icon: (<LoginIcon fontSize="small" />), + icon: <LoginIcon fontSize="small" />, handler: handleLogin, }; + const actionConnections = { + key: 'connections', + title: t('sharedConnections'), + icon: <LinkIcon fontSize="small" />, + handler: (userId) => navigate(`/settings/user/${userId}/connections`), + }; + useEffectAsync(async () => { setLoading(true); try { @@ -93,7 +104,7 @@ const UsersPage = () => { editPath="/settings/user" endpoint="users" setTimestamp={setTimestamp} - customAction={admin ? loginAction : null} + customActions={admin ? [actionLogin, actionConnections] : [actionConnections]} /> </TableCell> </TableRow> diff --git a/modern/src/settings/components/CollectionActions.js b/modern/src/settings/components/CollectionActions.js index e40b3eaa..b84d8f0f 100644 --- a/modern/src/settings/components/CollectionActions.js +++ b/modern/src/settings/components/CollectionActions.js @@ -17,7 +17,7 @@ const useStyles = makeStyles(() => ({ })); const CollectionActions = ({ - itemId, editPath, endpoint, setTimestamp, customAction, + itemId, editPath, endpoint, setTimestamp, customActions, }) => { const theme = useTheme(); const classes = useStyles(); @@ -39,8 +39,8 @@ const CollectionActions = ({ setMenuAnchorEl(null); }; - const handleCustom = () => { - customAction.handler(itemId); + const handleCustom = (action) => { + action.handler(itemId); setMenuAnchorEl(null); }; @@ -59,20 +59,20 @@ const CollectionActions = ({ <MoreVertIcon fontSize="small" /> </IconButton> <Menu open={!!menuAnchorEl} anchorEl={menuAnchorEl} onClose={() => setMenuAnchorEl(null)}> - {customAction && ( - <MenuItem onClick={handleCustom}>{customAction.title}</MenuItem> - )} + {customActions && customActions.map((action) => ( + <MenuItem onClick={() => handleCustom(action)} key={action.key}>{action.title}</MenuItem> + ))} <MenuItem onClick={handleEdit}>{t('sharedEdit')}</MenuItem> <MenuItem onClick={handleRemove}>{t('sharedRemove')}</MenuItem> </Menu> </> ) : ( <div className={classes.row}> - {customAction && ( - <IconButton size="small" onClick={handleCustom}> - {customAction.icon} + {customActions && customActions.map((action) => ( + <IconButton size="small" onClick={() => handleCustom(action)} key={action.key}> + {action.icon} </IconButton> - )} + ))} <IconButton size="small" onClick={handleEdit}> <EditIcon fontSize="small" /> </IconButton> |