mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
relates to #19869 > NOTE: API integration will be included in a separate PR This adds the UI updates to support the new VPP feature on the software pages. This includes the software titles page and software titles details page. The new UI includes: **Add Vpp apps tab in Add software modal:**  **Various updates to the SoftwareIcon component to support icons from an external source:**  **Various updates to the SoftwarePackageCard compont to support app store apps.**  - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Added/updated testss: - [x] Manual QA for all new/changed functionality
250 lines
7.4 KiB
TypeScript
250 lines
7.4 KiB
TypeScript
import React, { useContext, useState } from "react";
|
|
|
|
import { NotificationContext } from "context/notification";
|
|
import { getFileDetails } from "utilities/file/fileUtils";
|
|
import getInstallScript from "utilities/software_install_scripts";
|
|
|
|
import Button from "components/buttons/Button";
|
|
import Checkbox from "components/forms/fields/Checkbox";
|
|
import Editor from "components/Editor";
|
|
import {
|
|
FileUploader,
|
|
FileDetails,
|
|
} from "components/FileUploader/FileUploader";
|
|
import Spinner from "components/Spinner";
|
|
import TooltipWrapper from "components/TooltipWrapper";
|
|
|
|
import AddSoftwareAdvancedOptions from "../AddSoftwareAdvancedOptions";
|
|
|
|
import { generateFormValidation } from "./helpers";
|
|
|
|
export 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>
|
|
);
|
|
};
|
|
|
|
export interface IAddSoftwareFormData {
|
|
software: File | null;
|
|
installScript: string;
|
|
preInstallCondition?: string;
|
|
postInstallScript?: string;
|
|
selfService: boolean;
|
|
}
|
|
|
|
export interface IFormValidation {
|
|
isValid: boolean;
|
|
software: { isValid: boolean };
|
|
preInstallCondition?: { isValid: boolean; message?: string };
|
|
postInstallScript?: { isValid: boolean; message?: string };
|
|
selfService?: { isValid: boolean };
|
|
}
|
|
|
|
interface IAddSoftwareFormProps {
|
|
isUploading: boolean;
|
|
onCancel: () => void;
|
|
onSubmit: (formData: IAddSoftwareFormData) => void;
|
|
}
|
|
|
|
const AddSoftwareForm = ({
|
|
isUploading,
|
|
onCancel,
|
|
onSubmit,
|
|
}: IAddSoftwareFormProps) => {
|
|
const { renderFlash } = useContext(NotificationContext);
|
|
|
|
const [showPreInstallCondition, setShowPreInstallCondition] = useState(false);
|
|
const [showPostInstallScript, setShowPostInstallScript] = useState(false);
|
|
const [formData, setFormData] = useState<IAddSoftwareFormData>({
|
|
software: null,
|
|
installScript: "",
|
|
preInstallCondition: undefined,
|
|
postInstallScript: undefined,
|
|
selfService: false,
|
|
});
|
|
const [formValidation, setFormValidation] = useState<IFormValidation>({
|
|
isValid: false,
|
|
software: { isValid: false },
|
|
});
|
|
|
|
const onFileUpload = (files: FileList | null) => {
|
|
if (files && files.length > 0) {
|
|
const file = files[0];
|
|
|
|
let installScript: string;
|
|
try {
|
|
installScript = getInstallScript(file.name);
|
|
} catch (e) {
|
|
renderFlash("error", `${e}`);
|
|
return;
|
|
}
|
|
|
|
const newData = {
|
|
...formData,
|
|
software: file,
|
|
installScript,
|
|
};
|
|
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 onToggleSelfServiceCheckbox = (value: boolean) => {
|
|
const newData = { ...formData, selfService: 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 && (
|
|
<Editor
|
|
wrapEnabled
|
|
maxLines={10}
|
|
name="install-script"
|
|
onChange={onChangeInstallScript}
|
|
value={formData.installScript}
|
|
helpText="Fleet will run this command on hosts to install software."
|
|
label="Install script"
|
|
labelTooltip={
|
|
<>
|
|
For security agents, add the script provided by the vendor.
|
|
<br />
|
|
In custom scripts, you can use the $INSTALLER_PATH variable to
|
|
point to the installer.
|
|
</>
|
|
}
|
|
/>
|
|
)}
|
|
<Checkbox
|
|
value={formData.selfService}
|
|
onChange={onToggleSelfServiceCheckbox}
|
|
>
|
|
<TooltipWrapper
|
|
tipContent={
|
|
<>
|
|
End users can install from{" "}
|
|
<b>Fleet Desktop {">"} Self-service</b>.
|
|
</>
|
|
}
|
|
>
|
|
Self-service
|
|
</TooltipWrapper>
|
|
</Checkbox>
|
|
<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;
|