From 81c732f34ddd0ab64535a42269622da418463f70 Mon Sep 17 00:00:00 2001 From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:05:43 -0700 Subject: [PATCH] Macos settings hosts filter (#10385) ## Addresses #9597 Screenshot 2023-03-09 at 2 03 58 PM ## Notes The UI for "No teams" filtered state will be implemented in the **Frontend** portion of #10409 ## Checklist - [x] Manual QA - [x] Updated testing inventory or added tests - [x] Change file --------- Co-authored-by: Jacob Shandling --- changes/9567-macos_settings-hosts-filter | 3 + .../hosts/ManageHostsPage/ManageHostsPage.tsx | 65 ++++++++++++++++--- .../pages/hosts/ManageHostsPage/_styles.scss | 17 +++++ .../pages/hosts/ManageHostsPage/constants.ts | 18 +++++ frontend/services/entities/hosts.ts | 13 ++++ 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 changes/9567-macos_settings-hosts-filter 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,