UI: Windows setup experience > install software (#32934)

## For #32541
_This cannot be end-to-end tested until associated
[server](https://github.com/fleetdm/fleet/issues/32542) and
[agent](https://github.com/fleetdm/fleet/issues/32544) work is complete_
- Include windows as a valid platform with all logic for setup
experience > software install

IT Admin flow:

![ezgif-26a18af23eb78b](https://github.com/user-attachments/assets/da294e8d-6992-41fa-b1f5-9e7b9500b8b7)

- [x] Changes file added for user-visible changes in `changes/`
- [ ] QA'd all new/changed functionality manually

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling 2025-09-16 10:12:25 -07:00 committed by GitHub
parent ba4c51f627
commit da98679a3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 65 additions and 26 deletions

View file

@ -0,0 +1 @@
* Implement setup experience software installation capabilities for Windows hosts

View file

@ -0,0 +1,25 @@
import {
ListEntitiesResponseCommon,
ListEntitiesResponsePaginationCommon,
} from "services/entities/common";
const DEFAULT_PAGINATION_RESPONSE: ListEntitiesResponsePaginationCommon = {
has_next_results: false,
has_previous_results: false,
};
export const createMockPaginationResponse = (
overrides?: Partial<ListEntitiesResponsePaginationCommon>
): typeof DEFAULT_PAGINATION_RESPONSE => {
return { ...DEFAULT_PAGINATION_RESPONSE, ...overrides };
};
const DEFAULT_LIST_ENTITIES_RESPONSE_COMMON_MOCK: ListEntitiesResponseCommon = {
meta: createMockPaginationResponse(),
count: 1,
};
export const createMockListEntitiesResponseCommon = (
overrides?: Partial<ListEntitiesResponseCommon>
): ListEntitiesResponseCommon => {
return { ...DEFAULT_LIST_ENTITIES_RESPONSE_COMMON_MOCK, ...overrides };
};

View file

@ -18,7 +18,9 @@ import {
ISoftwareVersionResponse,
} from "services/entities/software";
import { IOSVersionsResponse } from "../services/entities/operating_systems";
import { IGetSetupExperienceSoftwareResponse } from "../services/entities/mdm";
import { IOperatingSystemVersion } from "../interfaces/operating_system";
import { createMockListEntitiesResponseCommon } from "./commonMock";
const DEFAULT_SOFTWARE_MOCK: ISoftware = {
hosts_count: 1,
@ -227,6 +229,7 @@ const DEFAULT_SOFTWARE_PACKAGE_MOCK: ISoftwarePackage = {
hash_sha256: "abcd1234",
labels_include_any: null,
labels_exclude_any: null,
install_during_setup: undefined,
};
export const createMockSoftwarePackage = (
@ -335,3 +338,15 @@ export const createMockSoftwareInstallResult = (
...overrides,
};
};
const DEFAULT_SETUP_EXPERIENCE_SOFTWARE_MOCK: IGetSetupExperienceSoftwareResponse = {
...createMockListEntitiesResponseCommon(),
counts_updated_at: null,
software_titles: [createMockSoftwareTitle()],
};
export const createMockSetupExperienceSoftware = (
overrides?: Partial<IGetSetupExperienceSoftwareResponse>
): IGetSetupExperienceSoftwareResponse => {
return { ...DEFAULT_SETUP_EXPERIENCE_SOFTWARE_MOCK, ...overrides };
};

View file

@ -19,7 +19,6 @@ import DataError from "components/DataError";
import Spinner from "components/Spinner";
import TabNav from "components/TabNav";
import TabText from "components/TabText";
import CustomLink from "components/CustomLink";
import TurnOnMdmMessage from "components/TurnOnMdmMessage";
import InstallSoftwarePreview from "./components/InstallSoftwarePreview";
@ -55,7 +54,7 @@ const InstallSoftware = ({
const {
data: softwareTitles,
isLoading,
isLoading: isLoadingSoftwareTitles,
isError,
refetch: refetchSoftwareTitles,
} = useQuery<
@ -71,7 +70,6 @@ const InstallSoftware = ({
per_page: PER_PAGE_SIZE,
}),
{
enabled: selectedPlatform !== "windows", // remove next iteration
...DEFAULT_USE_QUERY_OPTIONS,
select: (res) => res.software_titles,
}
@ -110,18 +108,11 @@ const InstallSoftware = ({
);
const renderTabContent = (platform: SetupExperiencePlatform) => {
if (platform === "windows") {
return (
<div className={`${baseClass}__windows`}>
<b>Windows setup experience is coming soon.</b>
<p>
Need to customize setup for Windows users?{" "}
<CustomLink url={SUPPORT_LINK} text="Let us know" newTab />
</p>
</div>
);
}
if (isLoading || isLoadingGlobalConfig || isLoadingTeamConfig) {
if (
isLoadingSoftwareTitles ||
isLoadingGlobalConfig ||
isLoadingTeamConfig
) {
return <Spinner />;
}
@ -133,9 +124,15 @@ const InstallSoftware = ({
const appleMdmAndAbmEnabled =
globalConfig?.mdm.enabled_and_configured &&
globalConfig?.mdm.apple_bm_enabled_and_configured;
const turnOnAppleMdm = platform === "macos" && !appleMdmAndAbmEnabled;
// TODO - incorporate Windows MDM condition when implementing Windows software install on setup
if (platform === "macos" && !appleMdmAndAbmEnabled) {
const turnOnWindowsMdm =
platform === "windows" &&
!globalConfig?.mdm.windows_enabled_and_configured;
const turnOnMdm = turnOnAppleMdm || turnOnWindowsMdm;
if (turnOnMdm) {
return (
<TurnOnMdmMessage
header="Additional configuration required"

View file

@ -1,9 +1,4 @@
.install-software {
&__windows {
@include tab-empty-state;
max-width: none;
padding: 64px 170px;
}
.tab-nav {
margin-top: -20px;
}

View file

@ -315,9 +315,9 @@ const DeviceUserPage = ({
} = dupResponse || {};
const isPremiumTier = license?.tier === "premium";
const isAppleHost = isAppleDevice(host?.platform);
const isSetupExperienceSoftwareEnabledPlatform = isLinuxLike(
host?.platform || ""
); // TODO - add windows with next iteration
const isSetupExperienceSoftwareEnabledPlatform =
isLinuxLike(host?.platform || "") || host?.platform === "windows";
const checkForSetupExperienceSoftware =
isSetupExperienceSoftwareEnabledPlatform && isPremiumTier;

View file

@ -1,5 +1,5 @@
// TODO - apply broadly
interface ListEntitiesResponsePaginationCommon {
export interface ListEntitiesResponsePaginationCommon {
has_next_results: boolean;
has_previous_results: boolean;
}

View file

@ -15,6 +15,12 @@ import sendRequest from "services";
import endpoints from "utilities/endpoints";
import { buildQueryStringFromParams } from "utilities/url";
import {
createMockSetupExperienceSoftware,
createMockSoftwarePackage,
createMockSoftwareTitle,
} from "__mocks__/softwareMock";
import { ISoftwareTitlesResponse } from "./software";
import { PaginationParams } from "./common";