import { useState, useContext, useRef } from 'react'; import { Box, Button, Checkbox, Divider, List, ListItem, Menu, MenuItem, styled, Typography, } from '@mui/material'; import { Add as AddIcon, Check as CheckIcon, Edit as EditIcon, MoreVert as MoreVertIcon, } from '@mui/icons-material'; import { BLACK, BLUE, DIVIDER, GREY, HOVER, LARGE_MOBILE_BREAKPOINT, RED, TEXT, } from '../lib/consts/DEFAULT_THEME'; import serverState from '../lib/consts/SERVERS'; import { AnvilContext } from './AnvilContext'; import Decorator, { Colours } from './Decorator'; import IconButton from './IconButton'; import { Panel, PanelHeader } from './Panels'; import Spinner from './Spinner'; import { BodyText, HeaderText } from './Text'; import hostsSanitizer from '../lib/sanitizers/hostsSanitizer'; import periodicFetch from '../lib/fetchers/periodicFetch'; import putFetch from '../lib/fetchers/putFetch'; import ProvisionServerDialog from './ProvisionServerDialog'; const PREFIX = 'Servers'; const classes = { root: `${PREFIX}-root`, divider: `${PREFIX}-divider`, verticalDivider: `${PREFIX}-verticalDivider`, button: `${PREFIX}-button`, headerPadding: `${PREFIX}-headerPadding`, hostsBox: `${PREFIX}-hostsBox`, hostBox: `${PREFIX}-hostBox`, checkbox: `${PREFIX}-checkbox`, serverActionButton: `${PREFIX}-serverActionButton`, editButtonBox: `${PREFIX}-editButtonBox`, dropdown: `${PREFIX}-dropdown`, power: `${PREFIX}-power`, on: `${PREFIX}-on`, off: `${PREFIX}-off`, all: `${PREFIX}-all`, }; const StyledDiv = styled('div')(({ theme }) => ({ [`& .${classes.root}`]: { width: '100%', overflow: 'auto', height: '78vh', paddingRight: '.3em', [theme.breakpoints.down(LARGE_MOBILE_BREAKPOINT)]: { height: '100%', overflow: 'hidden', }, }, [`& .${classes.divider}`]: { backgroundColor: DIVIDER, }, [`& .${classes.verticalDivider}`]: { height: '75%', paddingTop: '1em', }, [`& .${classes.button}`]: { '&:hover': { backgroundColor: HOVER, }, paddingLeft: 0, }, [`& .${classes.headerPadding}`]: { paddingLeft: '.3em', }, [`& .${classes.hostsBox}`]: { padding: '1em', paddingRight: 0, }, [`& .${classes.hostBox}`]: { paddingTop: 0, }, [`& .${classes.checkbox}`]: { paddingTop: '.8em', }, [`& .${classes.serverActionButton}`]: { backgroundColor: TEXT, color: BLACK, textTransform: 'none', '&:hover': { backgroundColor: GREY, }, }, [`& .${classes.editButtonBox}`]: { paddingTop: '.3em', }, [`& .${classes.dropdown}`]: { paddingTop: '.8em', paddingBottom: '.8em', }, [`& .${classes.power}`]: { color: BLACK, }, [`& .${classes.all}`]: { paddingTop: '.5em', paddingLeft: '.3em', }, })); const selectDecorator = (state: string): Colours => { switch (state) { case 'running': return 'ok'; case 'shut off': return 'off'; case 'crashed': return 'error'; default: return 'warning'; } }; const ServerActionButtonMenuItem = styled(MenuItem)({ backgroundColor: GREY, paddingRight: '3em', '&:hover': { backgroundColor: GREY, }, }); const ServerActionButtonMenuItemLabel = styled(Typography)({ [`&.${classes.on}`]: { color: BLUE, }, [`&.${classes.off}`]: { color: RED, }, }); type ButtonLabels = 'on' | 'off'; const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => { const [anchorEl, setAnchorEl] = useState(null); const [showCheckbox, setShowCheckbox] = useState(false); const [allSelected, setAllSelected] = useState(false); const [selected, setSelected] = useState([]); const [isOpenProvisionServerDialog, setIsOpenProvisionServerDialog] = useState(false); const { uuid } = useContext(AnvilContext); const buttonLabels = useRef([]); const { data, isLoading } = periodicFetch( `${process.env.NEXT_PUBLIC_API_URL}/get_servers?anvil_uuid=${uuid}`, ); const setButtons = (filtered: AnvilServer[]) => { buttonLabels.current = []; if ( filtered.filter((item: AnvilServer) => item.server_state === 'running') .length ) { buttonLabels.current.push('off'); } if ( filtered.filter((item: AnvilServer) => item.server_state === 'shut off') .length ) { buttonLabels.current.push('on'); } }; const handleClick = (event: React.MouseEvent): void => { setAnchorEl(event.currentTarget); }; const handlePower = (label: ButtonLabels) => { setAnchorEl(null); if (selected.length) { putFetch(`${process.env.NEXT_PUBLIC_API_URL}/set_power`, { server_uuid_list: selected, is_on: label === 'on', }); } }; const handleChange = (server_uuid: string): void => { const index = selected.indexOf(server_uuid); if (index === -1) selected.push(server_uuid); else selected.splice(index, 1); const filtered = data.servers.filter( (server: AnvilServer) => selected.indexOf(server.server_uuid) !== -1, ); setButtons(filtered); setSelected([...selected]); }; const anvilIndex = anvil.findIndex((a) => a.anvil_uuid === uuid); const filteredHosts = hostsSanitizer(anvil[anvilIndex]?.hosts); return ( <> setIsOpenProvisionServerDialog(true)}> setShowCheckbox(!showCheckbox)}> {showCheckbox ? : } {showCheckbox && ( <> setAnchorEl(null)} > {buttonLabels.current.map((label: ButtonLabels) => ( handlePower(label)} key={label} > {label.replace(/^[a-z]/, (c) => c.toUpperCase())} ))} { if (!allSelected) { setButtons(data.servers); setSelected( data.servers.map( (server: AnvilServer) => server.server_uuid, ), ); } else { setButtons([]); setSelected([]); } setAllSelected(!allSelected); }} /> )} {!isLoading ? ( {data?.servers.map((server: AnvilServer) => ( <> handleChange(server.server_uuid)} > {showCheckbox && ( s === server.server_uuid, ) !== undefined } /> )} {server.server_state !== 'shut off' && server.server_state !== 'crashed' && filteredHosts.map( ( host: AnvilStatusHost, index: number, ): JSX.Element => ( <> {index !== filteredHosts.length - 1 && ( )} ), )} ))} ) : ( )} ); }; export default Servers;