Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
11 KiB

import { debounce } from 'lodash';
import { ReactElement, ReactNode, useMemo } from 'react';
import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES';
import Grid from '../Grid';
import IconButton from '../IconButton';
import InputWithRef from '../InputWithRef';
import OutlinedInputWithLabel from '../OutlinedInputWithLabel';
import { InnerPanel, InnerPanelBody, InnerPanelHeader } from '../Panels';
import SelectWithLabel from '../SelectWithLabel';
import { buildIPAddressTestBatch } from '../../lib/test_input';
const INPUT_ID_PREFIX_AN_NETWORK = 'an-network-input';
const INPUT_CELL_ID_PREFIX_AN = `${INPUT_ID_PREFIX_AN_NETWORK}-cell`;
const MAP_TO_AN_INPUT_HANDLER: MapToManifestFormInputHandler = {
gateway: (container, input) => {
const {
dataset: { networkId = '' },
value,
} = input;
const {
networkConfig: { networks },
} = container;
networks[networkId].networkGateway = value;
},
minip: (container, input) => {
const {
dataset: { networkId = '' },
value,
} = input;
const {
networkConfig: { networks },
} = container;
networks[networkId].networkMinIp = value;
},
network: (container, input) => {
const {
dataset: { networkId = '', networkNumber: rawNn = '', networkType = '' },
} = input;
const {
networkConfig: { networks },
} = container;
const networkNumber = Number.parseInt(rawNn, 10);
networks[networkId] = {
networkNumber,
networkType,
} as ManifestNetwork;
},
subnetmask: (container, input) => {
const {
dataset: { networkId = '' },
value,
} = input;
const {
networkConfig: { networks },
} = container;
networks[networkId].networkSubnetMask = value;
},
};
const buildInputIdANGateway = (networkId: string): string =>
`${INPUT_ID_PREFIX_AN_NETWORK}-${networkId}-gateway`;
const buildInputIdANMinIp = (networkId: string): string =>
`${INPUT_ID_PREFIX_AN_NETWORK}-${networkId}-min-ip`;
const buildInputIdANNetworkType = (networkId: string): string =>
`${INPUT_ID_PREFIX_AN_NETWORK}-${networkId}-network-type`;
const buildInputIdANSubnetMask = (networkId: string): string =>
`${INPUT_ID_PREFIX_AN_NETWORK}-${networkId}-subnet-mask`;
const AnNetworkInputGroup = <M extends MapToInputTestID>({
debounceWait = 500,
formUtils: {
buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
buildInputUnmountFunction,
setMessage,
},
inputGatewayLabel = 'Gateway',
inputMinIpLabel = 'IP address',
inputSubnetMaskLabel = 'Subnet mask',
networkId,
networkNumber,
networkType,
networkTypeOptions,
onClose,
onNetworkGatewayChange,
onNetworkMinIpChange,
onNetworkSubnetMaskChange,
onNetworkTypeChange,
previous: {
gateway: previousGateway,
minIp: previousIpAddress,
subnetMask: previousSubnetMask,
} = {},
readonlyNetworkName: isReadonlyNetworkName,
showCloseButton: isShowCloseButton,
showGateway: isShowGateway,
}: AnNetworkInputGroupProps<M>): ReactElement => {
const networkName = useMemo(
() => `${NETWORK_TYPES[networkType]} ${networkNumber}`,
[networkNumber, networkType],
);
const inputCellIdGateway = useMemo(
() => `${INPUT_CELL_ID_PREFIX_AN}-${networkId}-gateway`,
[networkId],
);
const inputCellIdIp = useMemo(
() => `${INPUT_CELL_ID_PREFIX_AN}-${networkId}-ip`,
[networkId],
);
const inputCellIdSubnetMask = useMemo(
() => `${INPUT_CELL_ID_PREFIX_AN}-${networkId}-subnet-mask`,
[networkId],
);
const inputIdANNetwork = useMemo(
() => `${INPUT_ID_PREFIX_AN_NETWORK}-${networkId}`,
[networkId],
);
const inputIdGateway = useMemo(
() => buildInputIdANGateway(networkId),
[networkId],
);
const inputIdMinIp = useMemo(
() => buildInputIdANMinIp(networkId),
[networkId],
);
const inputIdNetworkType = useMemo(
() => buildInputIdANNetworkType(networkId),
[networkId],
);
const inputIdSubnetMask = useMemo(
() => buildInputIdANSubnetMask(networkId),
[networkId],
);
const inputCellGatewayDisplay = useMemo(
() => (isShowGateway ? undefined : 'none'),
[isShowGateway],
);
const debounceNetworkGatewayChangeHandler = useMemo(
() =>
onNetworkGatewayChange && debounce(onNetworkGatewayChange, debounceWait),
[debounceWait, onNetworkGatewayChange],
);
const debounceNetworkMinIpChangeHandler = useMemo(
() => onNetworkMinIpChange && debounce(onNetworkMinIpChange, debounceWait),
[debounceWait, onNetworkMinIpChange],
);
const debounceNetworkSubnetMaskChangeHandler = useMemo(
() =>
onNetworkSubnetMaskChange &&
debounce(onNetworkSubnetMaskChange, debounceWait),
[debounceWait, onNetworkSubnetMaskChange],
);
const closeButtonElement = useMemo<ReactNode>(
() =>
isShowCloseButton && (
<IconButton
mapPreset="close"
iconProps={{ fontSize: 'small' }}
onClick={(...args) => {
onClose?.call(null, { networkId, networkType }, ...args);
}}
sx={{
padding: '.2em',
position: 'absolute',
right: '-.6rem',
top: '-.2rem',
}}
/>
),
[isShowCloseButton, networkId, networkType, onClose],
);
const inputGatewayElement = useMemo<ReactNode>(() => {
let result: ReactNode;
if (isShowGateway && inputIdGateway) {
result = (
<InputWithRef
createInputOnChangeHandlerOptions={{
postSet: (...args) =>
debounceNetworkGatewayChangeHandler?.call(
null,
{ networkId, networkType },
...args,
),
}}
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'gateway',
'data-network-id': networkId,
}}
id={inputIdGateway}
label={inputGatewayLabel}
value={previousGateway}
/>
}
inputTestBatch={buildIPAddressTestBatch(
`${networkName} ${inputGatewayLabel}`,
() => {
setMessage(inputIdGateway);
},
{
onFinishBatch: buildFinishInputTestBatchFunction(inputIdGateway),
},
(message) => {
setMessage(inputIdGateway, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(inputIdGateway)}
onUnmount={buildInputUnmountFunction(inputIdGateway)}
required={isShowGateway}
/>
);
}
return result;
}, [
isShowGateway,
inputIdGateway,
networkId,
inputGatewayLabel,
previousGateway,
networkName,
buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
buildInputUnmountFunction,
debounceNetworkGatewayChangeHandler,
networkType,
setMessage,
]);
return (
<InnerPanel mv={0}>
<InnerPanelHeader>
<InputWithRef
input={
<SelectWithLabel
id={inputIdNetworkType}
isReadOnly={isReadonlyNetworkName}
onChange={(...args) => {
onNetworkTypeChange?.call(
null,
{ networkId, networkType },
...args,
);
}}
selectItems={networkTypeOptions}
selectProps={{
renderValue: () => networkName,
}}
value={networkType}
/>
}
/>
{closeButtonElement}
</InnerPanelHeader>
<InnerPanelBody>
<input
hidden
id={inputIdANNetwork}
readOnly
data-handler="network"
data-network-id={networkId}
data-network-number={networkNumber}
data-network-type={networkType}
/>
<Grid
layout={{
[inputCellIdIp]: {
children: (
<InputWithRef
createInputOnChangeHandlerOptions={{
postSet: (...args) =>
debounceNetworkMinIpChangeHandler?.call(
null,
{ networkId, networkType },
...args,
),
}}
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'minip',
'data-network-id': networkId,
}}
id={inputIdMinIp}
label={inputMinIpLabel}
value={previousIpAddress}
/>
}
inputTestBatch={buildIPAddressTestBatch(
`${networkName} ${inputMinIpLabel}`,
() => {
setMessage(inputIdMinIp);
},
{
onFinishBatch:
buildFinishInputTestBatchFunction(inputIdMinIp),
},
(message) => {
setMessage(inputIdMinIp, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(inputIdMinIp)}
onUnmount={buildInputUnmountFunction(inputIdMinIp)}
required
/>
),
},
[inputCellIdSubnetMask]: {
children: (
<InputWithRef
createInputOnChangeHandlerOptions={{
postSet: (...args) =>
debounceNetworkSubnetMaskChangeHandler?.call(
null,
{ networkId, networkType },
...args,
),
}}
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'subnetmask',
'data-network-id': networkId,
}}
id={inputIdSubnetMask}
label={inputSubnetMaskLabel}
value={previousSubnetMask}
/>
}
inputTestBatch={buildIPAddressTestBatch(
`${networkName} ${inputSubnetMaskLabel}`,
() => {
setMessage(inputIdSubnetMask);
},
{
onFinishBatch:
buildFinishInputTestBatchFunction(inputIdSubnetMask),
},
(message) => {
setMessage(inputIdSubnetMask, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(
inputIdSubnetMask,
)}
onUnmount={buildInputUnmountFunction(inputIdSubnetMask)}
required
/>
),
},
[inputCellIdGateway]: {
children: inputGatewayElement,
display: inputCellGatewayDisplay,
},
}}
spacing="1em"
/>
</InnerPanelBody>
</InnerPanel>
);
};
export {
INPUT_ID_PREFIX_AN_NETWORK,
MAP_TO_AN_INPUT_HANDLER,
buildInputIdANGateway,
buildInputIdANMinIp,
buildInputIdANNetworkType,
buildInputIdANSubnetMask,
};
export default AnNetworkInputGroup;