imported the gate and terminal related files to prep for the aviation map

This commit is contained in:
csawatzky 2025-03-28 15:54:11 -06:00
parent e2e061151b
commit 505cb8e3aa
25 changed files with 4868 additions and 9 deletions

509
src/pages/Terminals.tsx Normal file
View file

@ -0,0 +1,509 @@
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 GateSettings from "gate/GateSettings";
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 { Gate } from "models/Gate";
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 AddGateFab from "gate/AddGateFab";
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 [loading, setLoading] = useState(false);
const terminalAPI = useTerminalAPI();
const [value, setValue] = useState(0);
const [terminals, setTerminals] = useState<Terminal[]>([]);
const [gates, setGates] = useState<Gate[]>([]);
// map using the terminal key and the gates for that terminal
const [gateMap, setGateMap] = useState<Map<string, Gate[]>>(new Map<string, Gate[]>());
const [displayGates, setDisplayGates] = useState<Gate[]>([]);
const [gateDialog, setGateDialog] = useState(false);
const classes = useStyles();
const theme = useTheme();
const [addTerminal, setAddTerminal] = useState(false);
const [tabClick, setTabClick] = useState(true);
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);
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);
setDisplayGates(gateMap.get(props.terminal.key) ?? []);
}
}, [props.terminal, gateMap]);
const load = useCallback(() => {
console.log(loading)
if (loading) return;
console.log("terminal load")
setLoading(true);
terminalAPI
.listTerminalsAndGates(200, 0)
.then(resp => {
console.log(resp)
setTerminals(resp.data.terminals.map(a => Terminal.any(a)));
let allGates = resp.data.gates.map(t => Gate.any(t));
setGates(allGates);
let tMap: Map<string, Gate[]> = new Map<string, Gate[]>();
allGates.forEach(gate => {
let mapEntry = tMap.get(gate.terminal());
if (mapEntry) {
mapEntry.push(gate);
} else {
tMap.set(gate.terminal(), [gate]);
}
setGateMap(tMap);
setDisplayGates(allGates);
});
})
.catch(err => {
openSnack("There was a problem loading your terminals");
})
.finally(() => {
setLoading(false);
});
}, [terminalAPI, openSnack]); //eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
console.log("load use")
load();
}, [load]);
const remove = () => {
if (currentTerminal) {
terminalAPI
.removeTerminal(currentTerminal.key)
.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) => {
if (tabClick && newValue <= terminals.length) {
setValue(newValue);
if (terminals[newValue - 1]) {
setDisplayGates(gateMap.get(terminals[newValue - 1].key) ?? []);
} else {
setDisplayGates(gates);
}
}
};
const gateDisplay = () => {
return (
<GateList
gates={displayGates}
terminals={terminals}
useMobile={props.terminal !== undefined}
/>
);
};
//could update the tabs in the same way as the bin yard tabs
const terminalTabs = () => {
return (
<React.Fragment>
<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
onMouseEnter={() => setTabClick(false)}
onMouseLeave={() => setTabClick(true)}
className={classes.icon}
onClick={event => {
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>
<GateSettings
open={gateDialog}
close={newGate => {
if (newGate) {
if (newGate.terminal() !== "") {
let tMap = gateMap;
tMap.get(newGate.terminal())?.push(newGate);
}
let t = gates;
t.push(newGate);
setGates([...t]);
}
setGateDialog(false);
}}
terminals={terminals}
/>
<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()}
<AddGateFab
onClick={() => {
setGateDialog(true);
}}
pulse={gates.length < 1}
/>
</PageContainer>
);
}