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
This commit is contained in:
Gabriel Hernandez
2023-05-05 14:44:05 +01:00
committed by GitHub
parent d9979a3cf8
commit 60f55fbbe9
8 changed files with 74 additions and 61 deletions
@@ -0,0 +1 @@
- change macOS settings UI to always show the profile status aggregate data.
+3 -5
View File
@@ -22,6 +22,8 @@ export const MDM_ENROLLMENT_STATUS = {
export type MdmEnrollmentStatus = keyof typeof MDM_ENROLLMENT_STATUS;
export type ProfileSummaryResponse = Record<MdmProfileStatus, number>;
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";
@@ -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<MdmProfileStatus, number>;
interface AggregateMacSettingsIndicatorsProps {
teamId: number;
aggregateProfileStatusData: ProfileSummaryResponse;
}
const AggregateMacSettingsIndicators = ({
teamId,
aggregateProfileStatusData,
}: AggregateMacSettingsIndicatorsProps) => {
const {
data: aggregateProfileStatusesResponse,
} = useQuery<ProfileSummaryResponse>(
["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 (
<div className="aggregate-mac-settings-indicator">
@@ -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<ProfileSummaryResponse>(
["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 = ({
<p className={`${baseClass}__description`}>
Remotely enforce settings on macOS hosts assigned to this team.
</p>
{profiles && <AggregateMacSettingsIndicators teamId={teamId} />}
{aggregateProfileStatusData && (
<AggregateMacSettingsIndicators
teamId={teamId}
aggregateProfileStatusData={aggregateProfileStatusData}
/>
)}
<SideNav
className={`${baseClass}__side-nav`}
navItems={MAC_OS_SETTINGS_NAV_ITEMS.map((navItem) => ({
@@ -67,9 +79,7 @@ const MacOSSettings = ({
<CurrentCard
key={teamId}
currentTeamId={teamId}
profiles={profiles}
onProfileUpload={refectchProfiles}
onProfileDelete={refectchProfiles}
onMutation={refetchAggregateProfileStatus}
/>
}
/>
@@ -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<IApiError>;
@@ -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", "Couldnt delete. Please try again.");
@@ -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<ILoadTeamResponse, Error, ITeamConfig>(
["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();
@@ -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();
@@ -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";