diff --git a/striker-ui/components/StrikerInitForm.tsx b/striker-ui/components/StrikerInitForm.tsx index 0465f545..e6ea9a6a 100644 --- a/striker-ui/components/StrikerInitForm.tsx +++ b/striker-ui/components/StrikerInitForm.tsx @@ -1,42 +1,229 @@ -import { FC, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useState } from 'react'; import { Box as MUIBox } from '@mui/material'; import NetworkInitForm from './NetworkInitForm'; +import { OutlinedInputProps } from './OutlinedInput'; import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import { Panel, PanelHeader } from './Panels'; import { HeaderText } from './Text'; +import pad from '../lib/pad'; -const StrikerInitForm: FC = () => { +const MAX_ORGANIZATION_PREFIX_LENGTH = 5; +const MIN_ORGANIZATION_PREFIX_LENGTH = 2; + +const MAP_TO_ORGANIZATION_PREFIX_BUILDER: Record< + number, + (words: string[]) => string +> = { + 0: () => '', + 1: ([word]) => + word.substring(0, MIN_ORGANIZATION_PREFIX_LENGTH).toLocaleLowerCase(), + 2: (words) => + words.map((word) => word.substring(0, 1).toLocaleLowerCase()).join(''), +}; + +export type MapToType = { + number: number; + string: string; +}; + +export type MapToStateSetter = { + [TypeName in keyof MapToType]: Dispatch>; +}; + +export type MapToValueConverter = { + [TypeName in keyof MapToType]: (value: unknown) => MapToType[TypeName]; +}; + +export type InputOnChangeParameters = Parameters< + Exclude +>; + +const MAP_TO_VALUE_CONVERTER: MapToValueConverter = { + number: (value) => parseInt(String(value), 10) || 0, + string: (value) => String(value), +}; + +const createInputOnChangeHandler = + ({ + postSet, + preSet, + set, + setType = 'string', + }: { + postSet?: (...args: InputOnChangeParameters) => void; + preSet?: (...args: InputOnChangeParameters) => void; + set?: MapToStateSetter[TypeName]; + setType?: TypeName | 'string'; + }): OutlinedInputProps['onChange'] => + (event) => { + const { + target: { value }, + } = event; + const postConvertValue = MAP_TO_VALUE_CONVERTER[setType]( + value, + ) as MapToType[TypeName]; + + preSet?.call(null, event); + set?.call(null, postConvertValue); + postSet?.call(null, event); + }; + +const buildOrganizationPrefix = (organizationName: string) => { + const words: string[] = organizationName + .split(/\s/) + .filter((word) => !/and|of/.test(word)) + .slice(0, MAX_ORGANIZATION_PREFIX_LENGTH); + + const builderKey: number = words.length > 1 ? 2 : words.length; + + return MAP_TO_ORGANIZATION_PREFIX_BUILDER[builderKey](words); +}; + +const buildHostName = ( + organizationPrefix: string, + hostNumber: number, + domainName: string, + existingHostName = '', +) => + organizationPrefix.length > 0 && hostNumber > 0 && domainName.length > 0 + ? `${organizationPrefix}-striker${pad(hostNumber)}.${domainName}` + : existingHostName; + +const StrikerInitGeneralForm: FC = () => { const [organizationNameInput, setOrganizationNameInput] = useState(''); + const [organizationPrefixInput, setOrganizationPrefixInput] = + useState(''); + const [domainNameInput, setDomainNameInput] = useState(''); + const [hostNumberInput, setHostNumberInput] = useState(0); + const [hostNameInput, setHostNameInput] = useState(''); + + const handleOrganizationNameInputOnChange = createInputOnChangeHandler({ + set: setOrganizationNameInput, + }); + const handleOrganizationPrefixInputOnChange = createInputOnChangeHandler({ + set: setOrganizationPrefixInput, + }); + const handleDomainNameInputOnChange = createInputOnChangeHandler({ + set: setDomainNameInput, + }); + const handleHostNumberInputOnChange = createInputOnChangeHandler({ + set: setHostNumberInput, + setType: 'number', + }); + const handleHostNameInputOnChange = createInputOnChangeHandler({ + set: setHostNameInput, + }); return ( - - - - - :not(:first-child)': { marginTop: '1em' }, + :not(:first-child)': { + marginTop: '1em', + }, + }} + > + { + const newOrganizationName = String(value); + + setOrganizationPrefixInput( + buildOrganizationPrefix(newOrganizationName), + ); + }, }} - > - { - setOrganizationNameInput(String(value)); - }} - value={organizationNameInput} - /> - - - - - - + label="Organization name" + onChange={handleOrganizationNameInputOnChange} + value={organizationNameInput} + /> + { + const newOrganizationPrefix = String(value); + + setHostNameInput( + buildHostName( + newOrganizationPrefix, + hostNumberInput, + domainNameInput, + hostNameInput, + ), + ); + }, + }} + label="Organization prefix" + onChange={handleOrganizationPrefixInputOnChange} + value={organizationPrefixInput} + /> + { + const newDomainName = String(value); + + setHostNameInput( + buildHostName( + organizationPrefixInput, + hostNumberInput, + newDomainName, + hostNameInput, + ), + ); + }, + }} + onChange={handleDomainNameInputOnChange} + value={domainNameInput} + /> + { + const newHostNumber = parseInt(value, 10); + + setHostNameInput( + buildHostName( + organizationPrefixInput, + newHostNumber, + domainNameInput, + hostNameInput, + ), + ); + }, + }} + label="Host number" + onChange={handleHostNumberInputOnChange} + value={hostNumberInput} + /> + + ); }; +const StrikerInitForm: FC = () => ( + + + + + :not(:first-child)': { marginTop: '1em' }, + }} + > + + + + +); + export default StrikerInitForm;