From 60f55fbbe9f10edac032f5821ecf6ecd9dc21faf Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Fri, 5 May 2023 14:44:05 +0100 Subject: [PATCH] always show profile status aggregate UI on macOS Settings page (#11524) relates to #11450 This will show the profile status aggregate UI at all times when on the macOS settings page. This is a change from showing it conditionally. This also cleans up where some of the requests occur to move it closer to where it is needed and changing the `MdmProfileStatus` enum to a union. - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Manual QA for all new/changed functionality --- ...50-always-show-profile-status-aggregate-ui | 1 + frontend/interfaces/mdm.ts | 8 ++-- .../AggregateMacSettingsIndicators.tsx | 32 ++++++---------- .../MacOSSettings/MacOSSettings.tsx | 38 ++++++++++++------- .../cards/CustomSettings/CustomSettings.tsx | 31 +++++++++------ .../cards/DiskEncryption/DiskEncryption.tsx | 13 +++++-- .../MacSettingStatusCell.tests.tsx | 6 +-- .../MacSettingsIndicator.tsx | 6 +-- 8 files changed, 74 insertions(+), 61 deletions(-) create mode 100644 changes/issue-11450-always-show-profile-status-aggregate-ui diff --git a/changes/issue-11450-always-show-profile-status-aggregate-ui b/changes/issue-11450-always-show-profile-status-aggregate-ui new file mode 100644 index 0000000000..b3f3fc5158 --- /dev/null +++ b/changes/issue-11450-always-show-profile-status-aggregate-ui @@ -0,0 +1 @@ +- change macOS settings UI to always show the profile status aggregate data. diff --git a/frontend/interfaces/mdm.ts b/frontend/interfaces/mdm.ts index 849b2326d0..2bbed67d61 100644 --- a/frontend/interfaces/mdm.ts +++ b/frontend/interfaces/mdm.ts @@ -22,6 +22,8 @@ export const MDM_ENROLLMENT_STATUS = { export type MdmEnrollmentStatus = keyof typeof MDM_ENROLLMENT_STATUS; +export type ProfileSummaryResponse = Record; + export interface IMdmStatusCardData { status: MdmEnrollmentStatus; hosts: number; @@ -68,11 +70,7 @@ export interface IMdmProfilesResponse { profiles: IMdmProfile[] | null; } -export enum MdmProfileStatus { - VERIFYING = "verifying", - PENDING = "pending", - FAILED = "failed", -} +export type MdmProfileStatus = "verifying" | "pending" | "failed"; export type MacMdmProfileOperationType = "remove" | "install"; diff --git a/frontend/pages/ManageControlsPage/MacOSSettings/AggregateMacSettingsIndicators/AggregateMacSettingsIndicators.tsx b/frontend/pages/ManageControlsPage/MacOSSettings/AggregateMacSettingsIndicators/AggregateMacSettingsIndicators.tsx index 904bd2eedc..f376b5be8c 100644 --- a/frontend/pages/ManageControlsPage/MacOSSettings/AggregateMacSettingsIndicators/AggregateMacSettingsIndicators.tsx +++ b/frontend/pages/ManageControlsPage/MacOSSettings/AggregateMacSettingsIndicators/AggregateMacSettingsIndicators.tsx @@ -1,11 +1,11 @@ -import { IconNames } from "components/icons"; -import { MdmProfileStatus } from "interfaces/mdm"; -import MacSettingsIndicator from "pages/hosts/details/MacSettingsIndicator"; import React from "react"; -import { useQuery } from "react-query"; + import paths from "router/paths"; -import mdmAPI from "services/entities/mdm"; import { buildQueryStringFromParams } from "utilities/url"; +import { MdmProfileStatus, ProfileSummaryResponse } from "interfaces/mdm"; +import MacSettingsIndicator from "pages/hosts/details/MacSettingsIndicator"; + +import { IconNames } from "components/icons"; const baseClass = "aggregate-mac-settings-indicators"; @@ -18,21 +18,21 @@ interface IAggregateDisplayOption { const AGGREGATE_STATUS_DISPLAY_OPTIONS: IAggregateDisplayOption[] = [ { - value: MdmProfileStatus.VERIFYING, + value: "verifying", text: "Verifying", iconName: "success-partial", tooltipText: "Hosts that told Fleet all settings are enforced. Fleet is verifying.", }, { - value: MdmProfileStatus.PENDING, + value: "pending", text: "Pending", iconName: "pending-partial", tooltipText: "Hosts that will have settings enforced when the hosts come online.", }, { - value: MdmProfileStatus.FAILED, + value: "failed", text: "Failed", iconName: "error", tooltipText: @@ -40,28 +40,18 @@ const AGGREGATE_STATUS_DISPLAY_OPTIONS: IAggregateDisplayOption[] = [ }, ]; -type ProfileSummaryResponse = Record; - interface AggregateMacSettingsIndicatorsProps { teamId: number; + aggregateProfileStatusData: ProfileSummaryResponse; } const AggregateMacSettingsIndicators = ({ teamId, + aggregateProfileStatusData, }: AggregateMacSettingsIndicatorsProps) => { - const { - data: aggregateProfileStatusesResponse, - } = useQuery( - ["aggregateProfileStatuses", teamId], - () => mdmAPI.getAggregateProfileStatuses(teamId), - { refetchOnWindowFocus: false } - ); - - if (!aggregateProfileStatusesResponse) return null; - const indicators = AGGREGATE_STATUS_DISPLAY_OPTIONS.map((status) => { const { value, text, iconName, tooltipText } = status; - const count = aggregateProfileStatusesResponse[value]; + const count = aggregateProfileStatusData[value]; return (
diff --git a/frontend/pages/ManageControlsPage/MacOSSettings/MacOSSettings.tsx b/frontend/pages/ManageControlsPage/MacOSSettings/MacOSSettings.tsx index 9d1dc77eff..77f05a6258 100644 --- a/frontend/pages/ManageControlsPage/MacOSSettings/MacOSSettings.tsx +++ b/frontend/pages/ManageControlsPage/MacOSSettings/MacOSSettings.tsx @@ -1,10 +1,14 @@ -import React, { useContext } from "react"; +import React, { useContext, useState } from "react"; import { Params } from "react-router/lib/Router"; import { AppContext } from "context/app"; import SideNav from "pages/admin/components/SideNav"; import { useQuery } from "react-query"; -import { IMdmProfile, IMdmProfilesResponse } from "interfaces/mdm"; +import { + IMdmProfile, + IMdmProfilesResponse, + ProfileSummaryResponse, +} from "interfaces/mdm"; import { API_NO_TEAM_ID, APP_CONTEXT_NO_TEAM_ID } from "interfaces/team"; import mdmAPI from "services/entities/mdm"; @@ -33,14 +37,17 @@ const MacOSSettings = ({ ? API_NO_TEAM_ID // coerce undefined and -1 to 0 for 'No team' : currentTeam.id; - const { data: profiles, refetch: refectchProfiles } = useQuery< - IMdmProfilesResponse, - unknown, - IMdmProfile[] | null - >(["profiles", teamId], () => mdmAPI.getProfiles(teamId), { - select: (data) => data.profiles, - refetchOnWindowFocus: false, - }); + const { + data: aggregateProfileStatusData, + refetch: refetchAggregateProfileStatus, + } = useQuery( + ["aggregateProfileStatuses", teamId], + () => mdmAPI.getAggregateProfileStatuses(teamId), + { + refetchOnWindowFocus: false, + retry: false, + } + ); const DEFAULT_SETTINGS_SECTION = MAC_OS_SETTINGS_NAV_ITEMS[0]; @@ -55,7 +62,12 @@ const MacOSSettings = ({

Remotely enforce settings on macOS hosts assigned to this team.

- {profiles && } + {aggregateProfileStatusData && ( + + )} ({ @@ -67,9 +79,7 @@ const MacOSSettings = ({ } /> diff --git a/frontend/pages/ManageControlsPage/MacOSSettings/cards/CustomSettings/CustomSettings.tsx b/frontend/pages/ManageControlsPage/MacOSSettings/cards/CustomSettings/CustomSettings.tsx index 6eb6bc5002..bf27f50674 100644 --- a/frontend/pages/ManageControlsPage/MacOSSettings/cards/CustomSettings/CustomSettings.tsx +++ b/frontend/pages/ManageControlsPage/MacOSSettings/cards/CustomSettings/CustomSettings.tsx @@ -1,8 +1,9 @@ import React, { useContext, useRef, useState } from "react"; +import { useQuery } from "react-query"; import { AxiosResponse } from "axios"; import { IApiError } from "interfaces/errors"; -import { IMdmProfile } from "interfaces/mdm"; +import { IMdmProfile, IMdmProfilesResponse } from "interfaces/mdm"; import mdmAPI from "services/entities/mdm"; import { AppContext } from "context/app"; import { NotificationContext } from "context/notification"; @@ -20,18 +21,15 @@ import ProfileListHeading from "./components/ProfileListHeading"; const baseClass = "custom-settings"; interface ICustomSettingsProps { - profiles: IMdmProfile[]; - onProfileUpload: () => void; - onProfileDelete: () => void; + currentTeamId: number; + onMutation: () => void; } const CustomSettings = ({ - profiles, - onProfileUpload, - onProfileDelete, + currentTeamId, + onMutation, }: ICustomSettingsProps) => { const { renderFlash } = useContext(NotificationContext); - const { currentTeam } = useContext(AppContext); const [showDeleteProfileModal, setShowDeleteProfileModal] = useState(false); const [showLoading, setShowLoading] = useState(false); @@ -43,6 +41,15 @@ const CustomSettings = ({ setShowDeleteProfileModal(true); }; + const { data: profiles, refetch: refetchProfiles } = useQuery< + IMdmProfilesResponse, + unknown, + IMdmProfile[] | null + >(["profiles", currentTeamId], () => mdmAPI.getProfiles(currentTeamId), { + select: (data) => data.profiles, + refetchOnWindowFocus: false, + }); + const onFileUpload = async (files: FileList | null) => { setShowLoading(true); @@ -63,8 +70,9 @@ const CustomSettings = ({ } try { - await mdmAPI.uploadProfile(file, currentTeam?.id); - onProfileUpload(); + await mdmAPI.uploadProfile(file, currentTeamId); + refetchProfiles(); + onMutation(); renderFlash("success", "Successfully uploaded!"); } catch (e) { const error = e as AxiosResponse; @@ -83,7 +91,8 @@ const CustomSettings = ({ const onDeleteProfile = async (profileId: number) => { try { await mdmAPI.deleteProfile(profileId); - onProfileDelete(); + refetchProfiles(); + onMutation(); renderFlash("success", "Successfully deleted!"); } catch (e) { renderFlash("error", "Couldn’t delete. Please try again."); diff --git a/frontend/pages/ManageControlsPage/MacOSSettings/cards/DiskEncryption/DiskEncryption.tsx b/frontend/pages/ManageControlsPage/MacOSSettings/cards/DiskEncryption/DiskEncryption.tsx index 93e6c5b100..6edd4f35f5 100644 --- a/frontend/pages/ManageControlsPage/MacOSSettings/cards/DiskEncryption/DiskEncryption.tsx +++ b/frontend/pages/ManageControlsPage/MacOSSettings/cards/DiskEncryption/DiskEncryption.tsx @@ -18,10 +18,14 @@ import DiskEncryptionTable from "./components/DiskEncryptionTable"; const baseClass = "disk-encryption"; interface IDiskEncryptionProps { - currentTeamId?: number; + currentTeamId: number; + onMutation: () => void; } -const DiskEncryption = ({ currentTeamId }: IDiskEncryptionProps) => { +const DiskEncryption = ({ + currentTeamId, + onMutation, +}: IDiskEncryptionProps) => { const { isPremiumTier, config, setConfig } = useContext(AppContext); const { renderFlash } = useContext(NotificationContext); @@ -56,11 +60,11 @@ const DiskEncryption = ({ currentTeamId }: IDiskEncryptionProps) => { useQuery( ["team", currentTeamId], - () => teamsAPI.load(currentTeamId ?? 0), + () => teamsAPI.load(currentTeamId), { refetchOnWindowFocus: false, retry: false, - enabled: Boolean(currentTeamId), + enabled: currentTeamId !== 0, select: (res) => res.team, onSuccess: (res) => { const enableDiskEncryption = @@ -79,6 +83,7 @@ const DiskEncryption = ({ currentTeamId }: IDiskEncryptionProps) => { "success", "Successfully updated disk encryption enforcement!" ); + onMutation(); setShowAggregate(diskEncryptionEnabled); if (currentTeamId === 0) { getUpdatedAppConfig(); diff --git a/frontend/pages/hosts/details/MacSettingsModal/MacSettingsTable/MacSettingStatusCell/MacSettingStatusCell.tests.tsx b/frontend/pages/hosts/details/MacSettingsModal/MacSettingsTable/MacSettingStatusCell/MacSettingStatusCell.tests.tsx index 52d30d8d97..a40fd9d034 100644 --- a/frontend/pages/hosts/details/MacSettingsModal/MacSettingsTable/MacSettingStatusCell/MacSettingStatusCell.tests.tsx +++ b/frontend/pages/hosts/details/MacSettingsModal/MacSettingsTable/MacSettingStatusCell/MacSettingStatusCell.tests.tsx @@ -1,12 +1,12 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import { createCustomRenderer } from "test/test-utils"; -import { MacMdmProfileOperationType, MdmProfileStatus } from "interfaces/mdm"; +import { MacMdmProfileOperationType } from "interfaces/mdm"; import MacSettingStatusCell from "./MacSettingStatusCell"; describe("Mac setting status cell", () => { it("Correctly displays the status text of a profile", () => { - const status = MdmProfileStatus.VERIFYING; + const status = "verifying"; const operationType: MacMdmProfileOperationType = "install"; render( @@ -17,7 +17,7 @@ describe("Mac setting status cell", () => { }); it("Correctly displays the tooltip text for a profile", async () => { - const status = MdmProfileStatus.VERIFYING; + const status = "verifying"; const operationType: MacMdmProfileOperationType = "install"; const customRender = createCustomRenderer(); diff --git a/frontend/pages/hosts/details/cards/HostSummary/MacSettingsIndicator/MacSettingsIndicator.tsx b/frontend/pages/hosts/details/cards/HostSummary/MacSettingsIndicator/MacSettingsIndicator.tsx index cd9db6e8df..68b89546bc 100644 --- a/frontend/pages/hosts/details/cards/HostSummary/MacSettingsIndicator/MacSettingsIndicator.tsx +++ b/frontend/pages/hosts/details/cards/HostSummary/MacSettingsIndicator/MacSettingsIndicator.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactTooltip from "react-tooltip"; -import { IHostMacMdmProfile, MdmProfileStatus } from "interfaces/mdm"; +import { IHostMacMdmProfile } from "interfaces/mdm"; import Icon from "components/Icon"; import Button from "components/buttons/Button"; @@ -40,10 +40,10 @@ const getMacSettingsStatus = ( hostMacSettings?: IHostMacMdmProfile[] ): MacSettingsStatus => { const statuses = hostMacSettings?.map((setting) => setting.status); - if (statuses?.includes(MdmProfileStatus.FAILED)) { + if (statuses?.includes("failed")) { return "Failing"; } - if (statuses?.includes(MdmProfileStatus.PENDING)) { + if (statuses?.includes("pending")) { return "Pending"; } return "Verifying";