actually added device actions

This commit is contained in:
Carter 2024-12-18 11:02:55 -06:00
parent aa3301ac35
commit 4b08791bc9
5 changed files with 726 additions and 6 deletions

View file

@ -0,0 +1,537 @@
import {
Divider,
IconButton,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Theme,
Tooltip
} from "@mui/material";
// import { amber, blue, green } from "@mui/co";
import {
AddCircle as AddIcon,
ExitToApp as RemoveSelfIcon,
History as HistoryIcon,
MoreVert as MoreIcon,
PauseCircleFilled,
PlayCircleFilled,
Reorder as ComponentOrderIcon,
Save as SaveDeviceProfileIcon,
SaveAlt as LoadDeviceProfileIcon,
Settings as DeviceSettingsIcon,
Share as ShareObjectIcon,
AccountCircle as ObjectUsersIcon,
SupervisedUserCircle as ObjectTeamsIcon,
Sync as SyncDeviceIcon,
Wifi as WifiIcon
} from "@mui/icons-material";
import { Skeleton } from "@mui/material";
import Datadog from "assets/external/datadog.png";
import { ImgIcon } from "common/ImgIcon";
import NotificationButton from "common/NotificationButton";
// import ComponentOrder from "component/ComponentOrder";
// import ComponentSettings from "component/ComponentSettings";
// import DeviceSettings from "device/DeviceSettings";
// import LoadDeviceProfile from "device/LoadDeviceProfile";
// import { PauseData } from "device/PauseData";
// import { ResumeData } from "device/ResumeData";
// import SaveDeviceProfile from "device/SaveDeviceProfile";
// import SyncDevice from "device/SyncDevice";
// import UpgradeDevice from "device/UpgradeDevice";
// import InteractionSettings from "interactions/InteractionSettings";
import { cloneDeep } from "lodash";
// import { Component, Device, deviceScope, Interaction } from "models";
import { Device, deviceScope } from "models";
// import { MatchParams } from "navigation/Routes";
import { isShareableLink } from "pbHelpers/Device";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useState } from "react";
// import { useHistory, useRouteMatch } from "react-router";
import { isOffline } from "utils/environment";
import { or } from "utils/types";
import ObjectUsers from "user/ObjectUsers";
import RemoveSelfFromObject from "user/RemoveSelfFromObject";
import ShareObject from "user/ShareObject";
// import Connection from "./Connection";
// import ObjectTeams from "teams/ObjectTeams";
// import ArcGISDeviceData from "./ArcGISDeviceData";
// import { DeviceAvailabilityMap, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
import { amber, blue, green } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";
import { useNavigate } from "react-router-dom";
const useStyles = makeStyles((theme: Theme) => {
// const isMobile = useMobile()
return ({
greenIcon: {
color: green["500"],
"&:hover": {
color: green["600"]
}
},
amberIcon: {
color: amber["600"],
"&:hover": {
color: amber["700"]
}
},
blueIcon: {
color: blue["500"],
"&:hover": {
color: blue["600"]
}
},
red: {
color: "var(--status-alert)"
}
});
});
interface Props {
device: Device;
isPaused: boolean;
// components: Component[];
// interactions: Interaction[];
refreshCallback: () => void;
// availablePositions: DeviceAvailabilityMap;
// availableOffsets: OffsetAvailabilityMap;
permissions: Array<pond.Permission>;
preferences: pond.DevicePreferences;
isLoading: boolean;
toggleNotificationPreference: Function;
}
interface DialogState {
isAddComponentDialogOpen: boolean;
isDeviceSettingsDialogOpen: boolean;
isShareObjectDialogOpen: boolean;
isSyncDeviceDialogOpen: boolean;
isPauseDialogOpen: boolean;
isResumeDialogOpen: boolean;
isInteractionSettingsOpen: boolean;
isObjectUsersDialogOpen: boolean;
isObjectTeamsDialogOpen: boolean;
isRemoveSelfDialogOpen: boolean;
isSaveDeviceProfileOpen: boolean;
isLoadDeviceProfileOpen: boolean;
isConnectionDialogOpen: boolean;
isComponentOrderDialogOpen: boolean;
isJsonDataDialogOpen: boolean;
}
export default function DeviceActions(props: Props) {
const {
device,
isPaused,
// components,
// interactions,
refreshCallback,
// availablePositions,
// availableOffsets,
permissions,
preferences,
isLoading,
toggleNotificationPreference
} = props;
const classes = useStyles();
const [{ user }] = useGlobalState();
const canWrite = permissions.includes(pond.Permission.PERMISSION_WRITE);
const navigate = useNavigate();
// const match = useRouteMatch<MatchParams>();
const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
const [dialogState, setDialogState] = useState<DialogState>({
isAddComponentDialogOpen: false,
isDeviceSettingsDialogOpen: false,
isShareObjectDialogOpen: false,
isSyncDeviceDialogOpen: false,
isPauseDialogOpen: false,
isResumeDialogOpen: false,
isInteractionSettingsOpen: false,
isObjectUsersDialogOpen: false,
isObjectTeamsDialogOpen: false,
isRemoveSelfDialogOpen: false,
isSaveDeviceProfileOpen: false,
isLoadDeviceProfileOpen: false,
isConnectionDialogOpen: false,
isComponentOrderDialogOpen: false,
isJsonDataDialogOpen: false
});
const openDialog = (target: keyof DialogState) => {
let updatedDialogState = cloneDeep(dialogState);
updatedDialogState[target] = true;
setDialogState(updatedDialogState);
setMenuAnchorEl(null);
};
const closeDialog = (target: keyof DialogState) => {
let updatedDialogState = cloneDeep(dialogState);
updatedDialogState[target] = false;
setDialogState(updatedDialogState);
};
const closeAndRefresh = (target: keyof DialogState) => (refresh: boolean) => {
closeDialog(target);
if (refresh) props.refreshCallback();
};
const deviceMenu = () => {
const canShare = permissions.includes(pond.Permission.PERMISSION_SHARE);
const canManageUsers = permissions.includes(pond.Permission.PERMISSION_USERS);
const canProvision = user.allowedTo("provision");
const isCellular = device.settings.platform === pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON;
const canPause = canWrite && isCellular && user.allowedTo("pause-data");
const isWifi = device.settings.platform === pond.DevicePlatform.DEVICE_PLATFORM_PHOTON;
// const isShareLink = isShareableLink(match.params.deviceID);
const isShareLink = false
return (
<Menu
id="deviceMenu"
anchorEl={menuAnchorEl ? menuAnchorEl : null}
open={menuAnchorEl !== null}
onClose={() => setMenuAnchorEl(null)}
disableAutoFocusItem>
{canWrite && (
<MenuItem
onClick={() => {
openDialog("isAddComponentDialogOpen");
}}
dense>
<ListItemIcon>
<AddIcon className={classes.greenIcon} />
</ListItemIcon>
<ListItemText secondary="Add Component" />
</MenuItem>
)}
{canWrite && (
<MenuItem onClick={() => openDialog("isInteractionSettingsOpen")} dense>
<ListItemIcon>
<AddIcon className={classes.greenIcon} />
</ListItemIcon>
<ListItemText secondary="Add Interaction" />
</MenuItem>
)}
{canWrite && <Divider />}
{!isOffline() && canShare && (
<MenuItem onClick={() => openDialog("isShareObjectDialogOpen")} dense>
<ListItemIcon>
<ShareObjectIcon className={classes.blueIcon} />
</ListItemIcon>
<ListItemText secondary="Share" />
</MenuItem>
)}
{canWrite &&
user.hasAdmin() && ( //TODO: once the resync issue has been resolved remove the admin check
<MenuItem onClick={() => openDialog("isSyncDeviceDialogOpen")} dense>
<ListItemIcon>
<SyncDeviceIcon className={classes.amberIcon} />
</ListItemIcon>
<ListItemText secondary="Resync" />
</MenuItem>
)}
{user.hasFeature("json") && (
<MenuItem onClick={() => openDialog("isJsonDataDialogOpen")} dense>
<ListItemIcon>
<LoadDeviceProfileIcon className={classes.greenIcon} />
</ListItemIcon>
<ListItemText secondary="Export Json Data" />
</MenuItem>
)}
<MenuItem onClick={() => openDialog("isComponentOrderDialogOpen")} dense>
<ListItemIcon>
<ComponentOrderIcon />
</ListItemIcon>
<ListItemText secondary="Component Order" />
</MenuItem>
<Divider />
{canPause && !isPaused && (
<MenuItem onClick={() => openDialog("isPauseDialogOpen")} dense>
<ListItemIcon>
<PauseCircleFilled />
</ListItemIcon>
<ListItemText secondary="Pause Data" />
</MenuItem>
)}
{canPause && isPaused && (
<MenuItem onClick={() => openDialog("isResumeDialogOpen")} dense>
<ListItemIcon>
<PlayCircleFilled className={classes.greenIcon} />
</ListItemIcon>
<ListItemText secondary="Resume Data" />
</MenuItem>
)}
{isWifi && canWrite && (
<MenuItem onClick={() => openDialog("isConnectionDialogOpen")} dense>
<ListItemIcon>
<WifiIcon />
</ListItemIcon>
<ListItemText secondary="Connection" />
</MenuItem>
)}
{(canPause || (isWifi && canWrite)) && <Divider />}
{canProvision && (
<MenuItem onClick={() => openDialog("isSaveDeviceProfileOpen")} dense>
<ListItemIcon>
<SaveDeviceProfileIcon className={classes.blueIcon} />
</ListItemIcon>
<ListItemText secondary="Save Profile" />
</MenuItem>
)}
{canProvision && (
<MenuItem onClick={() => openDialog("isLoadDeviceProfileOpen")} dense>
<ListItemIcon>
<LoadDeviceProfileIcon className={classes.greenIcon} />
</ListItemIcon>
<ListItemText secondary="Load Profile" />
</MenuItem>
)}
{canProvision && <Divider />}
{!isOffline() && canManageUsers && (
<MenuItem onClick={() => openDialog("isObjectUsersDialogOpen")} dense>
<ListItemIcon>
<ObjectUsersIcon />
</ListItemIcon>
<ListItemText secondary="Users" />
</MenuItem>
)}
{!isOffline() && canManageUsers && (
<MenuItem onClick={() => openDialog("isObjectTeamsDialogOpen")} dense>
<ListItemIcon>
<ObjectTeamsIcon />
</ListItemIcon>
<ListItemText secondary="Teams" />
</MenuItem>
)}
<MenuItem
onClick={() => {
// const groupID: number = parseInt(match.params.groupID, 10);
// const groupPath: string = groupID > 0 ? "/groups/" + groupID.toString() : "";
const devicePath: string = "/devices/" + device.id().toString();
// let path = groupPath + devicePath + "/history";
let path = devicePath + "/history";
navigate(path);
}}
dense>
<ListItemIcon>
<HistoryIcon />
</ListItemIcon>
<ListItemText secondary="History" />
</MenuItem>
{/* {canProvision && (
<MenuItem
onClick={() => {
window.open(
"https://app.datadoghq.com/logs?cols=core_host%2Ccore_service&index=main&live=true&messageDisplay=inline&stream_sort=desc&query=%40device%3A" +
device.id(),
"_blank"
);
}}
dense>
<ListItemIcon>
<ImgIcon src={Datadog} />
</ListItemIcon>
<ListItemText secondary="Datadog" />
</MenuItem>
)} */}
<Divider />
{!isShareLink && (
<MenuItem onClick={() => openDialog("isRemoveSelfDialogOpen")} dense>
<ListItemIcon>
<RemoveSelfIcon className={classes.red} />
</ListItemIcon>
<ListItemText secondary="Leave" />
</MenuItem>
)}
</Menu>
);
};
const dialogs = () => {
const deviceName = device.name();
const {
isAddComponentDialogOpen,
isDeviceSettingsDialogOpen,
isShareObjectDialogOpen,
isSyncDeviceDialogOpen,
isPauseDialogOpen,
isResumeDialogOpen,
isInteractionSettingsOpen,
isObjectUsersDialogOpen,
isObjectTeamsDialogOpen,
isRemoveSelfDialogOpen,
isSaveDeviceProfileOpen,
isLoadDeviceProfileOpen,
isConnectionDialogOpen,
isComponentOrderDialogOpen,
isJsonDataDialogOpen
} = dialogState;
return (
<React.Fragment>
{/* <DeviceSettings
device={device}
isDialogOpen={or(isDeviceSettingsDialogOpen, false)}
closeDialogCallback={() => closeDialog("isDeviceSettingsDialogOpen")}
refreshCallback={refreshCallback}
canEdit={canWrite}
components={components}
/> */}
{/* <ComponentSettings
mode="add"
device={device}
isDialogOpen={or(isAddComponentDialogOpen, false)}
closeDialogCallback={() => closeDialog("isAddComponentDialogOpen")}
availablePositions={availablePositions}
availableOffsets={availableOffsets}
refreshCallback={refreshCallback}
canEdit={canWrite}
/>
<SyncDevice
device={device}
isDialogOpen={or(isSyncDeviceDialogOpen, false)}
closeDialogCallback={() => closeDialog("isSyncDeviceDialogOpen")}
refreshCallback={refreshCallback}
/> */}
<ShareObject
scope={deviceScope(device.id().toString())}
label={deviceName}
permissions={permissions}
isDialogOpen={or(isShareObjectDialogOpen, false)}
closeDialogCallback={() => closeDialog("isShareObjectDialogOpen")}
/>
{/* <InteractionSettings
device={device}
components={cloneDeep(components)}
mode="add"
isDialogOpen={or(isInteractionSettingsOpen, false)}
closeDialogCallback={() => closeDialog("isInteractionSettingsOpen")}
refreshCallback={refreshCallback}
canEdit={canWrite}
/> */}
<ObjectUsers
scope={deviceScope(device.id().toString())}
label={deviceName}
permissions={permissions}
isDialogOpen={or(isObjectUsersDialogOpen, false)}
closeDialogCallback={() => closeDialog("isObjectUsersDialogOpen")}
refreshCallback={refreshCallback}
/>
{/* <ObjectTeams
scope={deviceScope(device.id().toString())}
label={deviceName}
permissions={permissions}
isDialogOpen={or(isObjectTeamsDialogOpen, false)}
closeDialogCallback={() => closeDialog("isObjectTeamsDialogOpen")}
refreshCallback={refreshCallback}
/> */}
<RemoveSelfFromObject
scope={deviceScope(device.id().toString())}
label={deviceName}
isDialogOpen={isRemoveSelfDialogOpen}
closeDialogCallback={() => closeDialog("isRemoveSelfDialogOpen")}
/>
{/* <SaveDeviceProfile
isDialogOpen={isSaveDeviceProfileOpen}
closeDialogCallback={() => closeDialog("isSaveDeviceProfileOpen")}
device={device}
components={components}
interactions={interactions}
tagIds={device.status.tagKeys}
refreshCallback={refreshCallback}
/> */}
{/* <LoadDeviceProfile
isDialogOpen={isLoadDeviceProfileOpen}
closeDialogCallback={() => closeDialog("isLoadDeviceProfileOpen")}
device={device}
refreshCallback={refreshCallback}
/> */}
{/* <PauseData
id={device.id()}
isOpen={isPauseDialogOpen}
close={closeAndRefresh("isPauseDialogOpen")}
/>
<ResumeData
id={device.id()}
isOpen={isResumeDialogOpen}
close={closeAndRefresh("isResumeDialogOpen")}
/>
<Connection
deviceID={device.id()}
open={isConnectionDialogOpen}
close={closeAndRefresh("isConnectionDialogOpen")}
deviceName={device.name()}
/>
<ComponentOrder
device={device}
devicePreferences={preferences}
components={components}
open={isComponentOrderDialogOpen}
close={closeAndRefresh("isComponentOrderDialogOpen")}
/>
<ArcGISDeviceData
open={isJsonDataDialogOpen}
close={closeAndRefresh("isJsonDataDialogOpen")}
device={device}
components={components}
/> */}
</React.Fragment>
);
};
const buttons = () => {
return (
<React.Fragment>
{/* {canWrite && user.allowedTo("provision") && <UpgradeDevice device={device} />} */}
{/* {!isShareableLink(match.params.deviceID) && ( */}
<NotificationButton
notify={preferences.notify}
onChange={toggleNotificationPreference}
tooltip={
"Notifications for " +
device.name() +
" are " +
(preferences.notify ? "enabled" : "disabled")
}
hidden={isLoading}
/>
{/* )} */}
<Tooltip title="Settings">
<IconButton onClick={() => openDialog("isDeviceSettingsDialogOpen")}>
<DeviceSettingsIcon />
</IconButton>
</Tooltip>
<Tooltip title="More">
<IconButton
aria-owns={"deviceMenu"}
aria-haspopup="true"
onClick={event => setMenuAnchorEl(event.currentTarget)}>
<MoreIcon />
</IconButton>
</Tooltip>
</React.Fragment>
);
};
if (isLoading)
return (
<Skeleton>
<IconButton />
<IconButton />
<IconButton />
</Skeleton>
);
return (
<React.Fragment>
{buttons()}
{deviceMenu()}
{dialogs()}
</React.Fragment>
);
}

