From adf01f9bddb75e507d4182c481e8ee2aa52ed2bc Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Thu, 16 Mar 2023 16:41:45 -0400 Subject: [PATCH] fix(striker-ui): complete edit/remove network(s) in manifest network config --- .../AnvilNetworkConfigInputGroup.tsx | 131 ++++++++++++++---- .../ManageManifest/AnvilNetworkInputGroup.tsx | 27 +++- striker-ui/types/ManageManifest.d.ts | 9 ++ striker-ui/types/Select.d.ts | 5 + 4 files changed, 144 insertions(+), 28 deletions(-) diff --git a/striker-ui/components/ManageManifest/AnvilNetworkConfigInputGroup.tsx b/striker-ui/components/ManageManifest/AnvilNetworkConfigInputGroup.tsx index a2a93f06..896a26de 100644 --- a/striker-ui/components/ManageManifest/AnvilNetworkConfigInputGroup.tsx +++ b/striker-ui/components/ManageManifest/AnvilNetworkConfigInputGroup.tsx @@ -1,6 +1,8 @@ import { ReactElement, useCallback, useMemo, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; +import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES'; + import AnvilNetworkInputGroup from './AnvilNetworkInputGroup'; import buildObjectStateSetterCallback from '../../lib/buildObjectStateSetterCallback'; import Grid from '../Grid'; @@ -21,6 +23,8 @@ const INPUT_LABEL_ANVIL_NETWORK_CONFIG_DNS = 'DNS'; const INPUT_LABEL_ANVIL_NETWORK_CONFIG_MTU = 'MTU'; const INPUT_LABEL_ANVIL_NETWORK_CONFIG_NTP = 'NTP'; +const NETWORK_TYPE_ENTRIES = Object.entries(NETWORK_TYPES); + const DEFAULT_NETWORKS: AnvilNetworkConfigNetworkList = { bcn1: { networkMinIp: '', @@ -42,7 +46,8 @@ const DEFAULT_NETWORKS: AnvilNetworkConfigNetworkList = { }, }; -const isIfn = (type: string) => type === 'ifn'; +const assertIfn = (type: string) => type === 'ifn'; +const assertMn = (type: string) => type === 'mn'; const AnvilNetworkConfigInputGroup = < M extends MapToInputTestID & { @@ -69,7 +74,7 @@ const AnvilNetworkConfigInputGroup = < const [networkList, setNetworkList] = useState(previousNetworks); - const networkListArray = useMemo( + const networkListEntries = useMemo( () => Object.entries(networkList), [networkList], ); @@ -78,13 +83,15 @@ const AnvilNetworkConfigInputGroup = < ( type: string, { - input = networkListArray, - end = networkListArray.length, + input = networkListEntries, + end = networkListEntries.length, }: { input?: Array<[string, AnvilNetworkConfigNetwork]>; end?: number; } = {}, ) => { + const limit = end - 1; + let netNum = 0; input.every(([, { networkType }], networkIndex) => { @@ -92,12 +99,21 @@ const AnvilNetworkConfigInputGroup = < netNum += 1; } - return networkIndex < end; + return networkIndex < limit; }); return netNum; }, - [networkListArray], + [networkListEntries], + ); + + const networkTypeOptions = useMemo( + () => + NETWORK_TYPE_ENTRIES.map(([key, value]) => ({ + displayValue: value, + value: key, + })), + [], ); const buildNetwork = useCallback( @@ -106,7 +122,7 @@ const AnvilNetworkConfigInputGroup = < networkSubnetMask = '', networkType = 'ifn', // Params that depend on others. - networkGateway = isIfn(networkType) ? '' : undefined, + networkGateway = assertIfn(networkType) ? '' : undefined, networkNumber = getNetworkNumber(networkType) + 1, }: Partial = {}): { network: AnvilNetworkConfigNetwork; @@ -130,30 +146,80 @@ const AnvilNetworkConfigInputGroup = < [], ); - const removeNetwork = useCallback( + const handleNetworkTypeChange = useCallback( + ( + { networkId: targetId, networkType: previousType }, + { target: { value } }, + ) => { + const newType = String(value); + + let isIdMatch = false; + let newTypeNumber = 0; + + const newList = networkListEntries.reduce( + (previous, [networkId, networkValue]) => { + const { networkNumber: initnn, networkType: initnt } = networkValue; + + let networkNumber = initnn; + let networkType = initnt; + + if (networkId === targetId) { + isIdMatch = true; + + networkType = newType; + } + + const isTypeMatch = networkType === newType; + + if (isTypeMatch) { + newTypeNumber += 1; + } + + if (isIdMatch) { + if (isTypeMatch) { + networkNumber = newTypeNumber; + } else if (networkType === previousType) { + networkNumber -= 1; + } + + previous[networkId] = { + ...networkValue, + networkNumber, + networkType, + }; + } else { + previous[networkId] = networkValue; + } + + return previous; + }, + {}, + ); + + setNetworkList(newList); + }, + [networkListEntries], + ); + + const handleNetworkRemove = useCallback( ({ networkId: rmId, networkType: rmType }) => { let isIdMatch = false; let networkNumber = 0; - const newList = networkListArray.reduce( + const newList = networkListEntries.reduce( (previous, [networkId, networkValue]) => { - const { networkType } = networkValue; - if (networkId === rmId) { isIdMatch = true; } else { + const { networkType } = networkValue; + if (networkType === rmType) { networkNumber += 1; } - if (isIdMatch) { - previous[networkId] = { - ...networkValue, - networkNumber, - }; - } else { - previous[networkId] = networkValue; - } + previous[networkId] = isIdMatch + ? { ...networkValue, networkNumber } + : networkValue; } return previous; @@ -163,13 +229,13 @@ const AnvilNetworkConfigInputGroup = < setNetworkList(newList); }, - [networkListArray], + [networkListEntries], ); const networksGridLayout = useMemo(() => { let result: GridLayout = {}; - result = networkListArray.reduce( + result = networkListEntries.reduce( ( previous, [ @@ -190,10 +256,13 @@ const AnvilNetworkConfigInputGroup = < const inputIdPrefix = `${INPUT_ID_PREFIX_ANVIL_NETWORK_CONFIG}-${networkId}`; const inputGatewayId = `${inputIdPrefix}-gateway`; const inputMinIpId = `${inputIdPrefix}-min-ip`; + const inputNetworkTypeId = `${inputIdPrefix}-network-type`; const inputSubnetMaskId = `${inputIdPrefix}-subnet-mask`; const isFirstNetwork = networkNumber === 1; - const isShowGateway = isIfn(networkType); + const isIfn = assertIfn(networkType); + const isMn = assertMn(networkType); + const isOptional = isMn || !isFirstNetwork; previous[cellId] = { children: ( @@ -202,18 +271,22 @@ const AnvilNetworkConfigInputGroup = < idPrefix={idPrefix} inputGatewayId={inputGatewayId} inputMinIpId={inputMinIpId} + inputNetworkTypeId={inputNetworkTypeId} inputSubnetMaskId={inputSubnetMaskId} networkId={networkId} networkNumber={networkNumber} networkType={networkType} - onClose={removeNetwork} + networkTypeOptions={networkTypeOptions} + onClose={handleNetworkRemove} + onNetworkTypeChange={handleNetworkTypeChange} previous={{ gateway: networkGateway, minIp: networkMinIp, subnetMask: networkSubnetMask, }} - showCloseButton={!isFirstNetwork} - showGateway={isShowGateway} + readonlyNetworkName={!isOptional} + showCloseButton={isOptional} + showGateway={isIfn} /> ), md: 3, @@ -226,7 +299,13 @@ const AnvilNetworkConfigInputGroup = < ); return result; - }, [formUtils, networkListArray, removeNetwork]); + }, [ + formUtils, + networkListEntries, + networkTypeOptions, + handleNetworkRemove, + handleNetworkTypeChange, + ]); return ( ({ formUtils: { @@ -22,17 +22,21 @@ const AnvilNetworkInputGroup = ({ inputGatewayLabel = 'Gateway', inputMinIpId, inputMinIpLabel = 'IP address', + inputNetworkTypeId, inputSubnetMaskId, inputSubnetMaskLabel = 'Subnet mask', networkId, networkNumber, networkType, + networkTypeOptions, onClose, + onNetworkTypeChange, previous: { gateway: previousGateway, minIp: previousIpAddress, subnetMask: previousSubnetMask, } = {}, + readonlyNetworkName: isReadonlyNetworkName, showCloseButton: isShowCloseButton, showGateway: isShowGateway, }: AnvilNetworkInputGroupProps): ReactElement => { @@ -132,7 +136,26 @@ const AnvilNetworkInputGroup = ({ return ( - {networkName} + { + onNetworkTypeChange?.call( + null, + { networkId, networkType }, + ...args, + ); + }} + selectItems={networkTypeOptions} + selectProps={{ + renderValue: () => networkName, + }} + value={networkType} + /> + } + /> {closeButtonElement} diff --git a/striker-ui/types/ManageManifest.d.ts b/striker-ui/types/ManageManifest.d.ts index 82c25b7a..3e08fd7a 100644 --- a/striker-ui/types/ManageManifest.d.ts +++ b/striker-ui/types/ManageManifest.d.ts @@ -28,17 +28,24 @@ type AnvilNetworkCloseHandler = ( ...handlerArgs: Parameters ) => ReturnType; +type AnvilNetworkTypeChangeHandler = ( + args: { networkId: string } & Pick, + ...handlerArgs: Parameters +) => ReturnType; + type AnvilNetworkInputGroupOptionalProps = { inputGatewayId?: string; inputGatewayLabel?: string; inputMinIpLabel?: string; inputSubnetMaskLabel?: string; onClose?: AnvilNetworkCloseHandler; + onNetworkTypeChange?: AnvilNetworkTypeChangeHandler; previous?: { gateway?: string; minIp?: string; subnetMask?: string; }; + readonlyNetworkName?: boolean; showCloseButton?: boolean; showGateway?: boolean; }; @@ -48,10 +55,12 @@ type AnvilNetworkInputGroupProps = formUtils: FormUtils; idPrefix: string; inputMinIpId: string; + inputNetworkTypeId: string; inputSubnetMaskId: string; networkId: string; networkNumber: number; networkType: string; + networkTypeOptions: SelectItem[]; }; type AnvilHostInputGroupOptionalProps = { diff --git a/striker-ui/types/Select.d.ts b/striker-ui/types/Select.d.ts index 6b656304..fd678f4d 100644 --- a/striker-ui/types/Select.d.ts +++ b/striker-ui/types/Select.d.ts @@ -1,3 +1,8 @@ +type SelectChangeEventHandler = Exclude< + import('@mui/material').SelectProps['onChange'], + undefined +>; + type SelectOptionalProps = { onClearIndicatorClick?: import('@mui/material').IconButtonProps['onClick']; };