import { Grid as MUIGrid } from '@mui/material'; import { forwardRef, ReactNode, useCallback, useImperativeHandle, useRef, useState, } from 'react'; import FlexBox from './FlexBox'; import InputWithRef, { InputForwardedRefContent } from './InputWithRef'; import isEmpty from '../lib/isEmpty'; import MessageBox from './MessageBox'; import OutlinedInputWithLabel, { OutlinedInputWithLabelProps, } from './OutlinedInputWithLabel'; import pad from '../lib/pad'; import SuggestButton from './SuggestButton'; type GeneralInitFormForwardRefContent = { get?: () => { adminPassword?: string; organizationName?: string; organizationPrefix?: string; domainName?: string; hostNumber?: number; hostName?: string; }; }; type OutlinedInputWithLabelOnBlur = Exclude< OutlinedInputWithLabelProps['inputProps'], undefined >['onBlur']; const MAX_ORGANIZATION_PREFIX_LENGTH = 5; const MIN_ORGANIZATION_PREFIX_LENGTH = 2; const MAX_HOST_NUMBER_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(''), }; const buildOrganizationPrefix = (organizationName = '') => { 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, hostNumber, domainName, }: { organizationPrefix?: string; hostNumber?: number; domainName?: string; }) => isEmpty([organizationPrefix, hostNumber, domainName], { not: true }) ? `${organizationPrefix}-striker${pad(hostNumber)}.${domainName}` : ''; const GeneralInitForm = forwardRef( (generalInitFormProps, ref) => { const [helpMessage, setHelpText] = useState(); const [ isShowOrganizationPrefixSuggest, setIsShowOrganizationPrefixSuggest, ] = useState(false); const [isShowHostNameSuggest, setIsShowHostNameSuggest] = useState(false); const adminPasswordInputRef = useRef>( {}, ); const confirmAdminPasswordInputRef = useRef< InputForwardedRefContent<'string'> >({}); const organizationNameInputRef = useRef>( {}, ); const organizationPrefixInputRef = useRef< InputForwardedRefContent<'string'> >({}); const domainNameInputRef = useRef>({}); const hostNumberInputRef = useRef>({}); const hostNameInputRef = useRef>({}); const populateOrganizationPrefixInput = useCallback( ({ organizationName = organizationNameInputRef.current.getValue?.call( null, ), } = {}) => { const organizationPrefix = buildOrganizationPrefix(organizationName); organizationPrefixInputRef.current.setValue?.call( null, organizationPrefix, ); return organizationPrefix; }, [], ); const populateHostNameInput = useCallback( ({ organizationPrefix = organizationPrefixInputRef.current.getValue?.call( null, ), hostNumber = hostNumberInputRef.current.getValue?.call(null), domainName = domainNameInputRef.current.getValue?.call(null), } = {}) => { const hostName = buildHostName({ organizationPrefix, hostNumber, domainName, }); hostNameInputRef.current.setValue?.call(null, hostName); return hostName; }, [], ); const isOrganizationPrefixPrereqFilled = useCallback( () => isEmpty([organizationNameInputRef.current.getValue?.call(null)], { not: true, }), [], ); const isHostNamePrereqFilled = useCallback( () => isEmpty( [ organizationPrefixInputRef.current.getValue?.call(null), hostNumberInputRef.current.getValue?.call(null), domainNameInputRef.current.getValue?.call(null), ], { not: true, }, ), [], ); const populateOrganizationPrefixInputOnBlur: OutlinedInputWithLabelOnBlur = useCallback(() => { if (organizationPrefixInputRef.current.getIsChangedByUser?.call(null)) { setIsShowOrganizationPrefixSuggest( isOrganizationPrefixPrereqFilled(), ); } else { populateOrganizationPrefixInput(); } }, [isOrganizationPrefixPrereqFilled, populateOrganizationPrefixInput]); const populateHostNameInputOnBlur: OutlinedInputWithLabelOnBlur = useCallback(() => { if (hostNameInputRef.current.getIsChangedByUser?.call(null)) { setIsShowHostNameSuggest(isHostNamePrereqFilled()); } else { populateHostNameInput(); } }, [isHostNamePrereqFilled, populateHostNameInput]); const handleOrganizationPrefixSuggest = useCallback(() => { const organizationPrefix = populateOrganizationPrefixInput(); if (!hostNameInputRef.current.getIsChangedByUser?.call(null)) { populateHostNameInput({ organizationPrefix }); } }, [populateHostNameInput, populateOrganizationPrefixInput]); const handlerHostNameSuggest = useCallback(() => { populateHostNameInput(); }, [populateHostNameInput]); const buildHelpMessage = useCallback( (text: string) => (previous?: string) => previous === text ? undefined : text, [], ); useImperativeHandle(ref, () => ({ get: () => ({ adminPassword: adminPasswordInputRef.current.getValue?.call(null), organizationName: organizationNameInputRef.current.getValue?.call(null), organizationPrefix: organizationPrefixInputRef.current.getValue?.call(null), domainName: domainNameInputRef.current.getValue?.call(null), hostNumber: hostNumberInputRef.current.getValue?.call(null), hostName: hostNameInputRef.current.getValue?.call(null), }), })); return ( { setHelpText( buildHelpMessage( 'Name of the organization that maintains this Anvil! system. You can enter anything that makes sense to you.', ), ); }} /> } ref={organizationNameInputRef} /> :first-child': { flexGrow: 1, }, }} > ), inputProps: { maxLength: MAX_ORGANIZATION_PREFIX_LENGTH, sx: { minWidth: '2.5em', }, }, onBlur: populateHostNameInputOnBlur, }} label="Prefix" onChange={() => { setIsShowOrganizationPrefixSuggest( isOrganizationPrefixPrereqFilled(), ); }} onHelp={() => { setHelpText( buildHelpMessage( "Alphanumberic short-form of the organization name. It's used as the prefix for host names.", ), ); }} /> } ref={organizationPrefixInputRef} /> { setHelpText( buildHelpMessage( "Number or count of this striker; this should be '1' for the first striker, '2' for the second striker, and such.", ), ); }} /> } ref={hostNumberInputRef} valueType="number" /> { setHelpText( buildHelpMessage( "Domain name for this striker. It's also the default domain used when creating new install manifests.", ), ); }} /> } ref={domainNameInputRef} /> ), }} label="Host name" onChange={() => { setIsShowHostNameSuggest(isHostNamePrereqFilled()); }} onHelp={() => { setHelpText( buildHelpMessage( "Host name for this striker. It's usually a good idea to use the auto-generated value.", ), ); }} /> } ref={hostNameInputRef} /> * > *': { width: '100%', }, }} > { setHelpText( buildHelpMessage( "Password use to login to this Striker and connect to its database. Don't provide an used password here because it'll be stored as plaintext.", ), ); }} /> } ref={adminPasswordInputRef} /> } ref={confirmAdminPasswordInputRef} /> {helpMessage && ( { setHelpText(undefined); }} > {helpMessage} )} ); }, ); GeneralInitForm.displayName = 'GeneralInitForm'; export type { GeneralInitFormForwardRefContent }; export default GeneralInitForm;