export function DraftDockerfile()

in webview-ui/src/Draft/DraftDockerfile/DraftDockerfile.tsx [27:255]


export function DraftDockerfile(initialState: InitialState) {
    const { state, eventHandlers } = useStateManagement(stateUpdater, initialState, vscode);

    function handleChooseLocationClick() {
        vscode.postPickLocationRequest({
            defaultPath: state.workspaceConfig.fullPath,
            type: "directory",
            title: "Location to save Dockerfile",
            buttonLabel: "Select",
        });
    }

    function handleLanguageChange(language: LanguageInfo | null) {
        const validated = language === null ? missing<LanguageInfo>("Language is required.") : valid(language);
        eventHandlers.onSetSelectedLanguage(validated);
    }

    function handleLanguageVersionChange(version: string | null) {
        const validated = getValidatedLanguageVersion();
        eventHandlers.onSetSelectedLanguageVersion(validated);
        if (isValid(state.selectedLanguage) && isValid(validated)) {
            vscode.postGetLanguageVersionInfoRequest({
                language: state.selectedLanguage.value.name,
                version: validated.value,
            });
        }

        function getValidatedLanguageVersion(): Validatable<string> {
            if (version === null || version.length === 0) {
                return missing("Language version is required.");
            }

            return valid(version);
        }
    }

    function handlePortChange(e: ChangeEvent) {
        const elem = e.currentTarget as HTMLInputElement;
        const port = parseInt(elem.value);
        const validated = getValidatedPort(port);
        eventHandlers.onSetSelectedPort(validated);

        function getValidatedPort(port: number): Validatable<number> {
            if (Number.isNaN(port)) {
                return invalid(port, "Port must be a number.");
            }
            if (port < 1 || port > 65535) {
                return invalid(port, "Port number must be between 1 and 65535.");
            }

            return valid(port);
        }
    }

    function validate(): Maybe<CreateParams> {
        if (!isValid(state.selectedLocation)) return nothing();
        if (!isValid(state.selectedLanguage)) return nothing();
        if (!isValid(state.selectedLanguageVersion)) return nothing();
        if (state.isBuilderImageRequired && !isValid(state.builderImageTag)) return nothing();
        if (!isValid(state.runtimeImageTag)) return nothing();
        if (!isValid(state.selectedPort)) return nothing();

        return just({
            language: state.selectedLanguage.value.name,
            builderImageTag: orDefault(state.builderImageTag, null),
            runtimeImageTag: state.runtimeImageTag.value,
            port: state.selectedPort.value,
            location: state.selectedLocation.value,
        });
    }

    function handleFormSubmit(e: FormEvent) {
        e.preventDefault();
        const createParams = validate();
        if (isNothing(createParams)) {
            return;
        }

        eventHandlers.onSetCreating();
        vscode.postCreateDockerfileRequest(createParams.value);
    }

    function handleDraftDeploymentClick(e: MouseEvent) {
        e.preventDefault();
        vscode.postLaunchDraftDeployment({
            initialTargetPort: orDefault(state.selectedPort, null),
            initialLocation: state.selectedLocation.value,
        });
    }

    function handleDraftWorkflowClick(e: MouseEvent) {
        e.preventDefault();
        vscode.postLaunchDraftWorkflow({
            initialDockerfileLocation: state.selectedLocation.value,
        });
    }

    const locationTooltipMessage = "The folder where the Dockerfile will be saved.";

    const selectedLanguage = state.selectedLanguage;
    const languageVersionLabel =
        (state.selectedLanguage.hasValue && state.selectedLanguage.value.versionDescription) || "Language version";

    const languageVersionTooltipMessage =
        "The language version will be used to determine the builder and runtime image tags in the Dockerfile.";

    return (
        <form className={styles.wrapper} onSubmit={handleFormSubmit}>
            <h2>Automated Deployments: Draft a Dockerfile</h2>
            <p>
                To automatically containerize the app, please define the application environment, the port to expose the
                app, and the directory of the app source code to build.
            </p>

            <fieldset className={styles.inputContainer} disabled={state.status !== "Editing"}>
                <label htmlFor="location-input" className={styles.label}>
                    Location
                    <span className={"tooltip-holder"} data-tooltip-text={locationTooltipMessage}>
                        <i className={`${styles.inlineIcon} codicon codicon-info`} />
                    </span>
                </label>
                <input
                    type="text"
                    id="location-input"
                    readOnly
                    value={`.${state.workspaceConfig.pathSeparator}${state.selectedLocation.value}`}
                    className={styles.control}
                />
                <div className={styles.controlSupplement}>
                    <button className="choose-location-button" onClick={handleChooseLocationClick}>
                        <span className={styles.iconButton}>
                            <FontAwesomeIcon icon={faFolder} />
                            &nbsp;Choose location
                        </span>
                    </button>
                </div>
                {hasMessage(state.selectedLocation) && (
                    <span className={styles.validationMessage}>
                        <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                        {state.selectedLocation.message}
                    </span>
                )}

                <label htmlFor="language-input" className={styles.label}>
                    Programming language *
                </label>
                <ResourceSelector<LanguageInfo>
                    id="language-input"
                    className={styles.control}
                    resources={state.supportedLanguages}
                    selectedItem={toNullable(selectedLanguage)}
                    valueGetter={(l) => l.name}
                    labelGetter={(l) => l.displayName}
                    onSelect={handleLanguageChange}
                />
                {hasMessage(selectedLanguage) && (
                    <span className={styles.validationMessage}>
                        <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                        {selectedLanguage.message}
                    </span>
                )}

                {isValueSet(selectedLanguage) && (
                    <>
                        <label htmlFor="version-input" className={styles.label}>
                            {languageVersionLabel} *
                            <span className={"tooltip-holder"} data-tooltip-text={languageVersionTooltipMessage}>
                                <i className={`${styles.inlineIcon} codicon codicon-info`} />
                            </span>
                        </label>
                        <TextWithDropdown
                            id="version-input"
                            className={styles.control}
                            getAddItemText={(text) => `Use "${text}"`}
                            items={selectedLanguage.value.exampleVersions}
                            selectedItem={toNullable(state.selectedLanguageVersion)}
                            onSelect={handleLanguageVersionChange}
                        />
                        {hasMessage(state.selectedLanguageVersion) && (
                            <span className={styles.validationMessage}>
                                <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                                {state.selectedLanguageVersion.message}
                            </span>
                        )}
                        {state.isBuilderImageRequired && hasMessage(state.builderImageTag) && (
                            <span className={styles.validationMessage}>
                                <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                                {state.builderImageTag.message}
                            </span>
                        )}
                        {hasMessage(state.runtimeImageTag) && (
                            <span className={styles.validationMessage}>
                                <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                                {state.runtimeImageTag.message}
                            </span>
                        )}
                    </>
                )}

                <label htmlFor="port-input" className={styles.label}>
                    Application port *
                </label>
                <input
                    type="number"
                    id="port-input"
                    className={styles.control}
                    value={orDefault(state.selectedPort, "")}
                    onInput={handlePortChange}
                />
                {hasMessage(state.selectedPort) && (
                    <span className={styles.validationMessage}>
                        <FontAwesomeIcon className={styles.errorIndicator} icon={faTimesCircle} />
                        {state.selectedPort.message}
                    </span>
                )}
            </fieldset>

            <div className={styles.buttonContainer}>
                {state.status !== "Created" && (
                    <button type="submit" disabled={state.status !== "Editing" || isNothing(validate())}>
                        Create
                    </button>
                )}

                {state.existingFiles.map((path, i) => (
                    <button key={i} className="secondary-button" onClick={() => vscode.postOpenFileRequest(path)}>
                        Open {path}
                    </button>
                ))}