fix(striker-ui): add ProvisionServerDialog to Servers panel; see body

* update min memory and virtual disk size in ProvisionServerDialog
* revise input tests in ProvisionServerDialog to match changes to input
  tests
* add close button to ProvisionServerDialog
main
Tsu-ba-me 3 years ago
parent 3ad7ae62c8
commit d8cbf63fb7
  1. 74
      striker-ui/components/ProvisionServerDialog.tsx
  2. 33
      striker-ui/components/Servers.tsx

@ -7,15 +7,17 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { Box, Dialog, DialogProps, InputAdornment } from '@mui/material'; import { Box, Dialog, DialogProps, InputAdornment } from '@mui/material';
import { Close as CloseIcon } from '@mui/icons-material';
import { DataSizeUnit } from 'format-data-size'; import { DataSizeUnit } from 'format-data-size';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { BLUE, TEXT } from '../lib/consts/DEFAULT_THEME'; import { BLUE, RED, TEXT } from '../lib/consts/DEFAULT_THEME';
import Autocomplete from './Autocomplete'; import Autocomplete from './Autocomplete';
import ConfirmDialog from './ConfirmDialog'; import ConfirmDialog from './ConfirmDialog';
import ContainedButton, { ContainedButtonProps } from './ContainedButton'; import ContainedButton, { ContainedButtonProps } from './ContainedButton';
import { dsize, dsizeToByte } from '../lib/format_data_size_wrappers'; import { dsize, dsizeToByte } from '../lib/format_data_size_wrappers';
import IconButton, { IconButtonProps } from './IconButton';
import mainAxiosInstance from '../lib/singletons/mainAxiosInstance'; import mainAxiosInstance from '../lib/singletons/mainAxiosInstance';
import MessageBox, { MessageBoxProps } from './MessageBox'; import MessageBox, { MessageBoxProps } from './MessageBox';
import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import OutlinedInputWithLabel from './OutlinedInputWithLabel';
@ -40,6 +42,7 @@ type InputMessage = Partial<Pick<MessageBoxProps, 'type' | 'text'>>;
type ProvisionServerDialogProps = { type ProvisionServerDialogProps = {
dialogProps: DialogProps; dialogProps: DialogProps;
onClose: IconButtonProps['onClick'];
}; };
type HostMetadataForProvisionServerHost = { type HostMetadataForProvisionServerHost = {
@ -201,6 +204,12 @@ const DATA_SIZE_UNIT_SELECT_ITEMS: SelectItem<DataSizeUnit>[] = [
const INITIAL_DATA_SIZE_UNIT: DataSizeUnit = 'GiB'; const INITIAL_DATA_SIZE_UNIT: DataSizeUnit = 'GiB';
const CPU_CORES_MIN = 1;
// Unit: bytes; 64 KiB
const MEMORY_MIN = BigInt(65536);
// Unit: bytes; 100 MiB
const VIRTUAL_DISK_SIZE_MIN = BigInt(104857600);
const PROVISION_BUTTON_STYLES = { const PROVISION_BUTTON_STYLES = {
backgroundColor: BLUE, backgroundColor: BLUE,
color: TEXT, color: TEXT,
@ -866,11 +875,37 @@ const addVirtualDisk = ({
const filterBlanks: (array: string[]) => string[] = (array: string[]) => const filterBlanks: (array: string[]) => string[] = (array: string[]) =>
array.filter((value) => value !== ''); array.filter((value) => value !== '');
const getDisplayDsizeOptions = (
onSuccessString: (value: string, unit: DataSizeUnit) => void,
): Parameters<typeof dsize>[1] => ({
fromUnit: 'B',
onSuccess: {
string: onSuccessString,
},
precision: 0,
toUnit: 'ibyte',
});
let displayMemoryMin: string;
let displayVirtualDiskSizeMin: string;
dsize(
MEMORY_MIN,
getDisplayDsizeOptions((value, unit) => {
displayMemoryMin = `${value} ${unit}`;
}),
);
dsize(
VIRTUAL_DISK_SIZE_MIN,
getDisplayDsizeOptions((value, unit) => {
displayVirtualDiskSizeMin = `${value} ${unit}`;
}),
);
const ProvisionServerDialog = ({ const ProvisionServerDialog = ({
dialogProps: { open }, dialogProps: { open },
onClose: onCloseProvisionServerDialog,
}: ProvisionServerDialogProps): JSX.Element => { }: ProvisionServerDialogProps): JSX.Element => {
const inputCPUCoresMin = 1;
const [allAnvils, setAllAnvils] = useState< const [allAnvils, setAllAnvils] = useState<
OrganizedAnvilDetailMetadataForProvisionServer[] OrganizedAnvilDetailMetadataForProvisionServer[]
>([]); >([]);
@ -954,8 +989,6 @@ const ProvisionServerDialog = ({
const inputTests: InputTestBatches = { const inputTests: InputTestBatches = {
serverName: { serverName: {
defaults: { defaults: {
max: 0,
min: 0,
onSuccess: () => { onSuccess: () => {
setInputServerNameMessage(undefined); setInputServerNameMessage(undefined);
}, },
@ -989,7 +1022,7 @@ const ProvisionServerDialog = ({
cpuCores: { cpuCores: {
defaults: { defaults: {
max: inputCPUCoresMax, max: inputCPUCoresMax,
min: inputCPUCoresMin, min: CPU_CORES_MIN,
onSuccess: () => { onSuccess: () => {
setInputCPUCoresMessage(undefined); setInputCPUCoresMessage(undefined);
}, },
@ -1019,9 +1052,9 @@ const ProvisionServerDialog = ({
memory: { memory: {
defaults: { defaults: {
displayMax: `${inputMemoryMax} ${inputMemoryUnit}`, displayMax: `${inputMemoryMax} ${inputMemoryUnit}`,
displayMin: '1 B', displayMin: displayMemoryMin,
max: memoryMax, max: memoryMax,
min: 1, min: MEMORY_MIN,
onSuccess: () => { onSuccess: () => {
setInputMemoryMessage(undefined); setInputMemoryMessage(undefined);
}, },
@ -1047,8 +1080,6 @@ const ProvisionServerDialog = ({
}, },
installISO: { installISO: {
defaults: { defaults: {
max: 0,
min: 0,
onSuccess: () => { onSuccess: () => {
setInputInstallISOMessage(undefined); setInputInstallISOMessage(undefined);
}, },
@ -1058,8 +1089,6 @@ const ProvisionServerDialog = ({
}, },
anvil: { anvil: {
defaults: { defaults: {
max: 0,
min: 0,
onSuccess: () => { onSuccess: () => {
setInputAnvilMessage(undefined); setInputAnvilMessage(undefined);
}, },
@ -1069,8 +1098,6 @@ const ProvisionServerDialog = ({
}, },
optimizeForOS: { optimizeForOS: {
defaults: { defaults: {
max: 0,
min: 0,
onSuccess: () => { onSuccess: () => {
setInputOptimizeForOSMessage(undefined); setInputOptimizeForOSMessage(undefined);
}, },
@ -1083,9 +1110,9 @@ const ProvisionServerDialog = ({
inputTests[`vd${vdIndex}Size`] = { inputTests[`vd${vdIndex}Size`] = {
defaults: { defaults: {
displayMax: `${virtualDisks.inputMaxes[vdIndex]} ${virtualDisks.inputUnits[vdIndex]}`, displayMax: `${virtualDisks.inputMaxes[vdIndex]} ${virtualDisks.inputUnits[vdIndex]}`,
displayMin: '1 B', displayMin: displayVirtualDiskSizeMin,
max: virtualDisks.maxes[vdIndex], max: virtualDisks.maxes[vdIndex],
min: 1, min: VIRTUAL_DISK_SIZE_MIN,
onSuccess: () => { onSuccess: () => {
virtualDisks.inputSizeMessages[vdIndex] = undefined; virtualDisks.inputSizeMessages[vdIndex] = undefined;
}, },
@ -1118,8 +1145,6 @@ const ProvisionServerDialog = ({
inputTests[`vd${vdIndex}StorageGroup`] = { inputTests[`vd${vdIndex}StorageGroup`] = {
defaults: { defaults: {
max: 0,
min: 0,
onSuccess: () => { onSuccess: () => {
virtualDisks.inputStorageGroupUUIDMessages[vdIndex] = undefined; virtualDisks.inputStorageGroupUUIDMessages[vdIndex] = undefined;
}, },
@ -1353,6 +1378,17 @@ const ProvisionServerDialog = ({
> >
<PanelHeader> <PanelHeader>
<HeaderText text="Provision a Server" /> <HeaderText text="Provision a Server" />
<IconButton
onClick={onCloseProvisionServerDialog}
sx={{
backgroundColor: RED,
color: TEXT,
'&:hover': { backgroundColor: RED },
}}
>
<CloseIcon />
</IconButton>
</PanelHeader> </PanelHeader>
<Box <Box
sx={{ sx={{
@ -1413,7 +1449,7 @@ const ProvisionServerDialog = ({
} }
}, },
max: inputCPUCoresMax, max: inputCPUCoresMax,
min: inputCPUCoresMin, min: CPU_CORES_MIN,
}, },
}, },
)} )}

@ -7,7 +7,6 @@ import {
List, List,
ListItem, ListItem,
Menu, Menu,
MenuItem,
styled, styled,
Typography, Typography,
} from '@mui/material'; } from '@mui/material';
@ -33,14 +32,15 @@ import serverState from '../lib/consts/SERVERS';
import { AnvilContext } from './AnvilContext'; import { AnvilContext } from './AnvilContext';
import Decorator, { Colours } from './Decorator'; import Decorator, { Colours } from './Decorator';
import IconButton from './IconButton'; import IconButton from './IconButton';
import MenuItem from './MenuItem';
import { Panel, PanelHeader } from './Panels'; import { Panel, PanelHeader } from './Panels';
import ProvisionServerDialog from './ProvisionServerDialog';
import Spinner from './Spinner'; import Spinner from './Spinner';
import { BodyText, HeaderText } from './Text'; import { BodyText, HeaderText } from './Text';
import hostsSanitizer from '../lib/sanitizers/hostsSanitizer'; import hostsSanitizer from '../lib/sanitizers/hostsSanitizer';
import periodicFetch from '../lib/fetchers/periodicFetch'; import periodicFetch from '../lib/fetchers/periodicFetch';
import putFetch from '../lib/fetchers/putFetch'; import putFetch from '../lib/fetchers/putFetch';
import ProvisionServerDialog from './ProvisionServerDialog';
const PREFIX = 'Servers'; const PREFIX = 'Servers';
@ -148,14 +148,6 @@ const selectDecorator = (state: string): Colours => {
} }
}; };
const ServerActionButtonMenuItem = styled(MenuItem)({
backgroundColor: GREY,
paddingRight: '3em',
'&:hover': {
backgroundColor: GREY,
},
});
const ServerActionButtonMenuItemLabel = styled(Typography)({ const ServerActionButtonMenuItemLabel = styled(Typography)({
[`&.${classes.on}`]: { [`&.${classes.on}`]: {
color: BLUE, color: BLUE,
@ -180,7 +172,8 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
const buttonLabels = useRef<ButtonLabels[]>([]); const buttonLabels = useRef<ButtonLabels[]>([]);
const { data, isLoading } = periodicFetch<AnvilServers>( const { data: { servers = [] } = {}, isLoading } =
periodicFetch<AnvilServers>(
`${process.env.NEXT_PUBLIC_API_URL}/get_servers?anvil_uuid=${uuid}`, `${process.env.NEXT_PUBLIC_API_URL}/get_servers?anvil_uuid=${uuid}`,
); );
@ -221,7 +214,7 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
if (index === -1) selected.push(server_uuid); if (index === -1) selected.push(server_uuid);
else selected.splice(index, 1); else selected.splice(index, 1);
const filtered = data.servers.filter( const filtered = servers.filter(
(server: AnvilServer) => selected.indexOf(server.server_uuid) !== -1, (server: AnvilServer) => selected.indexOf(server.server_uuid) !== -1,
); );
setButtons(filtered); setButtons(filtered);
@ -269,17 +262,14 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
onClose={() => setAnchorEl(null)} onClose={() => setAnchorEl(null)}
> >
{buttonLabels.current.map((label: ButtonLabels) => ( {buttonLabels.current.map((label: ButtonLabels) => (
<ServerActionButtonMenuItem <MenuItem onClick={() => handlePower(label)} key={label}>
onClick={() => handlePower(label)}
key={label}
>
<ServerActionButtonMenuItemLabel <ServerActionButtonMenuItemLabel
className={classes[label]} className={classes[label]}
variant="subtitle1" variant="subtitle1"
> >
{label.replace(/^[a-z]/, (c) => c.toUpperCase())} {label.replace(/^[a-z]/, (c) => c.toUpperCase())}
</ServerActionButtonMenuItemLabel> </ServerActionButtonMenuItemLabel>
</ServerActionButtonMenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
</Box> </Box>
@ -292,9 +282,9 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
checked={allSelected} checked={allSelected}
onChange={() => { onChange={() => {
if (!allSelected) { if (!allSelected) {
setButtons(data.servers); setButtons(servers);
setSelected( setSelected(
data.servers.map( servers.map(
(server: AnvilServer) => server.server_uuid, (server: AnvilServer) => server.server_uuid,
), ),
); );
@ -316,7 +306,7 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
{!isLoading ? ( {!isLoading ? (
<Box className={classes.root}> <Box className={classes.root}>
<List component="nav"> <List component="nav">
{data?.servers.map((server: AnvilServer) => ( {servers.map((server: AnvilServer) => (
<> <>
<ListItem <ListItem
button button
@ -400,6 +390,9 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
</Panel> </Panel>
<ProvisionServerDialog <ProvisionServerDialog
dialogProps={{ open: isOpenProvisionServerDialog }} dialogProps={{ open: isOpenProvisionServerDialog }}
onClose={() => {
setIsOpenProvisionServerDialog(false);
}}
/> />
</> </>
); );

Loading…
Cancel
Save