diff --git a/striker-ui/components/PrepareNetworkForm.tsx b/striker-ui/components/PrepareNetworkForm.tsx index 0ed10ea1..7bf67563 100644 --- a/striker-ui/components/PrepareNetworkForm.tsx +++ b/striker-ui/components/PrepareNetworkForm.tsx @@ -1,20 +1,93 @@ import { useRouter } from 'next/router'; -import { FC, useCallback, useEffect, useMemo } from 'react'; +import { FC, useCallback, useEffect, useMemo, useRef } from 'react'; import api from '../lib/api'; +import ConfirmDialog from './ConfirmDialog'; import ContainedButton from './ContainedButton'; -import handleAPIError from '../lib/handleAPIError'; import FlexBox from './FlexBox'; +import FormSummary from './FormSummary'; import getQueryParam from '../lib/getQueryParam'; +import handleAPIError from '../lib/handleAPIError'; import InputWithRef from './InputWithRef'; import MessageBox, { Message } from './MessageBox'; -import NetworkInitForm from './NetworkInitForm'; +import MessageGroup, { MessageGroupForwardedRefContent } from './MessageGroup'; +import NetworkInitForm, { + NetworkInitFormForwardedRefContent, +} from './NetworkInitForm'; import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import { Panel, PanelHeader } from './Panels'; import Spinner from './Spinner'; +import { buildPeacefulStringTestBatch } from '../lib/test_input'; import { HeaderText } from './Text'; +import useConfirmDialogProps from '../hooks/useConfirmDialogProps'; +import useFormUtils from '../hooks/useFormUtils'; import useProtectedState from '../hooks/useProtectedState'; +const INPUT_ID_PREP_NET_HOST_NAME = 'prepare-network-host-name-input'; + +const INPUT_GROUP_ID_PREP_NET_NETCONF = 'prepare-network-netconf-input-group'; + +const INPUT_LABEL_PREP_NET_HOST_NAME = 'Host name'; + +const getFormData = ( + { + netconf, + }: { + netconf: NetworkInitFormForwardedRefContent; + }, + ...[{ target }]: DivFormEventHandlerParameters +) => { + const { elements } = target as HTMLFormElement; + + const { value: hostName } = elements.namedItem( + INPUT_ID_PREP_NET_HOST_NAME, + ) as HTMLInputElement; + + const data = { + hostName, + ...netconf.get?.call(null), + }; + + return data; +}; + +const toFormEntries = (body: ReturnType): FormEntries => { + const { networks: nets = [], ...rest } = body; + + const networks = nets.reduce((previous, network) => { + const { + interfaces: ifaces, + ipAddress, + name = '', + type, + typeCount, + subnetMask, + } = network; + const networkId = `${type}${typeCount}`; + + const interfaces = ifaces.reduce((pIfaces, iface, index) => { + if (iface) { + const { networkInterfaceName } = iface; + const linkNumber = index + 1; + + pIfaces[`link${linkNumber}`] = networkInterfaceName; + } + + return pIfaces; + }, {}); + + previous[networkId] = { + name, + network: `${ipAddress}/${subnetMask}`, + ...interfaces, + }; + + return previous; + }, {}); + + return { ...rest, networks }; +}; + const PrepareNetworkForm: FC = ({ expectUUID: isExpectExternalHostUUID = false, hostUUID, @@ -24,6 +97,15 @@ const PrepareNetworkForm: FC = ({ query: { host_uuid: queryHostUUID }, } = useRouter(); + const confirmDialogRef = useRef({}); + const netconfFormRef = useRef({}); + + const generalInputMessageGroupRef = useRef( + {}, + ); + + const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps(); + const [hostDetail, setHostDetail] = useProtectedState< APIHostDetail | undefined >(undefined); @@ -35,6 +117,17 @@ const PrepareNetworkForm: FC = ({ const [previousHostUUID, setPreviousHostUUID] = useProtectedState(undefined); + const { + buildFinishInputTestBatchFunction, + buildInputFirstRenderFunction, + isFormInvalid, + setMessage, + setValidity, + } = useFormUtils( + [INPUT_ID_PREP_NET_HOST_NAME, INPUT_GROUP_ID_PREP_NET_NETCONF], + generalInputMessageGroupRef, + ); + const isDifferentHostUUID = useMemo( () => hostUUID !== previousHostUUID, [hostUUID, previousHostUUID], @@ -52,6 +145,30 @@ const PrepareNetworkForm: FC = ({ ), [hostDetail], ); + const netconfForm = useMemo( + () => ( + { + setValidity(INPUT_GROUP_ID_PREP_NET_NETCONF, valid); + }} + /> + ), + [hostDetail, setValidity], + ); + const generalInputMessageArea = useMemo( + () => ( + + ), + [], + ); + const contentElement = useMemo(() => { let result; @@ -63,21 +180,72 @@ const PrepareNetworkForm: FC = ({ result = ( <> {panelHeaderElement} - + { + const [event] = args; + + event.preventDefault(); + + const body = getFormData( + { netconf: netconfFormRef.current }, + ...args, + ); + + setConfirmDialogProps({ + actionProceedText: 'Prepare', + content: ( + + /^(dns|[a-z]+n\d+)/.test(key) + ? key.toUpperCase() + : cap(key) + } + /> + ), + titleText: `Prepare ${hostDetail?.shortHostName} network?`, + }); + + confirmDialogRef.current.setOpen?.call(null, true); + }} + > } + inputTestBatch={buildPeacefulStringTestBatch( + INPUT_LABEL_PREP_NET_HOST_NAME, + () => { + setMessage(INPUT_ID_PREP_NET_HOST_NAME); + }, + { + onFinishBatch: buildFinishInputTestBatchFunction( + INPUT_ID_PREP_NET_HOST_NAME, + ), + }, + (message) => { + setMessage(INPUT_ID_PREP_NET_HOST_NAME, { + children: message, + }); + }, + )} + onFirstRender={buildInputFirstRenderFunction( + INPUT_ID_PREP_NET_HOST_NAME, + )} required /> - + {generalInputMessageArea} + {netconfForm} - Prepare network + + Prepare network + @@ -85,7 +253,20 @@ const PrepareNetworkForm: FC = ({ } return result; - }, [hostDetail, fatalErrorMessage, isLoadingHostDetail, panelHeaderElement]); + }, [ + isLoadingHostDetail, + fatalErrorMessage, + panelHeaderElement, + hostDetail?.hostName, + hostDetail?.shortHostName, + buildFinishInputTestBatchFunction, + buildInputFirstRenderFunction, + generalInputMessageArea, + netconfForm, + isFormInvalid, + setConfirmDialogProps, + setMessage, + ]); const getHostDetail = useCallback( (uuid: string) => { @@ -150,7 +331,17 @@ const PrepareNetworkForm: FC = ({ isReloadHostDetail, ]); - return {contentElement}; + return ( + <> + {contentElement} + + + ); }; export default PrepareNetworkForm;