457 lines
13 KiB
TypeScript
457 lines
13 KiB
TypeScript
import {
|
|
Tab,
|
|
Tabs,
|
|
Button,
|
|
Theme,
|
|
useTheme,
|
|
Menu,
|
|
MenuItem,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions
|
|
} from "@mui/material";
|
|
import { Terminal } from "models";
|
|
import { useTerminalAPI, useGlobalState } from "providers";
|
|
import React, { useCallback, useEffect, useState } from "react";
|
|
import PageContainer from "./PageContainer";
|
|
import AddIcon from "@mui/icons-material/Add";
|
|
import TerminalSettings from "terminal/TerminalSettings";
|
|
import { Delete, MoreVert, ExitToApp as RemoveSelfIcon } from "@mui/icons-material";
|
|
import EditIcon from "@mui/icons-material/Edit";
|
|
import { blue, green, red } from "@mui/material/colors";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import { useSnackbar, useUserAPI } from "hooks";
|
|
import GateList from "gate/GateList";
|
|
import { Scope } from "models";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import ObjectUsers from "user/ObjectUsers";
|
|
import ObjectTeams from "teams/ObjectTeams";
|
|
import ObjectUsersIcon from "@mui/icons-material/AccountCircle";
|
|
import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
|
|
import ShareObject from "user/ShareObject";
|
|
import RemoveSelfFromObject from "user/RemoveSelfFromObject";
|
|
import { makeStyles } from "@mui/styles";
|
|
|
|
const parentTab = {
|
|
"&": {
|
|
margin: "0px",
|
|
marginTop: "4px",
|
|
marginLeft: "4px",
|
|
animationDuration: "10s",
|
|
background: "rgba(150, 150, 150, 0)",
|
|
|
|
borderRadius: "-5px",
|
|
borderTopLeftRadius: "6px",
|
|
borderTopRightRadius: "6px"
|
|
},
|
|
"&:hover": {
|
|
background: "linear-gradient(rgba(150, 150, 150, 0.2), rgba(150, 150, 150, 0))"
|
|
}
|
|
};
|
|
|
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
tab: {
|
|
...parentTab,
|
|
minWidth: theme.spacing(25),
|
|
left: 0,
|
|
height: "100%",
|
|
background: theme.palette.background.paper
|
|
},
|
|
smallTab: {
|
|
...parentTab,
|
|
width: theme.spacing(8),
|
|
minWidth: theme.spacing(8),
|
|
background: theme.palette.background.paper
|
|
},
|
|
selectedTab: {
|
|
borderRadius: "-5px",
|
|
borderTopLeftRadius: "6px",
|
|
borderTopRightRadius: "6px",
|
|
background: theme.palette.background.default,
|
|
"&:hover": {
|
|
background:
|
|
"linear-gradient(rgba(150, 150, 150, 0.3)," + theme.palette.background.default + " 80%)"
|
|
}
|
|
},
|
|
tabText: {
|
|
position: "absolute",
|
|
left: theme.spacing(2),
|
|
color: "transparent",
|
|
textAlign: "left",
|
|
width: "80%",
|
|
backgroundImage: "linear-gradient(to right, white 70%, rgba(200, 200, 200, 0) 87.5%)",
|
|
backgroundClip: "text",
|
|
WebkitBackgroundClip: "text",
|
|
overflowX: "hidden",
|
|
whiteSpace: "nowrap",
|
|
top: 12
|
|
},
|
|
icon: {
|
|
display: "flex",
|
|
position: "absolute",
|
|
right: 0,
|
|
top: 6,
|
|
padding: 6,
|
|
marginRight: "2px",
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: "18px",
|
|
background: "rgba(0,0,0,0)",
|
|
"&:hover": {
|
|
background:
|
|
"radial-gradient(closest-side, rgba(150, 150, 150, 0.5) 50%, rgba(150, 150, 150, 0.5))"
|
|
}
|
|
},
|
|
green: {
|
|
marginRight: 0,
|
|
paddingRight: 0,
|
|
color: green["500"],
|
|
"&:hover": {
|
|
color: green["600"]
|
|
}
|
|
},
|
|
red: {
|
|
marginRight: 0,
|
|
paddingRight: 0,
|
|
color: red["500"],
|
|
"&:hover": {
|
|
color: red["600"]
|
|
}
|
|
},
|
|
blueIcon: {
|
|
color: blue["500"],
|
|
"&:hover": {
|
|
color: blue["600"]
|
|
}
|
|
},
|
|
addGate: {
|
|
position: "absolute",
|
|
bottom: 20,
|
|
right: 20,
|
|
backgroundColor: theme.palette.primary.main
|
|
}
|
|
}));
|
|
|
|
interface Props {
|
|
terminal?: Terminal;
|
|
}
|
|
|
|
export default function Terminals(props: Props) {
|
|
const terminalAPI = useTerminalAPI();
|
|
const [value, setValue] = useState(0);
|
|
const [terminals, setTerminals] = useState<Terminal[]>([]);
|
|
const classes = useStyles();
|
|
const theme = useTheme();
|
|
const [addTerminal, setAddTerminal] = useState(false);
|
|
const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
|
|
const [currentTerminal, setCurrentTerminal] = useState<Terminal>();
|
|
const [removeTerminal, setRemoveTerminal] = useState(false);
|
|
const { openSnack } = useSnackbar();
|
|
const [{ user, as }] = useGlobalState();
|
|
const userAPI = useUserAPI();
|
|
const [permissions, setPermissions] = useState<pond.Permission[]>([]);
|
|
const [userSharing, setUserSharing] = useState(false);
|
|
const [teamSharing, setTeamSharing] = useState(false);
|
|
const [baseShareDialog, setBaseShareDialog] = useState(false);
|
|
const [removeSelfDialog, setRemoveSelfDialog] = useState(false);
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (currentTerminal) {
|
|
let key = currentTerminal?.key;
|
|
let kind = "terminal";
|
|
if (as) {
|
|
key = as;
|
|
kind = "team";
|
|
}
|
|
userAPI.getUser(user.id(), { key: key, kind: kind } as Scope).then(resp => {
|
|
setPermissions(resp.permissions);
|
|
});
|
|
}
|
|
}, [as, userAPI, user, currentTerminal]);
|
|
|
|
useEffect(() => {
|
|
if (props.terminal) {
|
|
setCurrentTerminal(props.terminal);
|
|
}
|
|
}, [props.terminal]);
|
|
|
|
const load = useCallback(() => {
|
|
if(loading) return
|
|
setLoading(true)
|
|
terminalAPI.listTerminals(200, 0, "asc", "name", as).then(resp => {
|
|
setTerminals(resp.data.terminals.map(a => Terminal.any(a)));
|
|
}).catch(err => {
|
|
console.log("There was a problem loading Terminals")
|
|
}).finally(() => {
|
|
setLoading(false)
|
|
})
|
|
}, [terminalAPI, openSnack, as]); //eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
useEffect(() => {
|
|
load();
|
|
}, [load]);
|
|
|
|
const remove = () => {
|
|
if (currentTerminal) {
|
|
terminalAPI
|
|
.removeTerminal(currentTerminal.key, as)
|
|
.then(resp => {
|
|
openSnack("Terminal Deleted");
|
|
let a = terminals;
|
|
a.splice(terminals.indexOf(currentTerminal), 1);
|
|
setTerminals([...a]);
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to delete terminal");
|
|
})
|
|
.finally(() => {
|
|
setRemoveTerminal(false);
|
|
});
|
|
}
|
|
};
|
|
|
|
const terminalMenu = () => {
|
|
if (currentTerminal === undefined) return;
|
|
return (
|
|
<Menu
|
|
id="terminalMenu"
|
|
anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
|
open={menuAnchorEl !== null}
|
|
onClose={() => {
|
|
setMenuAnchorEl(null);
|
|
}}>
|
|
{permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
|
<MenuItem
|
|
onClick={() => {
|
|
setAddTerminal(true);
|
|
setMenuAnchorEl(null);
|
|
}}
|
|
aria-label="Edit Terminal"
|
|
dense>
|
|
<ListItemIcon>
|
|
<EditIcon className={classes.green} />
|
|
</ListItemIcon>
|
|
<ListItemText secondary="Edit Terminal" />
|
|
</MenuItem>
|
|
)}
|
|
{permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
|
<MenuItem
|
|
onClick={() => {
|
|
setRemoveTerminal(true);
|
|
setMenuAnchorEl(null);
|
|
}}
|
|
aria-label="Remove Terminal"
|
|
dense>
|
|
<ListItemIcon>
|
|
<Delete className={classes.red} />
|
|
</ListItemIcon>
|
|
<ListItemText secondary="Delete Terminal" />
|
|
</MenuItem>
|
|
)}
|
|
{permissions.includes(pond.Permission.PERMISSION_USERS) && (
|
|
<MenuItem
|
|
dense
|
|
onClick={() => {
|
|
setUserSharing(true);
|
|
setMenuAnchorEl(null);
|
|
}}>
|
|
<ListItemIcon>
|
|
<ObjectUsersIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Users" />
|
|
</MenuItem>
|
|
)}
|
|
{permissions.includes(pond.Permission.PERMISSION_USERS) && (
|
|
<MenuItem
|
|
dense
|
|
onClick={() => {
|
|
setTeamSharing(true);
|
|
setMenuAnchorEl(null);
|
|
}}>
|
|
<ListItemIcon>
|
|
<ObjectTeamsIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Teams" />
|
|
</MenuItem>
|
|
)}
|
|
<MenuItem
|
|
dense
|
|
onClick={() => {
|
|
setRemoveSelfDialog(true);
|
|
setMenuAnchorEl(null);
|
|
}}>
|
|
<ListItemIcon>
|
|
<RemoveSelfIcon className={classes.red} />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Leave" />
|
|
</MenuItem>
|
|
</Menu>
|
|
);
|
|
};
|
|
|
|
const changeTabs = (newValue: number) => {
|
|
//TODO: change this to just change the selected tab
|
|
if (newValue <= terminals.length) {
|
|
setValue(newValue);
|
|
if (terminals[newValue - 1]) {
|
|
setCurrentTerminal(terminals[newValue -1])
|
|
} else {
|
|
setCurrentTerminal(undefined)
|
|
}
|
|
}
|
|
};
|
|
|
|
const gateDisplay = () => {
|
|
return (
|
|
<GateList
|
|
currentTerminal={currentTerminal?.key}
|
|
terminals={terminals}
|
|
useMobile={props.terminal !== undefined}
|
|
/>
|
|
);
|
|
};
|
|
|
|
//could update the tabs in the same way as the bin yard tabs
|
|
const terminalTabs = () => {
|
|
return (
|
|
<React.Fragment>
|
|
{/* TODO: convert this to use DraggableTabs */}
|
|
<Tabs
|
|
variant="scrollable"
|
|
scrollButtons="auto"
|
|
value={value}
|
|
onChange={(_, v) => changeTabs(v)}
|
|
TabIndicatorProps={{ style: { background: "rgba(0,0,0,0)" } }}>
|
|
<Tab
|
|
key={"all"}
|
|
label="All"
|
|
disableTouchRipple
|
|
classes={{ root: classes.smallTab, selected: classes.selectedTab }}
|
|
style={{ marginLeft: theme.spacing(1) }}
|
|
/>
|
|
{terminals.map((a, index) => (
|
|
<Tab
|
|
classes={{ root: classes.tab, selected: classes.selectedTab }}
|
|
disableTouchRipple
|
|
key={a.key}
|
|
label={
|
|
<span>
|
|
<div className={classes.tabText}>{a.name}</div>
|
|
<MoreVert
|
|
className={classes.icon}
|
|
onClick={event => {
|
|
event.stopPropagation()
|
|
let target = event.currentTarget;
|
|
setMenuAnchorEl(target);
|
|
setCurrentTerminal(terminals[index]);
|
|
}}
|
|
/>
|
|
</span>
|
|
}
|
|
/>
|
|
))}
|
|
|
|
<Tab
|
|
classes={{ root: classes.smallTab, selected: classes.selectedTab }}
|
|
label={terminals.length === 0 ? "Add Terminal" : <AddIcon />}
|
|
onClick={() => {
|
|
setCurrentTerminal(undefined);
|
|
setAddTerminal(true);
|
|
}}
|
|
disableTouchRipple={true}
|
|
/>
|
|
</Tabs>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const removeDialog = () => {
|
|
return (
|
|
<ResponsiveDialog
|
|
open={removeTerminal}
|
|
onClose={() => {
|
|
setRemoveTerminal(false);
|
|
}}>
|
|
<DialogTitle>Delete Terminal</DialogTitle>
|
|
<DialogContent>Are you sure you wish to delete this Terminal?</DialogContent>
|
|
<DialogActions>
|
|
<Button>Cancel</Button>
|
|
<Button onClick={remove}>Confirm</Button>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
);
|
|
};
|
|
|
|
const sharingDialogs = () => {
|
|
if (!currentTerminal) return;
|
|
return (
|
|
<React.Fragment>
|
|
<ShareObject
|
|
scope={{ kind: "terminal", key: currentTerminal.key } as Scope}
|
|
label={currentTerminal.name}
|
|
permissions={permissions}
|
|
isDialogOpen={baseShareDialog}
|
|
closeDialogCallback={() => {
|
|
setBaseShareDialog(false);
|
|
}}
|
|
/>
|
|
<ObjectUsers
|
|
scope={{ kind: "terminal", key: currentTerminal.key } as Scope}
|
|
label={currentTerminal.name}
|
|
permissions={permissions}
|
|
isDialogOpen={userSharing}
|
|
closeDialogCallback={() => setUserSharing(false)}
|
|
refreshCallback={() => {}}
|
|
/>
|
|
<ObjectTeams
|
|
scope={{ kind: "terminal", key: currentTerminal.key } as Scope}
|
|
label={currentTerminal.name}
|
|
permissions={permissions}
|
|
isDialogOpen={teamSharing}
|
|
closeDialogCallback={() => setTeamSharing(false)}
|
|
refreshCallback={() => {
|
|
return true;
|
|
}}
|
|
/>
|
|
<RemoveSelfFromObject
|
|
scope={{ kind: "terminal", key: currentTerminal.key } as Scope}
|
|
label={currentTerminal.name}
|
|
isDialogOpen={removeSelfDialog}
|
|
closeDialogCallback={removed => {
|
|
if (removed) {
|
|
let a = terminals;
|
|
a.splice(terminals.indexOf(currentTerminal), 1);
|
|
setTerminals([...a]);
|
|
}
|
|
setRemoveSelfDialog(false);
|
|
}}
|
|
/>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<PageContainer>
|
|
<TerminalSettings
|
|
open={addTerminal}
|
|
closeDialog={newTerminal => {
|
|
if (newTerminal) {
|
|
let a = terminals;
|
|
a.push(newTerminal);
|
|
setTerminals([...a]);
|
|
}
|
|
setAddTerminal(false);
|
|
}}
|
|
terminal={currentTerminal}
|
|
/>
|
|
{!props.terminal && terminalTabs()}
|
|
{terminalMenu()}
|
|
{removeDialog()}
|
|
{sharingDialogs()}
|
|
{gateDisplay()}
|
|
</PageContainer>
|
|
);
|
|
}
|