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.
386 lines
10 KiB
386 lines
10 KiB
import { Box, styled, Tooltip } from '@mui/material'; |
|
import { ReactElement, ReactNode, useMemo } from 'react'; |
|
|
|
import INPUT_TYPES from '../../lib/consts/INPUT_TYPES'; |
|
|
|
import FlexBox from '../FlexBox'; |
|
import InputWithRef from '../InputWithRef'; |
|
import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; |
|
import { ExpandablePanel } from '../Panels'; |
|
import SelectWithLabel from '../SelectWithLabel'; |
|
import SwitchWithLabel from '../SwitchWithLabel'; |
|
import { |
|
buildIPAddressTestBatch, |
|
buildNumberTestBatch, |
|
buildPeacefulStringTestBatch, |
|
testNotBlank, |
|
} from '../../lib/test_input'; |
|
import { BodyText } from '../Text'; |
|
|
|
const CHECKED_STATES: Array<string | undefined> = ['1', 'on']; |
|
|
|
const INPUT_ID_SEPARATOR = '-'; |
|
|
|
const getStringParamInputTestBatch = <M extends MapToInputTestID>({ |
|
formUtils: { buildFinishInputTestBatchFunction, setMessage }, |
|
id, |
|
label, |
|
}: { |
|
formUtils: FormUtils<M>; |
|
id: string; |
|
label: string; |
|
}) => { |
|
const onFinishBatch = buildFinishInputTestBatchFunction(id); |
|
|
|
const onSuccess = () => { |
|
setMessage(id); |
|
}; |
|
|
|
return label.toLowerCase() === 'ip' |
|
? buildIPAddressTestBatch( |
|
label, |
|
onSuccess, |
|
{ onFinishBatch }, |
|
(message) => { |
|
setMessage(id, { children: message }); |
|
}, |
|
) |
|
: { |
|
defaults: { |
|
onSuccess, |
|
}, |
|
onFinishBatch, |
|
tests: [{ test: testNotBlank }], |
|
}; |
|
}; |
|
|
|
const buildNumberParamInput = <M extends MapToInputTestID>( |
|
args: FenceParameterInputBuilderParameters<M>, |
|
): ReactElement => { |
|
const { formUtils, id, isRequired, label = '', name = id, value } = args; |
|
|
|
const { |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
setMessage, |
|
} = formUtils; |
|
|
|
return ( |
|
<InputWithRef |
|
key={`${id}-wrapper`} |
|
input={ |
|
<OutlinedInputWithLabel |
|
id={id} |
|
label={label} |
|
name={name} |
|
value={value} |
|
/> |
|
} |
|
inputTestBatch={buildNumberTestBatch( |
|
label, |
|
() => { |
|
setMessage(id); |
|
}, |
|
{ onFinishBatch: buildFinishInputTestBatchFunction(id) }, |
|
(message) => { |
|
setMessage(id, { children: message }); |
|
}, |
|
)} |
|
onFirstRender={buildInputFirstRenderFunction(id)} |
|
required={isRequired} |
|
valueType="number" |
|
/> |
|
); |
|
}; |
|
|
|
const MAP_TO_INPUT_BUILDER: MapToInputBuilder<Record<string, string>> = { |
|
boolean: (args) => { |
|
const { id, isChecked = false, label, name = id } = args; |
|
|
|
return ( |
|
<InputWithRef |
|
key={`${id}-wrapper`} |
|
input={ |
|
<SwitchWithLabel |
|
checked={isChecked} |
|
id={id} |
|
label={label} |
|
name={name} |
|
/> |
|
} |
|
valueType="boolean" |
|
/> |
|
); |
|
}, |
|
integer: buildNumberParamInput, |
|
second: buildNumberParamInput, |
|
select: (args) => { |
|
const { |
|
formUtils, |
|
id, |
|
isRequired, |
|
label, |
|
name = id, |
|
selectOptions = [], |
|
value = '', |
|
} = args; |
|
|
|
const { |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
setMessage, |
|
} = formUtils; |
|
|
|
return ( |
|
<InputWithRef |
|
key={`${id}-wrapper`} |
|
input={ |
|
<SelectWithLabel |
|
id={id} |
|
label={label} |
|
name={name} |
|
selectItems={selectOptions} |
|
value={value} |
|
/> |
|
} |
|
inputTestBatch={{ |
|
defaults: { |
|
onSuccess: () => { |
|
setMessage(id); |
|
}, |
|
}, |
|
onFinishBatch: buildFinishInputTestBatchFunction(id), |
|
tests: [{ test: testNotBlank }], |
|
}} |
|
onFirstRender={buildInputFirstRenderFunction(id)} |
|
required={isRequired} |
|
/> |
|
); |
|
}, |
|
string: (args) => { |
|
const { |
|
formUtils, |
|
id, |
|
isRequired, |
|
isSensitive = false, |
|
label = '', |
|
name = id, |
|
value, |
|
} = args; |
|
|
|
const { buildInputFirstRenderFunction } = formUtils; |
|
|
|
let inputType; |
|
|
|
if (isSensitive) { |
|
inputType = INPUT_TYPES.password; |
|
} |
|
|
|
return ( |
|
<InputWithRef |
|
key={`${id}-wrapper`} |
|
input={ |
|
<OutlinedInputWithLabel |
|
id={id} |
|
label={label} |
|
name={name} |
|
type={inputType} |
|
value={value} |
|
/> |
|
} |
|
inputTestBatch={getStringParamInputTestBatch({ formUtils, id, label })} |
|
onFirstRender={buildInputFirstRenderFunction(id)} |
|
required={isRequired} |
|
/> |
|
); |
|
}, |
|
}; |
|
|
|
const combineIds = (...pieces: string[]) => pieces.join(INPUT_ID_SEPARATOR); |
|
|
|
const FenceInputWrapper = styled(FlexBox)({ |
|
margin: '.4em 0', |
|
}); |
|
|
|
const CommonFenceInputGroup = <M extends Record<string, string>>({ |
|
fenceId, |
|
fenceParameterTooltipProps, |
|
fenceTemplate, |
|
formUtils, |
|
previousFenceName, |
|
previousFenceParameters, |
|
}: CommonFenceInputGroupProps<M>): ReactElement => { |
|
const { |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
setMessage, |
|
} = formUtils; |
|
|
|
const fenceParameterElements = useMemo(() => { |
|
let result: ReactNode; |
|
|
|
if (fenceTemplate && fenceId) { |
|
const { parameters: fenceParameters } = fenceTemplate[fenceId]; |
|
|
|
let mapToPreviousFenceParameterValues: FenceParameters = {}; |
|
|
|
if (previousFenceParameters) { |
|
mapToPreviousFenceParameterValues = Object.entries( |
|
previousFenceParameters, |
|
).reduce<FenceParameters>((previous, [parameterId, parameterValue]) => { |
|
const newKey = combineIds(fenceId, parameterId); |
|
|
|
previous[newKey] = parameterValue; |
|
|
|
return previous; |
|
}, {}); |
|
} |
|
|
|
const { optional: optionalInputs, required: requiredInputs } = |
|
Object.entries(fenceParameters) |
|
.sort(([a], [b]) => (a > b ? 1 : -1)) |
|
.reduce<{ |
|
optional: ReactElement[]; |
|
required: ReactElement[]; |
|
}>( |
|
( |
|
previous, |
|
[ |
|
parameterId, |
|
{ |
|
content_type: parameterType, |
|
default: parameterDefault, |
|
deprecated: rawParameterDeprecated, |
|
description: parameterDescription, |
|
options: parameterSelectOptions, |
|
required: rawParameterRequired, |
|
}, |
|
], |
|
) => { |
|
const isParameterDeprecated = |
|
String(rawParameterDeprecated) === '1'; |
|
|
|
if (isParameterDeprecated) return previous; |
|
|
|
const { optional, required } = previous; |
|
const buildInput = |
|
MAP_TO_INPUT_BUILDER[parameterType] ?? |
|
MAP_TO_INPUT_BUILDER.string; |
|
const fenceJoinParameterId = combineIds(fenceId, parameterId); |
|
|
|
const initialValue = |
|
mapToPreviousFenceParameterValues[fenceJoinParameterId] ?? |
|
parameterDefault; |
|
const isParameterRequired = String(rawParameterRequired) === '1'; |
|
const isParameterSensitive = /passw/i.test(parameterId); |
|
|
|
const parameterInput = buildInput({ |
|
formUtils, |
|
id: fenceJoinParameterId, |
|
isChecked: CHECKED_STATES.includes(initialValue), |
|
isRequired: isParameterRequired, |
|
isSensitive: isParameterSensitive, |
|
label: parameterId, |
|
selectOptions: parameterSelectOptions, |
|
value: initialValue, |
|
}); |
|
const parameterInputWithTooltip = ( |
|
<Tooltip |
|
componentsProps={{ |
|
tooltip: { |
|
sx: { |
|
maxWidth: { md: '62.6em' }, |
|
}, |
|
}, |
|
}} |
|
disableInteractive |
|
key={`${fenceJoinParameterId}-tooltip`} |
|
placement="top-start" |
|
title={<BodyText>{parameterDescription}</BodyText>} |
|
{...fenceParameterTooltipProps} |
|
> |
|
<Box>{parameterInput}</Box> |
|
</Tooltip> |
|
); |
|
|
|
if (isParameterRequired) { |
|
required.push(parameterInputWithTooltip); |
|
} else { |
|
optional.push(parameterInputWithTooltip); |
|
} |
|
|
|
return previous; |
|
}, |
|
{ |
|
optional: [], |
|
required: [], |
|
}, |
|
); |
|
|
|
const inputIdFenceName = combineIds(fenceId, 'name'); |
|
const inputLabelFenceName = 'Fence device name'; |
|
|
|
result = ( |
|
<FlexBox |
|
sx={{ |
|
'& > div:first-child': { marginTop: 0 }, |
|
'& > div': { marginBottom: 0 }, |
|
}} |
|
> |
|
<ExpandablePanel expandInitially header="Required parameters"> |
|
<FenceInputWrapper> |
|
<InputWithRef |
|
key={`${inputIdFenceName}-wrapper`} |
|
input={ |
|
<OutlinedInputWithLabel |
|
id={inputIdFenceName} |
|
label={inputLabelFenceName} |
|
name={inputIdFenceName} |
|
value={previousFenceName} |
|
/> |
|
} |
|
inputTestBatch={buildPeacefulStringTestBatch( |
|
inputLabelFenceName, |
|
() => { |
|
setMessage(inputIdFenceName); |
|
}, |
|
{ |
|
onFinishBatch: |
|
buildFinishInputTestBatchFunction(inputIdFenceName), |
|
}, |
|
(message) => { |
|
setMessage(inputIdFenceName, { children: message }); |
|
}, |
|
)} |
|
onFirstRender={buildInputFirstRenderFunction(inputIdFenceName)} |
|
required |
|
/> |
|
{requiredInputs} |
|
</FenceInputWrapper> |
|
</ExpandablePanel> |
|
<ExpandablePanel header="Optional parameters"> |
|
<FenceInputWrapper>{optionalInputs}</FenceInputWrapper> |
|
</ExpandablePanel> |
|
</FlexBox> |
|
); |
|
} |
|
|
|
return result; |
|
}, [ |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
fenceId, |
|
fenceParameterTooltipProps, |
|
fenceTemplate, |
|
formUtils, |
|
previousFenceName, |
|
previousFenceParameters, |
|
setMessage, |
|
]); |
|
|
|
return <>{fenceParameterElements}</>; |
|
}; |
|
|
|
export { INPUT_ID_SEPARATOR }; |
|
|
|
export default CommonFenceInputGroup;
|
|
|