|
|
|
@ -1,6 +1,7 @@ |
|
|
|
|
import { Dispatch, FC, SetStateAction, useState } from 'react'; |
|
|
|
|
import { Box as MUIBox } from '@mui/material'; |
|
|
|
|
|
|
|
|
|
import ContainedButton, { ContainedButtonProps } from './ContainedButton'; |
|
|
|
|
import FlexBox from './FlexBox'; |
|
|
|
|
import NetworkInitForm from './NetworkInitForm'; |
|
|
|
|
import { OutlinedInputProps } from './OutlinedInput'; |
|
|
|
@ -37,6 +38,10 @@ export type MapToValueConverter = { |
|
|
|
|
[TypeName in keyof MapToType]: (value: unknown) => MapToType[TypeName]; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export type MapToValueIsEmptyFunction = { |
|
|
|
|
[TypeName in keyof MapToType]: (value: MapToType[TypeName]) => boolean; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export type InputOnChangeParameters = Parameters< |
|
|
|
|
Exclude<OutlinedInputProps['onChange'], undefined> |
|
|
|
|
>; |
|
|
|
@ -46,6 +51,11 @@ const MAP_TO_VALUE_CONVERTER: MapToValueConverter = { |
|
|
|
|
string: (value) => String(value), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const MAP_TO_VALUE_IS_EMPTY_FUNCTION: MapToValueIsEmptyFunction = { |
|
|
|
|
number: (value: number) => value === 0, |
|
|
|
|
string: (value: string) => value.trim().length === 0, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const createInputOnChangeHandler = |
|
|
|
|
<TypeName extends keyof MapToType>({ |
|
|
|
|
postSet, |
|
|
|
@ -71,6 +81,36 @@ const createInputOnChangeHandler = |
|
|
|
|
postSet?.call(null, event); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const isEmpty = <TypeName extends keyof MapToType>( |
|
|
|
|
values: Array<MapToType[TypeName]>, |
|
|
|
|
{ not, fn = 'every' }: { not?: boolean; fn?: 'every' | 'some' }, |
|
|
|
|
) => |
|
|
|
|
values[fn]((value) => { |
|
|
|
|
const type = typeof value as TypeName; |
|
|
|
|
|
|
|
|
|
let result = MAP_TO_VALUE_IS_EMPTY_FUNCTION[type](value); |
|
|
|
|
|
|
|
|
|
if (not) { |
|
|
|
|
result = !result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const createFunction = ( |
|
|
|
|
{ |
|
|
|
|
conditionFn = () => true, |
|
|
|
|
str = '', |
|
|
|
|
condition = conditionFn() && str.length === 0, |
|
|
|
|
}: { |
|
|
|
|
condition?: boolean; |
|
|
|
|
conditionFn?: (...args: unknown[]) => boolean; |
|
|
|
|
str?: string; |
|
|
|
|
}, |
|
|
|
|
fn: () => unknown, |
|
|
|
|
...fnArgs: Parameters<typeof fn> |
|
|
|
|
) => (condition ? fn.bind(null, ...fnArgs) : undefined); |
|
|
|
|
|
|
|
|
|
const buildOrganizationPrefix = (organizationName: string) => { |
|
|
|
|
const words: string[] = organizationName |
|
|
|
|
.split(/\s/) |
|
|
|
@ -87,10 +127,17 @@ const buildHostName = ( |
|
|
|
|
hostNumber: number, |
|
|
|
|
domainName: string, |
|
|
|
|
) => |
|
|
|
|
organizationPrefix.length > 0 && hostNumber > 0 && domainName.length > 0 |
|
|
|
|
isEmpty([organizationPrefix, hostNumber, domainName], { not: true }) |
|
|
|
|
? `${organizationPrefix}-striker${pad(hostNumber)}.${domainName}` |
|
|
|
|
: ''; |
|
|
|
|
|
|
|
|
|
const SuggestButton: FC<ContainedButtonProps> = ({ onClick, ...restProps }) => |
|
|
|
|
onClick ? ( |
|
|
|
|
<ContainedButton {...{ onClick, ...restProps }}>Suggest</ContainedButton> |
|
|
|
|
) : ( |
|
|
|
|
<></> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
const [organizationNameInput, setOrganizationNameInput] = |
|
|
|
|
useState<string>(''); |
|
|
|
@ -128,36 +175,58 @@ const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
}, |
|
|
|
|
set: setHostNameInput, |
|
|
|
|
}); |
|
|
|
|
const populateOrganizationPrefixInput = () => { |
|
|
|
|
setOrganizationPrefixInput(buildOrganizationPrefix(organizationNameInput)); |
|
|
|
|
}; |
|
|
|
|
const populateHostNameInput = () => { |
|
|
|
|
setHostNameInput( |
|
|
|
|
buildHostName(organizationPrefixInput, hostNumberInput, domainNameInput), |
|
|
|
|
); |
|
|
|
|
const populateOrganizationPrefixInput = ({ |
|
|
|
|
organizationName = organizationNameInput, |
|
|
|
|
} = {}) => { |
|
|
|
|
const organizationPrefix = buildOrganizationPrefix(organizationName); |
|
|
|
|
|
|
|
|
|
setOrganizationPrefixInput(organizationPrefix); |
|
|
|
|
|
|
|
|
|
return organizationPrefix; |
|
|
|
|
}; |
|
|
|
|
const createPopulateOnBlurHandler = |
|
|
|
|
( |
|
|
|
|
{ |
|
|
|
|
condition = true, |
|
|
|
|
toPopulate = '', |
|
|
|
|
}: { condition?: boolean; toPopulate?: string }, |
|
|
|
|
populate: (...args: unknown[]) => void, |
|
|
|
|
...populateArgs: Parameters<typeof populate> |
|
|
|
|
) => |
|
|
|
|
() => { |
|
|
|
|
if (condition && toPopulate.length === 0) { |
|
|
|
|
populate(...populateArgs); |
|
|
|
|
} |
|
|
|
|
const populateHostNameInput = ({ |
|
|
|
|
organizationPrefix = organizationPrefixInput, |
|
|
|
|
hostNumber = hostNumberInput, |
|
|
|
|
domainName = domainNameInput, |
|
|
|
|
} = {}) => { |
|
|
|
|
const hostName = buildHostName(organizationPrefix, hostNumber, domainName); |
|
|
|
|
|
|
|
|
|
setHostNameInput(hostName); |
|
|
|
|
|
|
|
|
|
return hostName; |
|
|
|
|
}; |
|
|
|
|
const populateOrganizationPrefixInputOnBlur = createPopulateOnBlurHandler( |
|
|
|
|
const populateOrganizationPrefixInputOnBlur = createFunction( |
|
|
|
|
{ condition: !isOrganizationPrefixInputUserChanged }, |
|
|
|
|
populateOrganizationPrefixInput, |
|
|
|
|
); |
|
|
|
|
const populateHostNameInputOnBlur = createPopulateOnBlurHandler( |
|
|
|
|
const populateHostNameInputOnBlur = createFunction( |
|
|
|
|
{ condition: !isHostNameInputUserChanged }, |
|
|
|
|
populateHostNameInput, |
|
|
|
|
); |
|
|
|
|
const handleOrganizationPrefixSuggest = createFunction( |
|
|
|
|
{ |
|
|
|
|
conditionFn: () => |
|
|
|
|
isOrganizationPrefixInputUserChanged && |
|
|
|
|
isEmpty([organizationNameInput], { not: true }), |
|
|
|
|
}, |
|
|
|
|
() => { |
|
|
|
|
const organizationPrefix = populateOrganizationPrefixInput(); |
|
|
|
|
|
|
|
|
|
if (!isHostNameInputUserChanged) { |
|
|
|
|
populateHostNameInput({ organizationPrefix }); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
); |
|
|
|
|
const handlerHostNameSuggest = createFunction( |
|
|
|
|
{ |
|
|
|
|
conditionFn: () => |
|
|
|
|
isHostNameInputUserChanged && |
|
|
|
|
isEmpty([organizationPrefixInput, hostNumberInput, domainNameInput], { |
|
|
|
|
not: true, |
|
|
|
|
}), |
|
|
|
|
}, |
|
|
|
|
populateHostNameInput, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<MUIBox |
|
|
|
@ -188,6 +257,7 @@ const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
onChange={handleOrganizationNameInputOnChange} |
|
|
|
|
value={organizationNameInput} |
|
|
|
|
/> |
|
|
|
|
<FlexBox row> |
|
|
|
|
<OutlinedInputWithLabel |
|
|
|
|
helpMessageBoxProps={{ |
|
|
|
|
text: "Alphanumberic short-form of the organization name. It's used as the prefix for host names.", |
|
|
|
@ -197,13 +267,15 @@ const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
inputProps: { maxLength: MAX_ORGANIZATION_PREFIX_LENGTH }, |
|
|
|
|
onBlur: populateHostNameInputOnBlur, |
|
|
|
|
sx: { |
|
|
|
|
width: '8em', |
|
|
|
|
width: '7em', |
|
|
|
|
}, |
|
|
|
|
}} |
|
|
|
|
label="Prefix" |
|
|
|
|
onChange={handleOrganizationPrefixInputOnChange} |
|
|
|
|
value={organizationPrefixInput} |
|
|
|
|
/> |
|
|
|
|
<SuggestButton onClick={handleOrganizationPrefixSuggest} /> |
|
|
|
|
</FlexBox> |
|
|
|
|
</FlexBox> |
|
|
|
|
<FlexBox> |
|
|
|
|
<OutlinedInputWithLabel |
|
|
|
@ -238,6 +310,7 @@ const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
onChange={handleHostNumberInputOnChange} |
|
|
|
|
value={hostNumberInput} |
|
|
|
|
/> |
|
|
|
|
<FlexBox row sx={{ '& > :first-child': { flexGrow: 1 } }}> |
|
|
|
|
<OutlinedInputWithLabel |
|
|
|
|
helpMessageBoxProps={{ |
|
|
|
|
text: "Host name for this striker. It's usually a good idea to use the auto-generated value.", |
|
|
|
@ -247,6 +320,8 @@ const StrikerInitGeneralForm: FC = () => { |
|
|
|
|
onChange={handleHostNameInputOnChange} |
|
|
|
|
value={hostNameInput} |
|
|
|
|
/> |
|
|
|
|
<SuggestButton onClick={handlerHostNameSuggest} /> |
|
|
|
|
</FlexBox> |
|
|
|
|
</FlexBox> |
|
|
|
|
</MUIBox> |
|
|
|
|
); |
|
|
|
|