fleet/frontend/components/AddHostsModal/DownloadInstallers/DownloadInstallers.tsx

255 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { FunctionComponent, useState } from "react";
import {
IInstallerPlatform,
IInstallerType,
INSTALLER_PLATFORM_BY_TYPE,
INSTALLER_TYPE_BY_PLATFORM,
} from "interfaces/installer";
import ENDPOINTS from "utilities/endpoints";
import local from "utilities/local";
import URL_PREFIX from "router/url_prefix";
import installerAPI from "services/entities/installers";
import Button from "components/buttons/Button";
import Checkbox from "components/forms/fields/Checkbox";
import DataError from "components/DataError";
import Spinner from "components/Spinner";
import TooltipWrapper from "components/TooltipWrapper";
import Icon from "components/Icon";
import SuccessIcon from "./../../../../assets/images/icon-circle-check-blue-48x48@2x.png";
interface IDownloadInstallersProps {
enrollSecret: string;
onCancel: () => void;
}
interface IDownloadFormProps {
url: string;
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
token: string | null;
enrollSecret: string;
includeDesktop: boolean;
selectedInstaller: string | undefined;
isCheckingForInstaller: boolean;
isDownloadSuccess: boolean;
}
const baseClass = "download-installers";
const displayOrder = [
"macOS",
"Windows",
"Linux (RPM)",
"Linux (deb)",
] as const;
const displayIcon = (platform: IInstallerPlatform, isSelected: boolean) => {
switch (platform) {
case "Linux (RPM)":
case "Linux (deb)":
return (
<Icon
name="linux"
size="large"
color={isSelected ? "core-fleet-blue" : "core-fleet-black"}
/>
);
case "macOS":
return (
<Icon
name="darwin"
size="large"
color={isSelected ? "core-fleet-blue" : "core-fleet-black"}
/>
);
case "Windows":
return (
<Icon
name="windows"
size="large"
color={isSelected ? "core-fleet-blue" : "core-fleet-black"}
/>
);
default:
return null;
}
};
const DownloadForm: FunctionComponent<IDownloadFormProps> = ({
url,
onSubmit,
token,
enrollSecret,
includeDesktop,
selectedInstaller,
isCheckingForInstaller,
isDownloadSuccess,
}) => {
return (
<form
key="form"
method="POST"
action={url}
target="_self"
onSubmit={onSubmit}
>
<input type="hidden" name="token" value={token || ""} />
<input type="hidden" name="enroll_secret" value={enrollSecret} />
<input type="hidden" name="desktop" value={String(includeDesktop)} />
{!isDownloadSuccess && (
<Button
className={`${baseClass}__button--download`}
disabled={!selectedInstaller}
type="submit"
isLoading={isCheckingForInstaller}
>
Download installer
</Button>
)}
</form>
);
};
const DownloadInstallers = ({
enrollSecret,
onCancel,
}: IDownloadInstallersProps): JSX.Element => {
const [includeDesktop, setIncludeDesktop] = useState(true);
const [isDownloading, setIsDownloading] = useState(false);
const [isDownloadError, setIsDownloadError] = useState(false);
const [isDownloadSuccess, setIsDownloadSuccess] = useState(false);
const [selectedInstaller, setSelectedInstaller] = useState<
IInstallerType | undefined
>();
const path = `${ENDPOINTS.DOWNLOAD_INSTALLER}/${selectedInstaller}`;
const { origin } = global.window.location;
const url = `${origin}${URL_PREFIX}/api${path}`;
const token = local.getItem("auth_token");
const downloadInstaller = async (event: React.FormEvent<HTMLFormElement>) => {
if (!selectedInstaller) {
// do nothing
return;
}
// Prevent the submit behavior, as we want to control when the POST is
// actually performed.
event.preventDefault();
event.persist();
setIsDownloading(true);
try {
// First check if the installer exists, no need to save the result of
// this operation as any status other than 200 will throw an error
await installerAPI.checkInstallerExistence({
enrollSecret,
includeDesktop,
installerType: selectedInstaller,
});
(event.target as HTMLFormElement).submit();
setIsDownloadSuccess(true);
} catch (error) {
setIsDownloadError(true);
} finally {
setIsDownloading(false);
}
};
const onClickSelector = (type: IInstallerType) => {
if (isDownloading) {
// do nothing
return;
}
if (type === selectedInstaller) {
setSelectedInstaller(undefined);
return;
}
setSelectedInstaller(type);
};
const form = (
<DownloadForm
key="downloadForm"
url={url}
onSubmit={downloadInstaller}
token={token}
enrollSecret={enrollSecret}
includeDesktop={includeDesktop}
selectedInstaller={selectedInstaller}
isCheckingForInstaller={isDownloading}
isDownloadSuccess={isDownloadSuccess}
/>
);
if (isDownloadError) {
return (
<div className={`${baseClass}__error`}>
<DataError />
</div>
);
}
if (isDownloadSuccess) {
const installerPlatform =
(selectedInstaller &&
`${INSTALLER_PLATFORM_BY_TYPE[selectedInstaller]} `) ||
"";
return (
<div className={`${baseClass}__success`}>
<img src={SuccessIcon} alt="download successful" />
<h2>You&rsquo;re almost there</h2>
<p>{`Run the installer on a ${installerPlatform}laptop, workstation, or server to add it to Fleet.`}</p>
<Button onClick={onCancel}>Got it</Button>
{form}
</div>
);
}
return (
<div className={`${baseClass}`}>
<p>Which platform is your host running?</p>
<div className={`${baseClass}__select-installer`}>
{displayOrder.map((platform) => {
const installerType = INSTALLER_TYPE_BY_PLATFORM[platform];
const isSelected = selectedInstaller === installerType;
return (
<div
key={installerType}
className={`${baseClass}__selector ${
isSelected ? `${baseClass}__selector--selected` : ""
}`}
onClick={() => onClickSelector(installerType)}
>
<span>
{displayIcon(platform, isSelected)}
{platform}
</span>
</div>
);
})}
</div>
<Checkbox
name="include-fleet-desktop"
onChange={(value: boolean) => setIncludeDesktop(value)}
value={includeDesktop}
>
<>
Include&nbsp;
<TooltipWrapper
tipContent={
"<p>Include Fleet Desktop if yourre adding workstations.</p>"
}
>
Fleet Desktop
</TooltipWrapper>
</>
</Checkbox>
{form}
</div>
);
};
export default DownloadInstallers;