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.
255 lines
8.2 KiB
255 lines
8.2 KiB
import { |
|
MutableRefObject, |
|
forwardRef, |
|
useCallback, |
|
useMemo, |
|
useRef, |
|
useState, |
|
} from 'react'; |
|
|
|
import INPUT_TYPES from '../../lib/consts/INPUT_TYPES'; |
|
|
|
import api from '../../lib/api'; |
|
import buildMapToMessageSetter from '../../lib/buildMapToMessageSetter'; |
|
import buildObjectStateSetterCallback from '../../lib/buildObjectStateSetterCallback'; |
|
import CheckboxWithLabel from '../CheckboxWithLabel'; |
|
import ConfirmDialog from '../ConfirmDialog'; |
|
import FlexBox from '../FlexBox'; |
|
import Grid from '../Grid'; |
|
import handleAPIError from '../../lib/handleAPIError'; |
|
import IconButton from '../IconButton'; |
|
import InputWithRef, { InputForwardedRefContent } from '../InputWithRef'; |
|
import { Message } from '../MessageBox'; |
|
import MessageGroup, { MessageGroupForwardedRefContent } from '../MessageGroup'; |
|
import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; |
|
import { |
|
buildIPAddressTestBatch, |
|
buildPeacefulStringTestBatch, |
|
} from '../../lib/test_input'; |
|
import { HeaderText } from '../Text'; |
|
|
|
const IT_IDS = { |
|
dbPort: 'dbPort', |
|
ipAddress: 'ipAddress', |
|
password: 'password', |
|
sshPort: 'sshPort', |
|
user: 'user', |
|
}; |
|
const LABEL = { |
|
dbPort: 'DB port', |
|
ipAddress: 'IP address', |
|
password: 'Password', |
|
ping: 'Ping', |
|
sshPort: 'SSH port', |
|
user: 'User', |
|
}; |
|
|
|
const AddPeerDialog = forwardRef< |
|
ConfirmDialogForwardedRefContent, |
|
AddPeerDialogProps |
|
>(({ formGridColumns = 2 }, ref) => { |
|
const inputPeerDBPortRef = useRef<InputForwardedRefContent<'string'>>({}); |
|
const inputPeerIPAddressRef = useRef<InputForwardedRefContent<'string'>>({}); |
|
const inputPeerPasswordRef = useRef<InputForwardedRefContent<'string'>>({}); |
|
const inputPeerSSHPortRef = useRef<InputForwardedRefContent<'string'>>({}); |
|
const inputPeerUserRef = useRef<InputForwardedRefContent<'string'>>({}); |
|
const messageGroupRef = useRef<MessageGroupForwardedRefContent>({}); |
|
|
|
const [formValidity, setFormValidity] = useState<{ |
|
[inputTestID: string]: boolean; |
|
}>({}); |
|
const [isEnablePingTest, setIsEnablePingTest] = useState<boolean>(false); |
|
const [isSubmittingAddPeer, setIsSubmittingAddPeer] = |
|
useState<boolean>(false); |
|
|
|
const buildInputFirstRenderFunction = useCallback( |
|
(key: string) => |
|
({ isValid }: InputFirstRenderFunctionArgs) => { |
|
setFormValidity(buildObjectStateSetterCallback(key, isValid)); |
|
}, |
|
[], |
|
); |
|
const buildFinishInputTestBatchFunction = useCallback( |
|
(key: string) => (result: boolean) => { |
|
setFormValidity(buildObjectStateSetterCallback(key, result)); |
|
}, |
|
[], |
|
); |
|
const setAPIMessage = useCallback((message?: Message) => { |
|
messageGroupRef.current.setMessage?.call(null, 'api', message); |
|
}, []); |
|
|
|
const isFormInvalid = useMemo( |
|
() => Object.values(formValidity).some((isInputValid) => !isInputValid), |
|
[formValidity], |
|
); |
|
const msgSetters = useMemo( |
|
() => buildMapToMessageSetter(IT_IDS, messageGroupRef), |
|
[], |
|
); |
|
|
|
return ( |
|
<ConfirmDialog |
|
actionProceedText="Add" |
|
content={ |
|
<Grid |
|
columns={{ xs: 1, sm: formGridColumns }} |
|
layout={{ |
|
'add-peer-ip-address': { |
|
children: ( |
|
<FlexBox row spacing=".3em"> |
|
<InputWithRef |
|
input={ |
|
<OutlinedInputWithLabel |
|
id="add-peer-ip-address-input" |
|
inputProps={{ |
|
// Initiallize the field as read-only, then unlock |
|
// when the user focuses; this avoids browser's |
|
// auto-complete. |
|
readOnly: true, |
|
onFocus: (event) => { |
|
event.target.readOnly = false; |
|
}, |
|
}} |
|
label={LABEL.ipAddress} |
|
/> |
|
} |
|
inputTestBatch={buildIPAddressTestBatch( |
|
LABEL.ipAddress, |
|
() => { |
|
msgSetters.ipAddress(); |
|
}, |
|
{ |
|
onFinishBatch: buildFinishInputTestBatchFunction( |
|
IT_IDS.ipAddress, |
|
), |
|
}, |
|
(message) => { |
|
msgSetters.ipAddress({ children: message }); |
|
}, |
|
)} |
|
onFirstRender={buildInputFirstRenderFunction( |
|
IT_IDS.ipAddress, |
|
)} |
|
ref={inputPeerIPAddressRef} |
|
required |
|
/> |
|
</FlexBox> |
|
), |
|
}, |
|
'add-peer-password': { |
|
children: ( |
|
<InputWithRef |
|
input={ |
|
<OutlinedInputWithLabel |
|
fillRow |
|
id="add-peer-password-input" |
|
label={LABEL.password} |
|
type={INPUT_TYPES.password} |
|
/> |
|
} |
|
inputTestBatch={buildPeacefulStringTestBatch( |
|
LABEL.password, |
|
() => { |
|
msgSetters.password(); |
|
}, |
|
{ |
|
onFinishBatch: buildFinishInputTestBatchFunction( |
|
IT_IDS.password, |
|
), |
|
}, |
|
(message) => { |
|
msgSetters.password({ children: message }); |
|
}, |
|
)} |
|
onFirstRender={buildInputFirstRenderFunction(IT_IDS.password)} |
|
ref={inputPeerPasswordRef} |
|
required |
|
/> |
|
), |
|
}, |
|
'add-peer-is-ping': { |
|
children: ( |
|
<CheckboxWithLabel |
|
checked={isEnablePingTest} |
|
label={LABEL.ping} |
|
onChange={(event, isChecked) => { |
|
setIsEnablePingTest(isChecked); |
|
}} |
|
/> |
|
), |
|
sx: { display: 'flex' }, |
|
}, |
|
'add-peer-message-group': { |
|
children: ( |
|
<MessageGroup |
|
count={1} |
|
defaultMessageType="warning" |
|
ref={messageGroupRef} |
|
/> |
|
), |
|
sm: formGridColumns, |
|
}, |
|
}} |
|
spacing="1em" |
|
/> |
|
} |
|
dialogProps={{ PaperProps: { sx: { minWidth: '16em' } } }} |
|
loadingAction={isSubmittingAddPeer} |
|
onActionAppend={() => { |
|
setAPIMessage(); |
|
}} |
|
onProceedAppend={() => { |
|
setIsSubmittingAddPeer(true); |
|
|
|
api |
|
.post('/host/connection', { |
|
ipAddress: inputPeerIPAddressRef.current.getValue?.call(null), |
|
isPing: isEnablePingTest, |
|
password: inputPeerPasswordRef.current.getValue?.call(null), |
|
port: inputPeerDBPortRef.current.getValue?.call(null), |
|
sshPort: inputPeerSSHPortRef.current.getValue?.call(null), |
|
user: inputPeerUserRef.current.getValue?.call(null), |
|
}) |
|
.then(() => { |
|
setAPIMessage({ |
|
children: `Successfully initiated the peer addition. You can continue to edit the field(s) to add another peer.`, |
|
type: 'info', |
|
}); |
|
}) |
|
.catch((error) => { |
|
const emsg = handleAPIError(error); |
|
|
|
emsg.children = `Failed to add the given peer. ${emsg.children}`; |
|
|
|
setAPIMessage(emsg); |
|
}) |
|
.finally(() => { |
|
setIsSubmittingAddPeer(false); |
|
}); |
|
}} |
|
proceedButtonProps={{ disabled: isFormInvalid }} |
|
ref={ref} |
|
titleText={ |
|
<> |
|
<HeaderText>Add a peer</HeaderText> |
|
<IconButton |
|
mapPreset="close" |
|
onClick={() => { |
|
if (ref && 'current' in ref) { |
|
( |
|
ref as MutableRefObject<ConfirmDialogForwardedRefContent> |
|
).current.setOpen?.call(null, false); |
|
} |
|
}} |
|
variant="redcontained" |
|
/> |
|
</> |
|
} |
|
/> |
|
); |
|
}); |
|
|
|
AddPeerDialog.displayName = 'AddPeerDialog'; |
|
|
|
export default AddPeerDialog;
|
|
|