2024-05-03 13:22:20 +00:00
|
|
|
import React, { useState } from "react";
|
|
|
|
|
|
2024-05-10 15:18:24 +00:00
|
|
|
import getInstallScript from "utilities/software_install_scripts";
|
|
|
|
|
|
2024-05-03 13:22:20 +00:00
|
|
|
import Spinner from "components/Spinner";
|
|
|
|
|
import Button from "components/buttons/Button";
|
|
|
|
|
import FileUploader from "components/FileUploader";
|
|
|
|
|
import Graphic from "components/Graphic";
|
2024-05-10 15:18:24 +00:00
|
|
|
import Editor from "components/Editor";
|
2024-05-03 13:22:20 +00:00
|
|
|
|
|
|
|
|
import AddSoftwareAdvancedOptions from "../AddSoftwareAdvancedOptions";
|
|
|
|
|
|
2024-05-10 15:18:24 +00:00
|
|
|
import { generateFormValidation, getFileDetails } from "./helpers";
|
2024-05-03 13:22:20 +00:00
|
|
|
|
|
|
|
|
const baseClass = "add-software-form";
|
|
|
|
|
|
|
|
|
|
const UploadingSoftware = () => {
|
|
|
|
|
return (
|
|
|
|
|
<div className={`${baseClass}__uploading-message`}>
|
|
|
|
|
<Spinner centered={false} />
|
|
|
|
|
<p>Uploading. It may take few minutes to finish.</p>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// TODO: if we reuse this one more time, we should consider moving this
|
|
|
|
|
// into FileUploader as a default preview. Currently we have this in
|
|
|
|
|
// AddProfileModal.tsx and here.
|
|
|
|
|
const FileDetails = ({
|
|
|
|
|
details: { name, platform },
|
|
|
|
|
}: {
|
|
|
|
|
details: {
|
|
|
|
|
name: string;
|
|
|
|
|
platform: string;
|
|
|
|
|
};
|
|
|
|
|
}) => (
|
|
|
|
|
<div className={`${baseClass}__selected-file`}>
|
|
|
|
|
<Graphic name="file-pkg" />
|
|
|
|
|
<div className={`${baseClass}__selected-file--details`}>
|
|
|
|
|
<div className={`${baseClass}__selected-file--details--name`}>{name}</div>
|
|
|
|
|
<div className={`${baseClass}__selected-file--details--platform`}>
|
|
|
|
|
{platform}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export interface IAddSoftwareFormData {
|
|
|
|
|
software: File | null;
|
|
|
|
|
installScript: string;
|
|
|
|
|
preInstallCondition?: string;
|
|
|
|
|
postInstallScript?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface IFormValidation {
|
|
|
|
|
isValid: boolean;
|
|
|
|
|
software: { isValid: boolean };
|
|
|
|
|
preInstallCondition?: { isValid: boolean; message?: string };
|
|
|
|
|
postInstallScript?: { isValid: boolean; message?: string };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IAddSoftwareFormProps {
|
|
|
|
|
isUploading: boolean;
|
|
|
|
|
onCancel: () => void;
|
|
|
|
|
onSubmit: (formData: IAddSoftwareFormData) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AddSoftwareForm = ({
|
|
|
|
|
isUploading,
|
|
|
|
|
onCancel,
|
|
|
|
|
onSubmit,
|
|
|
|
|
}: IAddSoftwareFormProps) => {
|
|
|
|
|
const [showPreInstallCondition, setShowPreInstallCondition] = useState(false);
|
|
|
|
|
const [showPostInstallScript, setShowPostInstallScript] = useState(false);
|
|
|
|
|
const [formData, setFormData] = useState<IAddSoftwareFormData>({
|
|
|
|
|
software: null,
|
|
|
|
|
installScript: "",
|
|
|
|
|
preInstallCondition: undefined,
|
|
|
|
|
postInstallScript: undefined,
|
|
|
|
|
});
|
|
|
|
|
const [formValidation, setFormValidation] = useState<IFormValidation>({
|
|
|
|
|
isValid: false,
|
|
|
|
|
software: { isValid: false },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const onFileUpload = (files: FileList | null) => {
|
|
|
|
|
if (files && files.length > 0) {
|
|
|
|
|
const file = files[0];
|
|
|
|
|
const newData = {
|
|
|
|
|
...formData,
|
|
|
|
|
software: file,
|
2024-05-10 15:18:24 +00:00
|
|
|
installScript: getInstallScript(file.name),
|
2024-05-03 13:22:20 +00:00
|
|
|
};
|
|
|
|
|
setFormData(newData);
|
|
|
|
|
setFormValidation(
|
|
|
|
|
generateFormValidation(
|
|
|
|
|
newData,
|
|
|
|
|
showPreInstallCondition,
|
|
|
|
|
showPostInstallScript
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onFormSubmit = (evt: React.FormEvent<HTMLFormElement>) => {
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
onSubmit(formData);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onTogglePreInstallConditionCheckbox = (value: boolean) => {
|
|
|
|
|
const newData = { ...formData, preInstallCondition: undefined };
|
|
|
|
|
setShowPreInstallCondition(value);
|
|
|
|
|
setFormData(newData);
|
|
|
|
|
setFormValidation(
|
|
|
|
|
generateFormValidation(newData, value, showPostInstallScript)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onTogglePostInstallScriptCheckbox = (value: boolean) => {
|
|
|
|
|
const newData = { ...formData, postInstallScript: undefined };
|
|
|
|
|
setShowPostInstallScript(value);
|
|
|
|
|
setFormData(newData);
|
|
|
|
|
setFormValidation(
|
|
|
|
|
generateFormValidation(newData, showPreInstallCondition, value)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onChangeInstallScript = (value: string) => {
|
|
|
|
|
setFormData({ ...formData, installScript: value });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onChangePreInstallCondition = (value?: string) => {
|
|
|
|
|
const newData = { ...formData, preInstallCondition: value };
|
|
|
|
|
setFormData(newData);
|
|
|
|
|
setFormValidation(
|
|
|
|
|
generateFormValidation(
|
|
|
|
|
newData,
|
|
|
|
|
showPreInstallCondition,
|
|
|
|
|
showPostInstallScript
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onChangePostInstallScript = (value?: string) => {
|
|
|
|
|
const newData = { ...formData, postInstallScript: value };
|
|
|
|
|
setFormData(newData);
|
|
|
|
|
setFormValidation(
|
|
|
|
|
generateFormValidation(
|
|
|
|
|
newData,
|
|
|
|
|
showPreInstallCondition,
|
|
|
|
|
showPostInstallScript
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isSubmitDisabled = !formValidation.isValid;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={baseClass}>
|
|
|
|
|
{isUploading ? (
|
|
|
|
|
<UploadingSoftware />
|
|
|
|
|
) : (
|
|
|
|
|
<form className={`${baseClass}__form`} onSubmit={onFormSubmit}>
|
|
|
|
|
<FileUploader
|
|
|
|
|
graphicName={"file-pkg"}
|
|
|
|
|
accept=".pkg,.msi,.exe,.deb"
|
|
|
|
|
message=".pkg, .msi, .exe, or .deb"
|
|
|
|
|
onFileUpload={onFileUpload}
|
|
|
|
|
buttonMessage="Choose file"
|
|
|
|
|
buttonType="link"
|
|
|
|
|
className={`${baseClass}__file-uploader`}
|
|
|
|
|
filePreview={
|
|
|
|
|
formData.software && (
|
|
|
|
|
<FileDetails details={getFileDetails(formData.software)} />
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
{formData.software && (
|
2024-05-10 15:18:24 +00:00
|
|
|
<Editor
|
|
|
|
|
wrapEnabled
|
|
|
|
|
maxLines={10}
|
|
|
|
|
name="install-script"
|
2024-05-03 13:22:20 +00:00
|
|
|
onChange={onChangeInstallScript}
|
2024-05-10 15:18:24 +00:00
|
|
|
value={formData.installScript}
|
2024-05-03 13:22:20 +00:00
|
|
|
helpText="Fleet will run this command on hosts to install software."
|
2024-05-10 15:18:24 +00:00
|
|
|
label="Install script"
|
|
|
|
|
labelTooltip="For security agents, add the script provided by the vendor."
|
2024-05-03 13:22:20 +00:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<AddSoftwareAdvancedOptions
|
|
|
|
|
errors={{
|
|
|
|
|
preInstallCondition: formValidation.preInstallCondition?.message,
|
|
|
|
|
postInstallScript: formValidation.postInstallScript?.message,
|
|
|
|
|
}}
|
|
|
|
|
showPreInstallCondition={showPreInstallCondition}
|
|
|
|
|
showPostInstallScript={showPostInstallScript}
|
|
|
|
|
preInstallCondition={formData.preInstallCondition}
|
|
|
|
|
postInstallScript={formData.postInstallScript}
|
|
|
|
|
onTogglePreInstallCondition={onTogglePreInstallConditionCheckbox}
|
|
|
|
|
onTogglePostInstallScript={onTogglePostInstallScriptCheckbox}
|
|
|
|
|
onChangePreInstallCondition={onChangePreInstallCondition}
|
|
|
|
|
onChangePostInstallScript={onChangePostInstallScript}
|
|
|
|
|
/>
|
|
|
|
|
<div className="modal-cta-wrap">
|
|
|
|
|
<Button type="submit" variant="brand" disabled={isSubmitDisabled}>
|
|
|
|
|
Add software
|
|
|
|
|
</Button>
|
|
|
|
|
<Button onClick={onCancel} variant="inverse">
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default AddSoftwareForm;
|