frontend/src/bin/BinActions.tsx

340 lines
9.6 KiB
TypeScript

import {
IconButton,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Theme,
Tooltip
} from "@mui/material";
import { blue } from "@mui/material/colors";
import RemoveSelfIcon from "@mui/icons-material/ExitToApp";
import MoreIcon from "@mui/icons-material/MoreVert";
import GroupSettingsIcon from "@mui/icons-material/Settings";
import ShareObjectIcon from "@mui/icons-material/Share";
import SensorIcon from "@mui/icons-material/SettingsInputAntenna";
import ObjectUsersIcon from "@mui/icons-material/AccountCircle";
import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
import BinSettings from "bin/BinSettings";
import { Bin, binScope, Component, User } from "models";
import { pond } from "protobuf-ts/pond";
import React, { useState } from "react";
import ObjectUsers from "user/ObjectUsers";
import RemoveSelfFromObject from "user/RemoveSelfFromObject";
import ShareObject from "user/ShareObject";
import { isOffline } from "utils/environment";
import ObjectTeams from "teams/ObjectTeams";
import BinDuplication from "./BinDuplication";
import BinsIcon from "products/Bindapt/BinsIcon";
import HelpIcon from "@mui/icons-material/Help";
import BinSensors from "./BinSensors";
import { useGlobalState, useSnackbar, useUserAPI } from "providers";
import { useMobile } from "hooks";
import { makeStyles } from "@mui/styles";
const useStyles = makeStyles((theme: Theme) => {
return ({
shareIcon: {
color: blue["500"],
"&:hover": {
color: blue["600"]
}
},
removeIcon: {
color: "var(--status-alert)"
},
red: {
color: "var(--status-alert)"
}
});
});
interface Props {
bin: Bin;
permissions: pond.Permission[];
refreshCallback: () => void;
userID: string;
components?: Map<string, Component>;
setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>;
updateBinStatus?: (componentKeys: string[], removed?: boolean) => void;
componentDevices?: Map<string, number>
setComponentDevices?: React.Dispatch<React.SetStateAction<Map<string, number>>>;
}
interface OpenState {
share: boolean;
users: boolean;
teams: boolean;
settings: boolean;
sensors: boolean;
removeSelf: boolean;
duplication: boolean;
}
export default function BinActions(props: Props) {
const classes = useStyles();
const {
bin,
permissions,
refreshCallback,
userID,
components,
componentDevices,
setComponents,
setComponentDevices,
updateBinStatus
} = props;
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [{ user }, dispatch] = useGlobalState();
const isMobile = useMobile();
const { error } = useSnackbar();
const userAPI = useUserAPI();
const [openState, setOpenState] = useState<OpenState>({
share: false,
users: false,
teams: false,
settings: false,
sensors: false,
removeSelf: false,
duplication: false
});
const startTour = () => {
if (user) {
let u = user.protobuf();
if (u.status) {
u.status.finishedBinIntro = "";
userAPI
.updateUser(userID, u)
.then(() => {
dispatch({ key: "user", value: User.any(u) });
})
.catch((err: any) => error(err));
}
}
};
const groupMenu = () => {
const canShare = permissions.includes(pond.Permission.PERMISSION_SHARE);
const canManageUsers = permissions.includes(pond.Permission.PERMISSION_USERS);
return (
<Menu
id="groupMenu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
keepMounted
disableAutoFocusItem>
{!isOffline() && canShare && (
<MenuItem
dense
onClick={() => {
setOpenState({ ...openState, share: true });
setAnchorEl(null);
}}
>
<ListItemIcon>
<ShareObjectIcon className={classes.shareIcon} />
</ListItemIcon>
<ListItemText primary="Share" />
</MenuItem>
)}
{!isOffline() && canManageUsers && (
<MenuItem
dense
onClick={() => {
setOpenState({ ...openState, users: true });
setAnchorEl(null);
}}
>
<ListItemIcon>
<ObjectUsersIcon />
</ListItemIcon>
<ListItemText primary="Users" />
</MenuItem>
)}
{!isOffline() && canManageUsers && (
<MenuItem
dense
onClick={() => {
setOpenState({ ...openState, teams: true });
setAnchorEl(null);
}}
>
<ListItemIcon>
<ObjectTeamsIcon />
</ListItemIcon>
<ListItemText primary="Teams" />
</MenuItem>
)}
<MenuItem
dense
onClick={() => {
setOpenState({ ...openState, duplication: true });
setAnchorEl(null);
}}
>
<ListItemIcon>
<BinsIcon />
</ListItemIcon>
<ListItemText primary="Duplicate Bin" />
</MenuItem>
{isMobile && (
<MenuItem
dense
onClick={() => {
setAnchorEl(null);
startTour();
}}
>
<ListItemIcon>
<HelpIcon />
</ListItemIcon>
<ListItemText primary="Redo Help Tour" />
</MenuItem>
)}
{isMobile && (
<MenuItem
dense
onClick={() => {
setAnchorEl(null);
setOpenState({ ...openState, sensors: true });
}}
>
<ListItemIcon>
<SensorIcon />
</ListItemIcon>
<ListItemText primary="Bin Sensors" />
</MenuItem>
)}
<MenuItem
dense
onClick={() => {
setOpenState({ ...openState, removeSelf: true });
setAnchorEl(null);
}}
>
<ListItemIcon>
<RemoveSelfIcon className={classes.red} />
</ListItemIcon>
<ListItemText primary="Leave" />
</MenuItem>
</Menu>
);
};
const dialogs = () => {
const hasWritePermission = permissions.includes(pond.Permission.PERMISSION_WRITE);
const key = bin.key();
const label = bin.name();
return (
<React.Fragment>
<BinSettings
mode="update"
bin={bin}
canEdit={hasWritePermission}
open={openState.settings}
userID={userID}
onClose={refresh => {
if (refresh === true) {
refreshCallback();
}
setOpenState({ ...openState, settings: false });
}}
/>
<BinSensors
mode="update"
bin={bin}
canEdit={hasWritePermission}
open={openState.sensors}
userID={userID}
components={components}
componentDevices={componentDevices}
setComponents={setComponents}
setComponentDevices={setComponentDevices}
updateBinStatus={updateBinStatus}
onClose={refresh => {
if (refresh === true) {
refreshCallback();
}
setOpenState({ ...openState, sensors: false });
}}
/>
<ShareObject
scope={binScope(key)}
label={label}
permissions={permissions}
isDialogOpen={openState.share}
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
/>
<ObjectUsers
scope={binScope(key)}
label={label}
permissions={permissions}
isDialogOpen={openState.users}
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
refreshCallback={refreshCallback}
/>
<BinDuplication
open={openState.duplication}
closeDialog={() => {
setOpenState({ ...openState, duplication: false });
}}
bin={bin}
refreshCallback={refreshCallback}
/>
<RemoveSelfFromObject
scope={binScope(key)}
label={label}
isDialogOpen={openState.removeSelf}
closeDialogCallback={() => setOpenState({ ...openState, removeSelf: false })}
/>
</React.Fragment>
);
};
return (
<React.Fragment>
{!isMobile && (
<Tooltip title="Redo help tour">
<IconButton onClick={startTour}>
<HelpIcon />
</IconButton>
</Tooltip>
)}
{!isMobile && (
<Tooltip title="Bin Sensors">
<IconButton
id="tour-bin-sensors"
onClick={() => setOpenState({ ...openState, sensors: true })}>
<SensorIcon />
</IconButton>
</Tooltip>
)}
<Tooltip title="Bin Settings">
<IconButton
id="tour-bin-settings"
onClick={() => setOpenState({ ...openState, settings: true })}>
<GroupSettingsIcon />
</IconButton>
</Tooltip>
<IconButton
aria-owns={anchorEl ? "groupMenu" : undefined}
aria-haspopup="true"
id="tour-bin-kebab"
onClick={(event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget)}>
<MoreIcon />
</IconButton>
{groupMenu()}
{dialogs()}
<ObjectTeams
scope={binScope(bin.key())}
label="Teams"
permissions={permissions}
isDialogOpen={openState.teams}
refreshCallback={refreshCallback}
closeDialogCallback={() => setOpenState({ ...openState, teams: false })}
/>
</React.Fragment>
);
}