mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
For #[26070](https://github.com/fleetdm/fleet/issues/26070) This adds the UI for enabling a manual agent install for a bootstrap package. This includes: **The new form option for enabling manual agent install of a bootstrap package**  **disabling adding install software and run script options when user has enabled manual agent install**   **improvements to the setup experience content styling. I've created a `SetupExperienceContentContainer` component to centralise the styles for the content of these sub sections.** **updates to the preview sections copy and replacing the gifs with videos** - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [ ] Added/updated automated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com>
186 lines
5.7 KiB
TypeScript
186 lines
5.7 KiB
TypeScript
import React, { useContext, useState } from "react";
|
|
import { useQuery } from "react-query";
|
|
import { AxiosResponse } from "axios";
|
|
|
|
import { IBootstrapPackageMetadata } from "interfaces/mdm";
|
|
import { IApiError } from "interfaces/errors";
|
|
import { IConfig } from "interfaces/config";
|
|
import { API_NO_TEAM_ID, ITeamConfig } from "interfaces/team";
|
|
import mdmAPI from "services/entities/mdm";
|
|
import configAPI from "services/entities/config";
|
|
import teamsAPI, { ILoadTeamResponse } from "services/entities/teams";
|
|
import { NotificationContext } from "context/notification";
|
|
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
|
|
|
|
import Spinner from "components/Spinner";
|
|
import SectionHeader from "components/SectionHeader";
|
|
|
|
import BootstrapPackagePreview from "./components/BootstrapPackagePreview";
|
|
import PackageUploader from "./components/BootstrapPackageUploader";
|
|
import UploadedPackageView from "./components/UploadedPackageView";
|
|
import DeleteBootstrapPackageModal from "./components/DeleteBootstrapPackageModal";
|
|
import SetupExperienceContentContainer from "../../components/SetupExperienceContentContainer";
|
|
import BootstrapAdvancedOptions from "./components/BootstrapAdvancedOptions";
|
|
|
|
const baseClass = "bootstrap-package";
|
|
|
|
export const getManualAgentInstallSetting = (
|
|
currentTeamId: number,
|
|
globalConfig?: IConfig,
|
|
teamConfig?: ITeamConfig
|
|
) => {
|
|
if (currentTeamId === API_NO_TEAM_ID) {
|
|
return globalConfig?.mdm.macos_setup.manual_agent_install || false;
|
|
}
|
|
return teamConfig?.mdm?.macos_setup.manual_agent_install || false;
|
|
};
|
|
|
|
interface IBootstrapPackageProps {
|
|
currentTeamId: number;
|
|
}
|
|
|
|
const BootstrapPackage = ({ currentTeamId }: IBootstrapPackageProps) => {
|
|
const { renderFlash } = useContext(NotificationContext);
|
|
const [
|
|
selectedManualAgentInstall,
|
|
setSelectedManualAgentInstall,
|
|
] = useState<boolean>(false);
|
|
const [
|
|
showDeleteBootstrapPackageModal,
|
|
setShowDeleteBootstrapPackageModal,
|
|
] = useState(false);
|
|
|
|
const {
|
|
isLoading: isLoadingGlobalConfig,
|
|
refetch: refetchGlobalConfig,
|
|
} = useQuery<IConfig, Error>(
|
|
["config", currentTeamId],
|
|
() => configAPI.loadAll(),
|
|
{
|
|
...DEFAULT_USE_QUERY_OPTIONS,
|
|
enabled: currentTeamId === API_NO_TEAM_ID,
|
|
onSuccess: (data) => {
|
|
setSelectedManualAgentInstall(
|
|
getManualAgentInstallSetting(currentTeamId, data)
|
|
);
|
|
},
|
|
}
|
|
);
|
|
|
|
const {
|
|
isLoading: isLoadingTeamConfig,
|
|
refetch: refetchTeamConfig,
|
|
} = useQuery<ILoadTeamResponse, Error, ITeamConfig>(
|
|
["team", currentTeamId],
|
|
() => teamsAPI.load(currentTeamId),
|
|
{
|
|
...DEFAULT_USE_QUERY_OPTIONS,
|
|
enabled: currentTeamId !== API_NO_TEAM_ID,
|
|
select: (res) => res.team,
|
|
onSuccess: (data) => {
|
|
setSelectedManualAgentInstall(
|
|
getManualAgentInstallSetting(currentTeamId, undefined, data)
|
|
);
|
|
},
|
|
}
|
|
);
|
|
|
|
const {
|
|
data: bootstrapMetadata,
|
|
isLoading,
|
|
error,
|
|
refetch: refretchBootstrapMetadata,
|
|
} = useQuery<
|
|
IBootstrapPackageMetadata,
|
|
AxiosResponse<IApiError>,
|
|
IBootstrapPackageMetadata
|
|
>(
|
|
["bootstrap-metadata", currentTeamId],
|
|
() => mdmAPI.getBootstrapPackageMetadata(currentTeamId),
|
|
{
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
cacheTime: 0,
|
|
}
|
|
);
|
|
|
|
const onUpload = () => {
|
|
refretchBootstrapMetadata();
|
|
};
|
|
|
|
const onDelete = async () => {
|
|
try {
|
|
await mdmAPI.deleteBootstrapPackage(currentTeamId);
|
|
await mdmAPI.updateSetupExperienceSettings({
|
|
team_id: currentTeamId,
|
|
manual_agent_install: false,
|
|
});
|
|
renderFlash("success", "Successfully deleted!");
|
|
} catch {
|
|
renderFlash("error", "Couldn't delete. Please try again.");
|
|
} finally {
|
|
setShowDeleteBootstrapPackageModal(false);
|
|
refretchBootstrapMetadata();
|
|
if (currentTeamId !== API_NO_TEAM_ID) {
|
|
refetchTeamConfig();
|
|
} else {
|
|
refetchGlobalConfig();
|
|
}
|
|
}
|
|
};
|
|
|
|
// we are relying on the API to tell us this resource does not exist to
|
|
// determine if the user has uploaded a bootstrap package.
|
|
const noPackageUploaded =
|
|
(error && error.status === 404) || !bootstrapMetadata;
|
|
|
|
const renderBootstrapView = () => {
|
|
const bootstrapPackageView = noPackageUploaded ? (
|
|
<PackageUploader currentTeamId={currentTeamId} onUpload={onUpload} />
|
|
) : (
|
|
<UploadedPackageView
|
|
bootstrapPackage={bootstrapMetadata}
|
|
currentTeamId={currentTeamId}
|
|
onDelete={() => setShowDeleteBootstrapPackageModal(true)}
|
|
/>
|
|
);
|
|
|
|
return (
|
|
<SetupExperienceContentContainer className={`${baseClass}__content`}>
|
|
<div className={`${baseClass}__uploader-container`}>
|
|
{bootstrapPackageView}
|
|
<BootstrapAdvancedOptions
|
|
currentTeamId={currentTeamId}
|
|
enableInstallManually={!noPackageUploaded}
|
|
selectManualAgentInstall={selectedManualAgentInstall}
|
|
onChange={(manualAgentInstall) => {
|
|
setSelectedManualAgentInstall(manualAgentInstall);
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className={`${baseClass}__preview-container`}>
|
|
<BootstrapPackagePreview />
|
|
</div>
|
|
</SetupExperienceContentContainer>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<section className={baseClass}>
|
|
<SectionHeader title="Bootstrap package" />
|
|
{isLoading || isLoadingGlobalConfig || isLoadingTeamConfig ? (
|
|
<Spinner />
|
|
) : (
|
|
renderBootstrapView()
|
|
)}
|
|
{showDeleteBootstrapPackageModal && (
|
|
<DeleteBootstrapPackageModal
|
|
onDelete={onDelete}
|
|
onCancel={() => setShowDeleteBootstrapPackageModal(false)}
|
|
/>
|
|
)}
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default BootstrapPackage;
|