diff --git a/changes/9567-macos_settings-hosts-filter b/changes/9567-macos_settings-hosts-filter
new file mode 100644
index 0000000000..b8d7622c37
--- /dev/null
+++ b/changes/9567-macos_settings-hosts-filter
@@ -0,0 +1,3 @@
+* Add ability to filter data under the Hosts tab by the aggregate status of hosts' MDM-managed macos
+settings. This filter is used when clicking Controls > macOS settings > "# hosts" under Latest,
+Pending, or Failing.
diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
index 25494ec571..e598a19cfa 100644
--- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
@@ -13,6 +13,7 @@ import globalPoliciesAPI from "services/entities/global_policies";
import hostsAPI, {
ILoadHostsOptions,
ISortOption,
+ MacSettingsStatusQueryParam,
} from "services/entities/hosts";
import hostCountAPI, {
IHostCountLoadOptions,
@@ -73,6 +74,7 @@ import {
DEFAULT_SORT_DIRECTION,
DEFAULT_PAGE_SIZE,
HOST_SELECT_STATUSES,
+ MAC_SETTINGS_FILTER_OPTIONS,
} from "./constants";
import { isAcceptableStatus, getNextLocationPath } from "./helpers";
import DeleteSecretModal from "../../../components/EnrollSecrets/DeleteSecretModal";
@@ -242,6 +244,7 @@ const ManageHostsPage = ({
const routeTemplate = route?.path ?? "";
const policyId = queryParams?.policy_id;
const policyResponse: PolicyResponse = queryParams?.policy_response;
+ const macSettingsStatus = queryParams?.macos_settings;
const softwareId =
queryParams?.software_id !== undefined
? parseInt(queryParams.software_id, 10)
@@ -421,25 +424,20 @@ const ManageHostsPage = ({
setIsHostsLoading(true);
options = {
...options,
- teamId: currentTeam?.id,
+ teamId: queryParams.team_id ? queryParams.team_id : currentTeam?.id,
};
- if (queryParams.team_id) {
- options.teamId = queryParams.team_id;
- }
-
try {
const {
hosts: returnedHosts,
software,
- mobile_device_management_solution,
- munki_issue,
+ mobile_device_management_solution: mdmSolution,
+ munki_issue: munkiIssue,
} = await hostsAPI.loadHosts(options);
setHosts(returnedHosts);
software && setSoftwareDetails(software);
- mobile_device_management_solution &&
- setMDMSolutionDetails(mobile_device_management_solution);
- munki_issue && setMunkiIssueDetails(munki_issue);
+ mdmSolution && setMDMSolutionDetails(mdmSolution);
+ munkiIssue && setMunkiIssueDetails(munkiIssue);
} catch (error) {
console.error(error);
setHasHostErrors(true);
@@ -514,6 +512,7 @@ const ManageHostsPage = ({
teamId: selectedTeam?.id,
policyId,
policyResponse,
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -632,6 +631,10 @@ const ManageHostsPage = ({
handleClearFilter(["os_id", "os_name", "os_version"]);
};
+ const handleClearMacSettingsStatusFilter = () => {
+ handleClearFilter(["macos_settings"]);
+ };
+
const handleClearSoftwareFilter = () => {
handleClearFilter(["software_id"]);
};
@@ -687,6 +690,21 @@ const ManageHostsPage = ({
);
};
+ const handleMacSettingsStatusDropdownChange = (
+ newMacSettingsStatus: MacSettingsStatusQueryParam
+ ) => {
+ handleResetPageIndex();
+
+ router.replace(
+ getNextLocationPath({
+ pathPrefix: PATHS.MANAGE_HOSTS,
+ routeTemplate,
+ routeParams,
+ queryParams: { ...queryParams, macos_settings: newMacSettingsStatus },
+ })
+ );
+ };
+
const onAddLabelClick = () => {
router.push(`${PATHS.NEW_LABEL}`);
};
@@ -764,6 +782,8 @@ const ManageHostsPage = ({
if (policyId && policyResponse) {
newQueryParams.policy_id = policyId;
newQueryParams.policy_response = policyResponse;
+ } else if (macSettingsStatus) {
+ newQueryParams.macos_settings = macSettingsStatus;
} else if (softwareId) {
newQueryParams.software_id = softwareId;
} else if (mdmId) {
@@ -801,6 +821,7 @@ const ManageHostsPage = ({
currentUser,
policyId,
queryParams,
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -989,6 +1010,7 @@ const ManageHostsPage = ({
teamId: currentTeam?.id,
policyId,
policyResponse,
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -1044,6 +1066,7 @@ const ManageHostsPage = ({
teamId: currentTeam?.id,
policyId,
policyResponse,
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -1168,6 +1191,24 @@ const ManageHostsPage = ({
>
);
+ const renderMacSettingsStatusFilterBlock = () => {
+ const label = "macOS settings";
+ return (
+ <>
+
+
+ >
+ );
+ };
+
const renderSoftwareFilterBlock = () => {
if (!softwareDetails) return null;
@@ -1464,6 +1505,7 @@ const ManageHostsPage = ({
teamId: currentTeam?.id,
policyId,
policyResponse,
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -1534,6 +1576,7 @@ const ManageHostsPage = ({
if (
showSelectedLabel ||
policyId ||
+ macSettingsStatus ||
softwareId ||
showSelectedLabel ||
mdmId ||
@@ -1570,6 +1613,8 @@ const ManageHostsPage = ({
return renderLabelFilterPill();
case !!policyId:
return renderPoliciesFilterBlock();
+ case !!macSettingsStatus:
+ return renderMacSettingsStatusFilterBlock();
case !!softwareId:
return renderSoftwareFilterBlock();
case !!mdmId:
diff --git a/frontend/pages/hosts/ManageHostsPage/_styles.scss b/frontend/pages/hosts/ManageHostsPage/_styles.scss
index cfb3746c25..1f1e6b8b8c 100644
--- a/frontend/pages/hosts/ManageHostsPage/_styles.scss
+++ b/frontend/pages/hosts/ManageHostsPage/_styles.scss
@@ -248,6 +248,23 @@
margin-left: $pad-small;
}
+ &__macsettings-dropdown {
+ width: 137px;
+
+ .Select-value {
+ display: flex;
+ align-items: center;
+
+ &::before {
+ position: relative;
+ content: url(../assets/images/icon-filter-v2-black-16x16@2x.png);
+ transform: scale(0.5);
+ left: -8px;
+ top: 4px;
+ }
+ }
+ }
+
&__labels-active-filter-wrap {
display: flex;
align-items: center;
diff --git a/frontend/pages/hosts/ManageHostsPage/constants.ts b/frontend/pages/hosts/ManageHostsPage/constants.ts
index 4b3bb27a33..69261f558e 100644
--- a/frontend/pages/hosts/ManageHostsPage/constants.ts
+++ b/frontend/pages/hosts/ManageHostsPage/constants.ts
@@ -36,3 +36,21 @@ export const HOST_SELECT_STATUSES = [
helpText: "Hosts added to Fleet in the last 24 hours.",
},
];
+
+export const MAC_SETTINGS_FILTER_OPTIONS = [
+ {
+ disabled: false,
+ label: "Latest",
+ value: "latest",
+ },
+ {
+ disabled: false,
+ label: "Pending",
+ value: "pending",
+ },
+ {
+ disabled: false,
+ label: "Failing",
+ value: "failing",
+ },
+];
diff --git a/frontend/services/entities/hosts.ts b/frontend/services/entities/hosts.ts
index ca509d8703..7ca91c01bd 100644
--- a/frontend/services/entities/hosts.ts
+++ b/frontend/services/entities/hosts.ts
@@ -14,6 +14,8 @@ export interface ISortOption {
direction: string;
}
+export type MacSettingsStatusQueryParam = "latest" | "pending" | "failing";
+
export interface ILoadHostsOptions {
page?: number;
perPage?: number;
@@ -23,6 +25,7 @@ export interface ILoadHostsOptions {
teamId?: number;
policyId?: number;
policyResponse?: string;
+ macSettingsStatus?: MacSettingsStatusQueryParam;
softwareId?: number;
status?: HostStatus;
mdmId?: number;
@@ -46,6 +49,7 @@ export interface IExportHostsOptions {
teamId?: number;
policyId?: number;
policyResponse?: string;
+ macSettingsStatus?: MacSettingsStatusQueryParam;
softwareId?: number;
status?: HostStatus;
mdmId?: number;
@@ -140,6 +144,7 @@ export default {
const policyId = options?.policyId;
const policyResponse = options?.policyResponse || "passing";
const softwareId = options?.softwareId;
+ const macSettingsStatus = options?.macSettingsStatus;
const status = options?.status;
const mdmId = options?.mdmId;
const mdmEnrollmentStatus = options?.mdmEnrollmentStatus;
@@ -186,6 +191,7 @@ export default {
teamId,
policyId,
policyResponse = "passing",
+ macSettingsStatus,
softwareId,
status,
mdmId,
@@ -202,6 +208,12 @@ export default {
const label = getLabel(selectedLabels);
const sortParams = getSortParams(sortBy);
+ // ensure macos_settings filter is always applied in
+ // conjuction with a team_id, 0 (no teams) by default
+ if (macSettingsStatus) {
+ teamId = teamId ?? 0;
+ }
+
const queryParams = {
page,
per_page: perPage,
@@ -211,6 +223,7 @@ export default {
order_key: sortParams.order_key,
order_direction: sortParams.order_direction,
status,
+ macos_settings: macSettingsStatus,
...reconcileMutuallyExclusiveHostParams({
label,
policyId,