View file

@ -5,7 +5,7 @@ import { makeStyles } from "@mui/styles";
import ResponsiveTable, { Column } from "common/ResponsiveTable"; import ResponsiveTable, { Column } from "common/ResponsiveTable";
import ProvisionDevice from "device/ProvisionDevice"; import ProvisionDevice from "device/ProvisionDevice";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
import { useDeviceAPI } from "providers"; import { useDeviceAPI, useGroupAPI } from "providers";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import PageContainer from "./PageContainer"; import PageContainer from "./PageContainer";
import { useMobile } from "hooks"; import { useMobile } from "hooks";
@ -34,6 +34,7 @@ export default function Devices() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const deviceAPI = useDeviceAPI(); const deviceAPI = useDeviceAPI();
const groupAPI = useGroupAPI();
const [limit, setLimit] = useState(10); const [limit, setLimit] = useState(10);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const [order, ] = useState<"asc" | "desc">("asc"); const [order, ] = useState<"asc" | "desc">("asc");
@ -43,6 +44,13 @@ export default function Devices() {
const [devices, setDevices] = useState<pond.Device[]>([]) const [devices, setDevices] = useState<pond.Device[]>([])
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false); const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
const [groupLimit, setGroupLimit] = useState(10);
const [groupPage, setGroupPage] = useState(0);
const [orderGroup, ] = useState<"asc" | "desc">("asc");
const [orderGroupBy, ] = useState("name");
const [searchGroup, setGroupSearch] = useState("");
const [totalGroups, setTotalGroups] = useState(0);
const openProvisionDialog = () => { const openProvisionDialog = () => {
setIsProvisionDialogOpen(true); setIsProvisionDialogOpen(true);
}; };
@ -51,6 +59,18 @@ export default function Devices() {
setIsProvisionDialogOpen(false); setIsProvisionDialogOpen(false);
}; };
const loadGroups = () => {
groupAPI.listGroups(
groupLimit,
groupPage*groupLimit,
orderGroup,
orderGroupBy,
searchGroup,
).then(resp => {
console.log(resp.data)
})
}
const loadDevices = () => { const loadDevices = () => {
deviceAPI.list( deviceAPI.list(
limit, limit,
@ -76,12 +96,17 @@ export default function Devices() {
loadDevices() loadDevices()
}, [limit, page, order, orderBy, search]) }, [limit, page, order, orderBy, search])
useEffect(() => {
loadDevices()
}, [groupLimit, groupPage, orderGroup, orderGroupBy, searchGroup])
const handleChange = (event: any) => { const handleChange = (event: any) => {
setLimit(event.target.value); setLimit(event.target.value);
}; };
const appendToUrl = (appendage: number | string) => { const appendToUrl = (appendage: number | string) => {
const basePath = location.pathname.replace(/\/$/, ""); // Ensure no trailing slash const basePath = location.pathname.replace(/\/$/, "");
return(`${basePath}/${appendage}`); return(`${basePath}/${appendage}`);
}; };

View file

@ -15,7 +15,7 @@ export {
// useFieldAPI, // useFieldAPI,
// useFieldMarkerAPI, // useFieldMarkerAPI,
// useFirmwareAPI, // useFirmwareAPI,
// useGroupAPI, useGroupAPI,
// useHarvestPlanAPI, // useHarvestPlanAPI,
// useHarvestYearAPI, // useHarvestYearAPI,
// useHomeMarkerAPI, // useHomeMarkerAPI,

View file

@ -0,0 +1,154 @@
import { useHTTP, usePermissionAPI } from "hooks";
import { createContext, PropsWithChildren, useContext } from "react";
import { pond } from "protobuf-ts/pond";
import { or } from "utils/types";
import { User, groupScope } from "models";
import { pondURL } from "./pond";
import { AxiosResponse } from "axios";
import { useGlobalState } from "providers";
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
export interface IGroupAPIContext {
addGroup: (group: pond.GroupSettings) => Promise<any>;
addDevice: (group: number, device: number) => Promise<any>;
removeDevice: (group: number, device: number) => Promise<any>;
updateGroup: (id: number, group: pond.GroupSettings) => Promise<any>;
removeGroup: (id: number) => Promise<any>;
getGroup: (id: number) => Promise<any>;
listGroups: (
limit: number,
offset: number,
order?: "asc" | "desc",
orderBy?: string,
search?: string,
asRoot?: boolean
) => Promise<any>;
listGroupDevices: (
id: number,
limit: number,
offset: number,
order: "asc" | "desc",
search?: string,
comprehensive?: boolean
) => Promise<AxiosResponse<pond.GetMultiDeviceResponse>>;
updateGroupPermissions: (id: number, users: User[]) => Promise<any>;
}
export const GroupAPIContext = createContext<IGroupAPIContext>({} as IGroupAPIContext);
interface Props {}
export default function GroupProvider(props: PropsWithChildren<Props>) {
const { children } = props;
const { get, post, put, del } = useHTTP();
const permissionAPI = usePermissionAPI();
const [{ as }] = useGlobalState();
const addGroup = (group: pond.GroupSettings) => {
if (as) return post(pondURL("/groups?as=" + as), group);
return post(pondURL("/groups"), group);
};
const addDevice = (group: number, device: number) => {
let url = "/groups/" + group + "/devices/" + device + "/add";
if (as) return post(pondURL(url + as), group);
return post(pondURL(url), group);
};
const removeDevice = (group: number, device: number) => {
let url = "/groups/" + group + "/devices/" + device + "/remove";
if (as) return post(pondURL(url + as), group);
return post(pondURL(url), group);
};
const updateGroup = (id: number, group: pond.GroupSettings) => {
if (as) return put(pondURL("/groups/" + id + "?as=" + as), group);
return put(pondURL("/groups/" + id), group);
};
const removeGroup = (id: number) => {
if (as) return del(pondURL("/groups/" + id + "?as=" + as));
return del(pondURL("/groups/" + id));
};
const getGroup = (id: number) => {
if (as) return get(pondURL("/groups/" + id + "?as=" + as));
return get(pondURL("/groups/" + id));
};
const listGroups = (
limit: number,
offset: number,
order?: "asc" | "desc",
orderBy?: string,
search?: string,
asRoot?: boolean
) => {
let keys = getContextKeys()
let types = getContextTypes()
return get(
pondURL(
"/groups" +
"?limit=" +
limit +
"&offset=" +
offset +
("&order=" + or(order, "asc")) +
("&orderBy=" + or(orderBy, "id")) +
(search ? "&search=" + search : "") +
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
(as ? "&as=" + as.toString() : "") +
(keys.length > 0 ? "&keys=" + keys : "") +
(types.length > 0 ? "&types=" + types : "")
)
);
};
const listGroupDevices = (
id: number,
limit: number,
offset: number,
order: "asc" | "desc",
search?: string,
comprehensive: boolean = false
) => {
return get<pond.GetMultiDeviceResponse>(
pondURL(
"/groups/" +
id +
"/devices?limit=" +
limit +
"&offset=" +
offset +
"&order=" +
order +
(search ? "&search=" + search : "") +
(comprehensive ? "&comprehensive=" + comprehensive.toString() : "") +
(as ? "&as=" + as.toString() : "")
)
);
};
const updateGroupPermissions = (id: number, users: User[]) => {
return permissionAPI.updatePermissions(groupScope(id.toString()), users);
};
return (
<GroupAPIContext.Provider
value={{
addGroup,
addDevice,
removeDevice,
updateGroup,
removeGroup,
getGroup,
listGroups,
listGroupDevices,
updateGroupPermissions
}}>
{children}
</GroupAPIContext.Provider>
);
}
export const useGroupAPI = () => useContext(GroupAPIContext);

View file

@ -9,6 +9,7 @@ import GateProvider, { useGateAPI } from "./gateAPI";
import NoteProvider, { useNoteAPI } from "./noteAPI"; import NoteProvider, { useNoteAPI } from "./noteAPI";
import DeviceProvider, { useDeviceAPI } from "./deviceAPI"; import DeviceProvider, { useDeviceAPI } from "./deviceAPI";
import BackpackProvider, { useBackpackAPI } from "./backpackAPI"; import BackpackProvider, { useBackpackAPI } from "./backpackAPI";
import GroupProvider, { useGroupAPI } from "./groupAPI";
// import NoteProvider from "providers/noteAPI"; // import NoteProvider from "providers/noteAPI";
export const pondURL = (partial: string, demo: boolean = false): string => { export const pondURL = (partial: string, demo: boolean = false): string => {
@ -33,9 +34,11 @@ export default function PondProvider(props: PropsWithChildren<any>) {
<GateProvider> <GateProvider>
<NoteProvider> <NoteProvider>
<DeviceProvider> <DeviceProvider>
<BackpackProvider> <GroupProvider>
{children} <BackpackProvider>
</BackpackProvider> {children}
</BackpackProvider>
</GroupProvider>
</DeviceProvider> </DeviceProvider>
</NoteProvider> </NoteProvider>
</GateProvider> </GateProvider>
@ -55,5 +58,6 @@ export {
usePermissionAPI, usePermissionAPI,
useBinAPI, useBinAPI,
useGateAPI, useGateAPI,
useGroupAPI,
useNoteAPI, useNoteAPI,
}; };