import { Box, Switch } from '@mui/material'; import { FC, ReactElement, ReactNode, useMemo, useState } from 'react'; import api from '../lib/api'; import Autocomplete from './Autocomplete'; import ContainedButton from './ContainedButton'; import FlexBox from './FlexBox'; import handleAPIError from '../lib/handleAPIError'; import InputWithRef from './InputWithRef'; import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import { ExpandablePanel } from './Panels'; import SelectWithLabel from './SelectWithLabel'; import Spinner from './Spinner'; import { BodyText } from './Text'; import useIsFirstRender from '../hooks/useIsFirstRender'; import useProtectedState from '../hooks/useProtectedState'; type FenceDeviceAutocompleteOption = { fenceDeviceDescription: string; fenceDeviceId: string; label: string; }; type FenceParameterInputBuilder = (args: { id: string; isChecked?: boolean; isRequired?: boolean; label?: string; selectOptions?: string[]; value?: string; }) => ReactElement; const MAP_TO_INPUT_BUILDER: Partial< Record, FenceParameterInputBuilder> > & { string: FenceParameterInputBuilder } = { boolean: ({ id, isChecked = false, label }) => ( {label} ), select: ({ id, isRequired, label, selectOptions = [], value = '' }) => ( } required={isRequired} /> ), string: ({ id, isRequired, label = '', value }) => ( } required={isRequired} /> ), }; const AddFenceDeivceForm: FC = () => { const isFirstRender = useIsFirstRender(); const [fenceDeviceTemplate, setFenceDeviceTemplate] = useProtectedState< APIFenceTemplate | undefined >(undefined); const [fenceDeviceTypeValue, setInputFenceDeviceTypeValue] = useState(null); const [isLoadingTemplate, setIsLoadingTemplate] = useProtectedState(true); const fenceDeviceTypeOptions = useMemo( () => fenceDeviceTemplate ? Object.entries(fenceDeviceTemplate).map( ([id, { description: rawDescription }]) => { const description = typeof rawDescription === 'string' ? rawDescription : 'No description.'; return { fenceDeviceDescription: description, fenceDeviceId: id, label: id, }; }, ) : [], [fenceDeviceTemplate], ); const fenceDeviceTypeElement = useMemo( () => ( option.fenceDeviceId === value.fenceDeviceId } label="Fence device type" onChange={(event, newFenceDeviceType) => { setInputFenceDeviceTypeValue(newFenceDeviceType); }} openOnFocus options={fenceDeviceTypeOptions} renderOption={( props, { fenceDeviceDescription, fenceDeviceId }, { selected }, ) => ( *': { width: '100%', }, }} {...props} > {fenceDeviceId} {fenceDeviceDescription} )} value={fenceDeviceTypeValue} /> ), [fenceDeviceTypeOptions, fenceDeviceTypeValue], ); const fenceParameterElements = useMemo(() => { let result: ReactNode; if (fenceDeviceTemplate && fenceDeviceTypeValue) { const { fenceDeviceId } = fenceDeviceTypeValue; const { parameters: fenceDeviceParameters } = fenceDeviceTemplate[fenceDeviceId]; const { optional: optionalInputs, required: requiredInputs } = Object.entries(fenceDeviceParameters).reduce<{ optional: ReactElement[]; required: ReactElement[]; }>( ( previous, [ parameterId, { content_type: contentType, default: parameterDefault, options: parameterSelectOptions, required: isRequired, }, ], ) => { const { optional, required } = previous; const buildInput = MAP_TO_INPUT_BUILDER[contentType] ?? MAP_TO_INPUT_BUILDER.string; const fenceJoinParameterId = `${fenceDeviceId}-${parameterId}`; const parameterIsRequired = isRequired === '1'; const parameterInput = buildInput({ id: fenceJoinParameterId, isChecked: parameterDefault === '1', isRequired: parameterIsRequired, label: parameterId, selectOptions: parameterSelectOptions, value: parameterDefault, }); if (parameterIsRequired) { required.push(parameterInput); } else { optional.push(parameterInput); } return previous; }, { optional: [], required: [ MAP_TO_INPUT_BUILDER.string({ id: `${fenceDeviceId}-name`, isRequired: true, label: 'Fence device name', }), ], }, ); result = ( <> {requiredInputs} {optionalInputs} ); } return result; }, [fenceDeviceTemplate, fenceDeviceTypeValue]); const formContent = useMemo( () => isLoadingTemplate ? ( ) : ( { event.preventDefault(); }} sx={{ '& > div': { marginBottom: 0 } }} > {fenceDeviceTypeElement} {fenceParameterElements} Add fence device ), [fenceDeviceTypeElement, fenceParameterElements, isLoadingTemplate], ); if (isFirstRender) { api .get(`/fence/template`) .then(({ data }) => { setFenceDeviceTemplate(data); }) .catch((error) => { handleAPIError(error); }) .finally(() => { setIsLoadingTemplate(false); }); } return <>{formContent}; }; export default AddFenceDeivceForm;