diff --git a/striker-ui/components/ProvisionServerDialog.tsx b/striker-ui/components/ProvisionServerDialog.tsx index 1b504bee..7dea84a4 100644 --- a/striker-ui/components/ProvisionServerDialog.tsx +++ b/striker-ui/components/ProvisionServerDialog.tsx @@ -8,6 +8,7 @@ import { } from 'react'; import { Box, Dialog, DialogProps, InputAdornment } from '@mui/material'; import { DataSizeUnit } from 'format-data-size'; +import { v4 as uuidv4 } from 'uuid'; import Autocomplete from './Autocomplete'; import ContainedButton, { ContainedButtonProps } from './ContainedButton'; @@ -29,6 +30,7 @@ import { } from '../types/TestInputFunction'; import { BodyText, HeaderText } from './Text'; import OutlinedLabeledInputWithSelect from './OutlinedLabeledInputWithSelect'; +import ConfirmDialog from './ConfirmDialog'; type InputMessage = Partial>; @@ -119,6 +121,10 @@ type AnvilUUIDMapToData = { [uuid: string]: OrganizedAnvilDetailMetadataForProvisionServer; }; +type FileUUIDMapToData = { + [uuid: string]: FileMetadataForProvisionServer; +}; + type StorageGroupUUIDMapToData = { [uuid: string]: OrganizedStorageGroupMetadataForProvisionServer; }; @@ -149,13 +155,14 @@ type FilterAnvilsFunction = ( }; type VirtualDiskStates = { - maxes: bigint[]; + stateIds: string[]; inputMaxes: string[]; inputSizeMessages: Array; inputSizes: string[]; inputStorageGroupUUIDMessages: Array; inputStorageGroupUUIDs: string[]; inputUnits: DataSizeUnit[]; + maxes: bigint[]; sizes: bigint[]; }; @@ -428,13 +435,14 @@ const createSelectItemDisplay = ({ ); const organizeAnvils = (data: AnvilDetailMetadataForProvisionServer[]) => { - const anvilFiles: Record = {}; + const allFiles: Record = {}; const result = data.reduce<{ anvils: OrganizedAnvilDetailMetadataForProvisionServer[]; anvilSelectItems: SelectItem[]; anvilUUIDMapToData: AnvilUUIDMapToData; files: FileMetadataForProvisionServer[]; fileSelectItems: SelectItem[]; + fileUUIDMapToData: FileUUIDMapToData; storageGroups: OrganizedStorageGroupMetadataForProvisionServer[]; storageGroupSelectItems: SelectItem[]; storageGroupUUIDMapToData: StorageGroupUUIDMapToData; @@ -516,7 +524,7 @@ const organizeAnvils = (data: AnvilDetailMetadataForProvisionServer[]) => { fileUUIDs.push(fileUUID); - anvilFiles[fileUUID] = file; + allFiles[fileUUID] = file; }); const resultAnvil = { @@ -581,18 +589,20 @@ const organizeAnvils = (data: AnvilDetailMetadataForProvisionServer[]) => { anvilUUIDMapToData: {}, files: [], fileSelectItems: [], + fileUUIDMapToData: {}, storageGroups: [], storageGroupSelectItems: [], storageGroupUUIDMapToData: {}, }, ); - Object.values(anvilFiles).forEach((distinctFile) => { + Object.values(allFiles).forEach((distinctFile) => { result.files.push(distinctFile); result.fileSelectItems.push({ displayValue: distinctFile.fileName, value: distinctFile.fileUUID, }); + result.fileUUIDMapToData[distinctFile.fileUUID] = distinctFile; }); return result; @@ -863,7 +873,7 @@ const createVirtualDiskForm = ( return ( >; size?: bigint; } = {}) => { const { - maxes, + stateIds, inputMaxes, inputSizeMessages, inputSizes, inputStorageGroupUUIDMessages, inputStorageGroupUUIDs, inputUnits, + maxes, sizes, } = virtualDisks; - maxes.push(max); + stateIds.push(stateId); inputMaxes.push(inputMax); inputSizeMessages.push(inputSizeMessage); inputSizes.push(inputSize); inputStorageGroupUUIDMessages.push(inputStorageGroupUUIDMessage); inputStorageGroupUUIDs.push(inputStorageGroupUUID); inputUnits.push(inputUnit); + maxes.push(max); sizes.push(size); setVirtualDisks?.call(null, { ...virtualDisks }); @@ -1020,6 +1035,9 @@ const ProvisionServerDialog = ({ >([]); const [anvilUUIDMapToData, setAnvilUUIDMapToData] = useState({}); + const [fileUUIDMapToData, setFileUUIDMapToData] = useState( + {}, + ); const [storageGroupUUIDMapToData, setStorageGroupUUIDMapToData] = useState({}); @@ -1084,6 +1102,9 @@ const ProvisionServerDialog = ({ string[] >([]); + const [isOpenProvisionConfirmDialog, setIsOpenProvisionConfirmDialog] = + useState(false); + const inputTests: InputTestBatches = { serverName: { defaults: { @@ -1433,12 +1454,14 @@ const ProvisionServerDialog = ({ anvilSelectItems: ueAnvilSelectItems, anvilUUIDMapToData: ueAnvilUUIDMapToData, fileSelectItems: ueFileSelectItems, + fileUUIDMapToData: ueFileUUIDMapToData, storageGroupSelectItems: ueStorageGroupSelectItems, storageGroupUUIDMapToData: ueStorageGroupUUIDMapToData, } = organizeAnvils(data.anvils); setAllAnvils(ueAllAnvils); setAnvilUUIDMapToData(ueAnvilUUIDMapToData); + setFileUUIDMapToData(ueFileUUIDMapToData); setStorageGroupUUIDMapToData(ueStorageGroupUUIDMapToData); setAnvilSelectItems(ueAnvilSelectItems); @@ -1463,216 +1486,295 @@ const ProvisionServerDialog = ({ }, [initLimits]); return ( - - - - - :not(:first-child)': { - marginTop: '1em', - }, + <> + - - { - setInputServerNameValue(value); + + + + :not(:first-child)': { + marginTop: '1em', + }, + }} + > + + { + setInputServerNameValue(value); + + testInput({ inputs: { serverName: { value } } }); + }, + value: inputServerNameValue, + }} + inputLabelProps={{ + isNotifyRequired: inputServerNameValue.length === 0, + }} + messageBoxProps={inputServerNameMessage} + /> + + {createOutlinedSlider( + 'ps-cpu-cores', + 'CPU cores', + inputCPUCoresValue, + { + messageBoxProps: inputCPUCoresMessage, + sliderProps: { + onChange: (value) => { + const newCPUCoresValue = value as number; + + if (newCPUCoresValue !== inputCPUCoresValue) { + setInputCPUCoresValue(newCPUCoresValue); + + const { maxCPUCores: newCPUCoresMax } = updateLimits({ + cpuCores: newCPUCoresValue, + }); + + testInput({ + inputs: { + cpuCores: { + max: newCPUCoresMax, + value: newCPUCoresValue, + }, + }, + }); + } + }, + max: inputCPUCoresMax, + min: inputCPUCoresMin, + }, + }, + )} + { + setInputMemoryValue(inputMemoryMax); + changeMemory({ cmValue: memoryMax }); + }, + }, + ), + onChange: ({ target: { value } }) => { + handleInputMemoryValueChange({ value }); + }, + type: 'number', + value: inputMemoryValue, + }, + inputLabelProps: { + isNotifyRequired: memory === BIGINT_ZERO, + }, + }} + selectItems={DATA_SIZE_UNIT_SELECT_ITEMS} + selectWithLabelProps={{ + selectProps: { + onChange: ({ target: { value } }) => { + const selectedUnit = value as DataSizeUnit; - testInput({ inputs: { serverName: { value } } }); + handleInputMemoryValueChange({ unit: selectedUnit }); + }, + value: inputMemoryUnit, }, - value: inputServerNameValue, }} + /> + {virtualDisks.stateIds.map((vdStateId, vdIndex) => + createVirtualDiskForm( + virtualDisks, + vdIndex, + setVirtualDisks, + storageGroupSelectItems, + includeStorageGroupUUIDs, + updateLimits, + storageGroupUUIDMapToData, + testInput, + ), + )} + value === inputDriverISOFileUUID} + hideItem={(value) => !includeFileUUIDs.includes(value)} + id="ps-install-image" inputLabelProps={{ - isNotifyRequired: inputServerNameValue.length === 0, + isNotifyRequired: inputInstallISOFileUUID.length === 0, }} - messageBoxProps={inputServerNameMessage} - /> - - {createOutlinedSlider('ps-cpu-cores', 'CPU cores', inputCPUCoresValue, { - messageBoxProps: inputCPUCoresMessage, - sliderProps: { - onChange: (value) => { - const newCPUCoresValue = value as number; - - if (newCPUCoresValue !== inputCPUCoresValue) { - setInputCPUCoresValue(newCPUCoresValue); - - const { maxCPUCores: newCPUCoresMax } = updateLimits({ - cpuCores: newCPUCoresValue, - }); - - testInput({ - inputs: { - cpuCores: { max: newCPUCoresMax, value: newCPUCoresValue }, - }, - }); - } - }, - max: inputCPUCoresMax, - min: inputCPUCoresMin, - }, - })} - { - setInputMemoryValue(inputMemoryMax); - changeMemory({ cmValue: memoryMax }); - }, - }, - ), + label="Install ISO" + messageBoxProps={inputInstallISOMessage} + selectItems={fileSelectItems} + selectProps={{ onChange: ({ target: { value } }) => { - handleInputMemoryValueChange({ value }); + const newInstallISOFileUUID = value as string; + + handleInputInstallISOFileUUIDChange(newInstallISOFileUUID); }, - type: 'number', - value: inputMemoryValue, - }, - inputLabelProps: { - isNotifyRequired: memory === BIGINT_ZERO, - }, - }} - selectItems={DATA_SIZE_UNIT_SELECT_ITEMS} - selectWithLabelProps={{ - selectProps: { + onClearIndicatorClick: () => + handleInputInstallISOFileUUIDChange(''), + value: inputInstallISOFileUUID, + }} + /> + value === inputInstallISOFileUUID} + hideItem={(value) => !includeFileUUIDs.includes(value)} + id="ps-driver-image" + label="Driver ISO" + messageBoxProps={inputDriverISOMessage} + selectItems={fileSelectItems} + selectProps={{ onChange: ({ target: { value } }) => { - const selectedUnit = value as DataSizeUnit; + const newDriverISOFileUUID = value as string; - handleInputMemoryValueChange({ unit: selectedUnit }); + handleInputDriverISOFileUUIDChange(newDriverISOFileUUID); }, - value: inputMemoryUnit, - }, - }} - /> - {virtualDisks.maxes.map((max, vdIndex) => - createVirtualDiskForm( - virtualDisks, - vdIndex, - setVirtualDisks, - storageGroupSelectItems, - includeStorageGroupUUIDs, - updateLimits, - storageGroupUUIDMapToData, - testInput, - ), - )} - value === inputDriverISOFileUUID} - hideItem={(value) => !includeFileUUIDs.includes(value)} - id="ps-install-image" - inputLabelProps={{ - isNotifyRequired: inputInstallISOFileUUID.length === 0, - }} - label="Install ISO" - messageBoxProps={inputInstallISOMessage} - selectItems={fileSelectItems} - selectProps={{ - onChange: ({ target: { value } }) => { - const newInstallISOFileUUID = value as string; - - handleInputInstallISOFileUUIDChange(newInstallISOFileUUID); - }, - onClearIndicatorClick: () => - handleInputInstallISOFileUUIDChange(''), - value: inputInstallISOFileUUID, - }} - /> - value === inputInstallISOFileUUID} - hideItem={(value) => !includeFileUUIDs.includes(value)} - id="ps-driver-image" - label="Driver ISO" - messageBoxProps={inputDriverISOMessage} - selectItems={fileSelectItems} - selectProps={{ - onChange: ({ target: { value } }) => { - const newDriverISOFileUUID = value as string; - - handleInputDriverISOFileUUIDChange(newDriverISOFileUUID); - }, - onClearIndicatorClick: () => handleInputDriverISOFileUUIDChange(''), - value: inputDriverISOFileUUID, - }} - /> - !includeAnvilUUIDs.includes(value)} - id="ps-anvil" - inputLabelProps={{ - isNotifyRequired: inputAnvilValue.length === 0, - }} - label="Anvil node pair" - messageBoxProps={inputAnvilMessage} - selectItems={anvilSelectItems} - selectProps={{ - onChange: ({ target: { value } }) => { - const newAnvilUUID: string = value as string; + onClearIndicatorClick: () => + handleInputDriverISOFileUUIDChange(''), + value: inputDriverISOFileUUID, + }} + /> + !includeAnvilUUIDs.includes(value)} + id="ps-anvil" + inputLabelProps={{ + isNotifyRequired: inputAnvilValue.length === 0, + }} + label="Anvil node pair" + messageBoxProps={inputAnvilMessage} + selectItems={anvilSelectItems} + selectProps={{ + onChange: ({ target: { value } }) => { + const newAnvilUUID: string = value as string; - handleInputAnvilValueChange(newAnvilUUID); - }, - onClearIndicatorClick: () => handleInputAnvilValueChange(''), - renderValue: (value) => { - const { anvilName: rvAnvilName = `Unknown ${value}` } = - anvilUUIDMapToData[value as string] ?? {}; + handleInputAnvilValueChange(newAnvilUUID); + }, + onClearIndicatorClick: () => handleInputAnvilValueChange(''), + renderValue: (value) => { + const { anvilName: rvAnvilName = `Unknown ${value}` } = + anvilUUIDMapToData[value as string] ?? {}; - return rvAnvilName; - }, - value: inputAnvilValue, + return rvAnvilName; + }, + value: inputAnvilValue, + }} + /> + { + inputLabelProps.isNotifyRequired = + inputOptimizeForOSValue === null; + }} + isOptionEqualToValue={(option, value) => option.key === value.key} + label="Optimize for OS" + messageBoxProps={inputOptimizeForOSMessage} + noOptionsText="No matching OS" + onChange={(event, value) => { + setInputOptimizeForOSValue(value); + }} + openOnFocus + options={osAutocompleteOptions} + value={inputOptimizeForOSValue} + /> + + - { - inputLabelProps.isNotifyRequired = inputOptimizeForOSValue === null; + > + { + setIsOpenProvisionConfirmDialog(true); + }} + > + Provision + + + + {isOpenProvisionConfirmDialog && ( + + + + + {virtualDisks.stateIds.map((vdStateId, vdIndex) => ( + + ))} + + + + + } + dialogProps={{ open: isOpenProvisionConfirmDialog }} + onCancel={() => { + setIsOpenProvisionConfirmDialog(false); }} - label="Optimize for OS" - messageBoxProps={inputOptimizeForOSMessage} - noOptionsText="No matching OS" - onChange={(event, value) => { - setInputOptimizeForOSValue(value); + onProceed={() => { + // const requestBody = { + // serverName: inputServerNameValue, + // cpuCores: inputCPUCoresValue, + // memory, + // virtualDisks: virtualDisks.stateIds.map((vdStateId, vdIndex) => ({ + // size: virtualDisks.sizes[vdIndex], + // storageGroupUUID: virtualDisks.inputStorageGroupUUIDs[vdIndex], + // })), + // installISOFileUUID: inputInstallISOFileUUID, + // driverISOFileUUID: inputDriverISOFileUUID, + // anvilUUID: inputAnvilValue, + // optimizeForOS: inputOptimizeForOSValue?.key, + // }; + + setIsOpenProvisionConfirmDialog(false); }} - openOnFocus - options={osAutocompleteOptions} - value={inputOptimizeForOSValue} + titleText={`Provision ${inputServerNameValue}?`} /> - - - - Provision - - - + )} + ); };