export function CreateClusterInput()

in webview-ui/src/CreateCluster/CreateClusterInput.tsx [28:234]


export function CreateClusterInput(props: CreateClusterInputProps) {
    const [existingResourceGroup, setExistingResourceGroup] = useState<Validatable<ResourceGroup | null>>(unset());
    const [name, setName] = useState<Validatable<string>>(unset());
    const [isNewResourceGroupDialogShown, setIsNewResourceGroupDialogShown] = useState(false);
    const [newResourceGroupName, setNewResourceGroupName] = useState<string | null>(null);
    const [presetSelected, setPresetSelected] = useState<PresetType>(PresetType.Automatic);
    const [selectedResourceGroup, setSelectedResourceGroup] = useState<string>("");
    const [location, setLocation] = useState<Validatable<string>>(unset());

    const newResourceGroup = newResourceGroupName
        ? {
              name: newResourceGroupName,
              location: isValid(location) ? location.value : "",
          }
        : null;
    const allResourceGroups = newResourceGroup ? [newResourceGroup, ...props.resourceGroups] : props.resourceGroups;

    function handleCreateResourceGroupDialogCancel() {
        setIsNewResourceGroupDialogShown(false);
    }

    function handleCreateResourceGroupDialogAccept(groupName: string) {
        setIsNewResourceGroupDialogShown(false);
        setExistingResourceGroup(valid(null));
        setNewResourceGroupName(groupName);
        setSelectedResourceGroup(groupName);
    }

    function handlePresetSelection(presetSelected: PresetType) {
        setPresetSelected(presetSelected);
    }

    function handleValidationAndIndex(value: string) {
        handleExistingResourceGroupChange(value);
        setSelectedResourceGroup(value);
    }

    function handleExistingResourceGroupChange(value: string) {
        const resourceGroup = value ? allResourceGroups.find((group) => group.name === value) : null;
        const validatable = resourceGroup ? valid(resourceGroup) : invalid(null, "Resource Group is required.");
        setExistingResourceGroup(validatable);
    }

    const handleCreateNewRG = (event: MouseEvent<HTMLButtonElement>) => {
        setIsNewResourceGroupDialogShown(true);
        event.preventDefault();
        event.stopPropagation();
    };

    function getValidatedName(name: string): Validatable<string> {
        if (!name) return invalid(name, "Cluster name must be at least 1 character long.");
        if (name.length > 63) return invalid(name, "Cluster name must be at most 63 characters long.");
        if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$/.test(name)) {
            return invalid(
                name,
                "The only allowed characters are letters, numbers, dashes, and underscore. The first and last character must be a letter or a number.",
            );
        }

        return valid(name);
    }

    function handleNameChange(e: FormEvent<HTMLInputElement>) {
        const name = e.currentTarget.value;
        const validated = getValidatedName(name);
        setName(validated);
    }

    function handleLocationChange(value: string) {
        const location = value ? props.locations.find((loc) => loc === value) : null;
        const validated = location ? valid(location) : missing<string>("Location is required.");
        setLocation(validated);
    }

    function validate(): Maybe<CreateClusterParams> {
        if (!isValid(location)) return nothing();
        let resourceGroupName: string;
        let isNewResourceGroup: boolean;
        if (isValid(existingResourceGroup) && existingResourceGroup.value !== null) {
            resourceGroupName = existingResourceGroup.value.name;
            isNewResourceGroup = false;
        } else if (newResourceGroupName) {
            resourceGroupName = newResourceGroupName;
            isNewResourceGroup = true;
        } else {
            return nothing();
        }
        if (!isValid(name)) return nothing();

        const parameters: CreateClusterParams = {
            isNewResourceGroup,
            resourceGroupName,
            location: location.value,
            name: name.value,
            preset: presetSelected,
        };

        return just(parameters);
    }

    function handleSubmit(e: FormEvent) {
        e.preventDefault();
        const parameters = validate();
        if (isNothing(parameters)) return;
        props.vscode.postCreateClusterRequest(parameters.value);
        props.eventHandlers.onSetCreating({ parameters: parameters.value });
    }

    return (
        <>
            <form className={styles.createForm} onSubmit={handleSubmit}>
                <div className={styles.inputContainer}>
                    <CreateClusterPresetInput onPresetSelected={handlePresetSelection}></CreateClusterPresetInput>
                    <label htmlFor="cluster-details" className={styles.clusterDetailsLabel}>
                        Cluster details
                    </label>

                    <label htmlFor="existing-resource-group-dropdown" className={styles.label}>
                        Resource Group*
                    </label>
                    <CustomDropdown
                        id="existing-resource-group-dropdown"
                        value={selectedResourceGroup}
                        onChange={handleValidationAndIndex}
                        disabled={false}
                        aria-label="Select a resource group"
                    >
                        <CustomDropdownOption value="" label="Select" />
                        {allResourceGroups.length > 0 ? (
                            allResourceGroups.map((group) => (
                                <CustomDropdownOption
                                    key={group.name}
                                    value={group.name}
                                    label={`${group === newResourceGroup ? "(New) " : ""}${group.name}`}
                                />
                            ))
                        ) : (
                            <CustomDropdownOption value="" label="No resource groups available" />
                        )}
                    </CustomDropdown>

                    <button className={styles.sideControl} onClick={handleCreateNewRG}>
                        Create New
                    </button>
                    {hasMessage(existingResourceGroup) && (
                        <span className={styles.validationMessage}>
                            <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                            {existingResourceGroup.message}
                        </span>
                    )}

                    <label htmlFor="name-input" className={styles.label}>
                        Cluster Name*
                    </label>
                    <input
                        type="text"
                        id="name-input"
                        value={isValueSet(name) ? name.value : ""}
                        className={`${styles.longControl} ${styles.validatable}`}
                        onBlur={handleNameChange}
                        onInput={handleNameChange}
                    />
                    {hasMessage(name) && (
                        <span className={styles.validationMessage}>
                            <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                            {name.message}
                        </span>
                    )}

                    <label htmlFor="location-dropdown" className={styles.label}>
                        Region*
                    </label>
                    <CustomDropdown
                        id="location-dropdown"
                        value={isValueSet(location) ? location.value : ""}
                        onChange={handleLocationChange}
                        disabled={false}
                    >
                        <CustomDropdownOption value="" label="Select" />
                        {props.locations.map((location) => (
                            <CustomDropdownOption key={location} value={location} label={location} />
                        ))}
                    </CustomDropdown>
                    {hasMessage(location) && (
                        <span className={styles.validationMessage}>
                            <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                            {location.message}
                        </span>
                    )}
                </div>

                <div className={styles.buttonContainer}>
                    <button type="submit">Create</button>
                </div>
            </form>

            {isNewResourceGroupDialogShown && (
                <CreateResourceGroupDialog
                    isShown={isNewResourceGroupDialogShown}
                    locations={props.locations}
                    onCancel={handleCreateResourceGroupDialogCancel}
                    onAccept={handleCreateResourceGroupDialogAccept}
                />
            )}
        </>
    );
}