Add OS settings filter to UI (#14412)

This commit is contained in:
gillespi314 2023-10-12 10:57:58 -05:00 committed by GitHub
parent 719e761f28
commit 632e21ce55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 133 additions and 30 deletions

View file

@ -3,6 +3,7 @@ import React from "react";
import paths from "router/paths";
import { buildQueryStringFromParams } from "utilities/url";
import { MdmProfileStatus } from "interfaces/mdm";
import { HOSTS_QUERY_PARAMS } from "services/entities/hosts";
import { ProfileStatusSummaryResponse } from "services/entities/mdm";
import Spinner from "components/Spinner";
@ -31,12 +32,12 @@ const ProfileStatusCount = ({
hostCount,
tooltipText,
}: IProfileStatusCountProps) => {
const generateFilterHostsByStatusLink = () => {
return `${paths.MANAGE_HOSTS}?${buildQueryStringFromParams({
const linkHostsByStatus = `${paths.MANAGE_HOSTS}?${buildQueryStringFromParams(
{
team_id: teamId,
macos_settings: statusValue,
})}`;
};
[HOSTS_QUERY_PARAMS.OS_SETTINGS]: statusValue,
}
)}`;
return (
<div className={`${baseClass}__profile-status-count`}>
@ -47,7 +48,7 @@ const ProfileStatusCount = ({
layout="vertical"
valueClassName={`${baseClass}__status-indicator-value`}
/>
<a href={generateFilterHostsByStatusLink()}>{hostCount} hosts</a>
<a href={linkHostsByStatus}>{hostCount} hosts</a>
</div>
);
};

View file

@ -5,7 +5,7 @@ import {
IDiskEncryptionStatusAggregate,
IDiskEncryptionSummaryResponse,
} from "services/entities/mdm";
import { DISK_ENCRYPTION_QUERY_PARAM_NAME } from "services/entities/hosts";
import { HOSTS_QUERY_PARAMS } from "services/entities/hosts";
import TextCell from "components/TableContainer/DataTable/TextCell";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
@ -101,7 +101,7 @@ const defaultTableHeaders: IDataColumn[] = [
<ViewAllHostsLink
className="view-hosts-link"
queryParams={{
[DISK_ENCRYPTION_QUERY_PARAM_NAME]: original.status.value,
[HOSTS_QUERY_PARAMS.DISK_ENCRYPTION]: original.status.value,
team_id: original.teamId,
}}
/>
@ -134,7 +134,7 @@ const windowsTableHeader: IDataColumn[] = [
<ViewAllHostsLink
className="view-hosts-link"
queryParams={{
[DISK_ENCRYPTION_QUERY_PARAM_NAME]: original.status.value,
[HOSTS_QUERY_PARAMS.DISK_ENCRYPTION]: original.status.value,
team_id: original.teamId,
}}
/>

View file

@ -1,7 +1,7 @@
import React from "react";
import Icon from "components/Icon";
import { DISK_ENCRYPTION_QUERY_PARAM_NAME } from "services/entities/hosts";
import { HOSTS_QUERY_PARAMS } from "services/entities/hosts";
export const MANAGE_HOSTS_PAGE_FILTER_KEYS = [
"query",
@ -18,7 +18,8 @@ export const MANAGE_HOSTS_PAGE_FILTER_KEYS = [
"os_version",
"munki_issue_id",
"low_disk_space",
DISK_ENCRYPTION_QUERY_PARAM_NAME,
HOSTS_QUERY_PARAMS.OS_SETTINGS,
HOSTS_QUERY_PARAMS.DISK_ENCRYPTION,
"bootstrap_package",
] as const;
@ -81,7 +82,7 @@ export const getHostSelectStatuses = (isSandboxMode = false) => {
];
};
export const MAC_SETTINGS_FILTER_OPTIONS = [
export const OS_SETTINGS_FILTER_OPTIONS = [
{
disabled: false,
label: "Verified",

View file

@ -18,7 +18,7 @@ import labelsAPI, { ILabelsResponse } from "services/entities/labels";
import teamsAPI, { ILoadTeamsResponse } from "services/entities/teams";
import globalPoliciesAPI from "services/entities/global_policies";
import hostsAPI, {
DISK_ENCRYPTION_QUERY_PARAM_NAME,
HOSTS_QUERY_PARAMS as PARAMS,
ILoadHostsQueryKey,
ILoadHostsResponse,
ISortOption,
@ -50,7 +50,11 @@ import { IOperatingSystemVersion } from "interfaces/operating_system";
import { IPolicy, IStoredPolicyResponse } from "interfaces/policy";
import { ITeam } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";
import { DiskEncryptionStatus, BootstrapPackageStatus } from "interfaces/mdm";
import {
DiskEncryptionStatus,
BootstrapPackageStatus,
MdmProfileStatus,
} from "interfaces/mdm";
import sortUtils from "utilities/sort";
import {
@ -233,8 +237,9 @@ const ManageHostsPage = ({
? parseInt(queryParams.low_disk_space, 10)
: undefined;
const missingHosts = queryParams?.status === "missing";
const osSettingsStatus = queryParams?.[PARAMS.OS_SETTINGS];
const diskEncryptionStatus: DiskEncryptionStatus | undefined =
queryParams?.[DISK_ENCRYPTION_QUERY_PARAM_NAME];
queryParams?.[PARAMS.DISK_ENCRYPTION];
const bootstrapPackageStatus: BootstrapPackageStatus | undefined =
queryParams?.bootstrap_package;
@ -366,6 +371,7 @@ const ManageHostsPage = ({
page: tableQueryData ? tableQueryData.pageIndex : 0,
perPage: tableQueryData ? tableQueryData.pageSize : 50,
device_mapping: true,
osSettings: osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
macSettingsStatus,
@ -402,6 +408,7 @@ const ManageHostsPage = ({
osId,
osName,
osVersion,
osSettings: osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
macSettingsStatus,
@ -570,7 +577,24 @@ const ManageHostsPage = ({
routeParams,
queryParams: {
...queryParams,
[DISK_ENCRYPTION_QUERY_PARAM_NAME]: newStatus,
[PARAMS.DISK_ENCRYPTION]: newStatus,
page: 0, // resets page index
},
})
);
};
const handleChangeOsSettingsFilter = (newStatus: MdmProfileStatus) => {
handleResetPageIndex();
router.replace(
getNextLocationPath({
pathPrefix: PATHS.MANAGE_HOSTS,
routeTemplate,
routeParams,
queryParams: {
...queryParams,
[PARAMS.OS_SETTINGS]: newStatus,
page: 0, // resets page index
},
})
@ -767,9 +791,11 @@ const ManageHostsPage = ({
newQueryParams.os_id = osId;
newQueryParams.os_name = osName;
newQueryParams.os_version = osVersion;
} else if (osSettingsStatus) {
newQueryParams[PARAMS.OS_SETTINGS] = osSettingsStatus;
} else if (diskEncryptionStatus && isPremiumTier) {
// Premium feature only
newQueryParams[DISK_ENCRYPTION_QUERY_PARAM_NAME] = diskEncryptionStatus;
newQueryParams[PARAMS.DISK_ENCRYPTION] = diskEncryptionStatus;
} else if (bootstrapPackageStatus && isPremiumTier) {
newQueryParams.bootstrap_package = bootstrapPackageStatus;
}
@ -807,6 +833,7 @@ const ManageHostsPage = ({
router,
routeTemplate,
routeParams,
osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
]
@ -1539,6 +1566,7 @@ const ManageHostsPage = ({
softwareDetails: hostsData?.software || null,
mdmSolutionDetails:
hostsData?.mobile_device_management_solution || null,
osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
}}
@ -1547,6 +1575,7 @@ const ManageHostsPage = ({
handleClearRouteParam={handleClearRouteParam}
handleClearFilter={handleClearFilter}
onChangePoliciesFilter={handleChangePoliciesFilter}
onChangeOsSettingsFilter={handleChangeOsSettingsFilter}
onChangeDiskEncryptionStatusFilter={
handleChangeDiskEncryptionStatusFilter
}

View file

@ -11,12 +11,13 @@ import {
BootstrapPackageStatus,
IMdmSolution,
MDM_ENROLLMENT_STATUS,
MdmProfileStatus,
} from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
import { ISoftware } from "interfaces/software";
import { IPolicy } from "interfaces/policy";
import {
DISK_ENCRYPTION_QUERY_PARAM_NAME,
HOSTS_QUERY_PARAMS,
MacSettingsStatusQueryParam,
} from "services/entities/hosts";
@ -32,7 +33,7 @@ import Icon from "components/Icon/Icon";
import FilterPill from "../FilterPill";
import PoliciesFilter from "../PoliciesFilter";
import { MAC_SETTINGS_FILTER_OPTIONS } from "../../HostsPageConfig";
import { OS_SETTINGS_FILTER_OPTIONS } from "../../HostsPageConfig";
import DiskEncryptionStatusFilter from "../DiskEncryptionStatusFilter";
import BootstrapPackageStatusFilter from "../BootstrapPackageStatusFilter/BootstrapPackageStatusFilter";
@ -63,6 +64,7 @@ interface IHostsFilterBlockProps {
osVersions?: IOperatingSystemVersion[];
softwareDetails: ISoftware | null;
mdmSolutionDetails: IMdmSolution | null;
osSettingsStatus?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
bootstrapPackageStatus?: BootstrapPackageStatus;
};
@ -71,6 +73,7 @@ interface IHostsFilterBlockProps {
handleClearRouteParam: () => void;
handleClearFilter: (omitParams: string[]) => void;
onChangePoliciesFilter: (response: PolicyResponse) => void;
onChangeOsSettingsFilter: (newStatus: MdmProfileStatus) => void;
onChangeDiskEncryptionStatusFilter: (response: DiskEncryptionStatus) => void;
onChangeBootstrapPackageStatusFilter: (
response: BootstrapPackageStatus
@ -105,6 +108,7 @@ const HostsFilterBlock = ({
softwareDetails,
policy,
mdmSolutionDetails,
osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
},
@ -113,6 +117,7 @@ const HostsFilterBlock = ({
handleClearRouteParam,
handleClearFilter,
onChangePoliciesFilter,
onChangeOsSettingsFilter,
onChangeDiskEncryptionStatusFilter,
onChangeBootstrapPackageStatusFilter,
onChangeMacSettingsFilter,
@ -215,7 +220,7 @@ const HostsFilterBlock = ({
<Dropdown
value={macSettingsStatus}
className={`${baseClass}__macsettings-dropdown`}
options={MAC_SETTINGS_FILTER_OPTIONS}
options={OS_SETTINGS_FILTER_OPTIONS}
onChange={onChangeMacSettingsFilter}
/>
<FilterPill
@ -367,6 +372,24 @@ const HostsFilterBlock = ({
);
};
const renderOsSettingsBlock = () => {
const label = "OS settings";
return (
<>
<Dropdown
value={osSettingsStatus}
className={`${baseClass}__os_settings-dropdown`}
options={OS_SETTINGS_FILTER_OPTIONS}
onChange={onChangeOsSettingsFilter}
/>
<FilterPill
label={label}
onClear={() => handleClearFilter([HOSTS_QUERY_PARAMS.OS_SETTINGS])}
/>
</>
);
};
const renderDiskEncryptionStatusBlock = () => {
if (!diskEncryptionStatus) return null;
@ -378,7 +401,9 @@ const HostsFilterBlock = ({
/>
<FilterPill
label="OS settings: Disk encryption"
onClear={() => handleClearFilter([DISK_ENCRYPTION_QUERY_PARAM_NAME])}
onClear={() =>
handleClearFilter([HOSTS_QUERY_PARAMS.DISK_ENCRYPTION])
}
/>
</>
);
@ -414,6 +439,7 @@ const HostsFilterBlock = ({
osId ||
(osName && osVersion) ||
munkiIssueId ||
osSettingsStatus ||
diskEncryptionStatus ||
bootstrapPackageStatus
) {
@ -458,6 +484,9 @@ const HostsFilterBlock = ({
return renderMunkiIssueFilterBlock();
case !!lowDiskSpaceHosts:
return renderLowDiskSpaceFilterBlock();
case !!osSettingsStatus:
console.log("renderOsSettingsBlock");
return renderOsSettingsBlock();
case !!diskEncryptionStatus:
return renderDiskEncryptionStatusBlock();
case !!bootstrapPackageStatus:

View file

@ -13,6 +13,7 @@
}
// NOTE: Look more into this styling
&__os_settings-dropdown,
&__macsettings-dropdown {
width: 137px;

View file

@ -1,7 +1,11 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import sendRequest from "services";
import endpoints from "utilities/endpoints";
import { DiskEncryptionStatus, BootstrapPackageStatus } from "interfaces/mdm";
import {
DiskEncryptionStatus,
BootstrapPackageStatus,
MdmProfileStatus,
} from "interfaces/mdm";
import { HostStatus } from "interfaces/host";
import {
buildQueryStringFromParams,
@ -43,6 +47,7 @@ export interface IHostCountLoadOptions {
osId?: number;
osName?: string;
osVersion?: string;
osSettings?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
bootstrapPackageStatus?: BootstrapPackageStatus;
}
@ -67,12 +72,17 @@ export default {
const osId = options?.osId;
const osName = options?.osName;
const osVersion = options?.osVersion;
const osSettings = options?.osSettings;
const diskEncryptionStatus = options?.diskEncryptionStatus;
const bootstrapPackageStatus = options?.bootstrapPackageStatus;
const queryParams = {
query: globalFilter,
...reconcileMutuallyInclusiveHostParams({ teamId, macSettingsStatus }),
...reconcileMutuallyInclusiveHostParams({
teamId,
macSettingsStatus,
osSettings,
}),
...reconcileMutuallyExclusiveHostParams({
label,
policyId,
@ -85,6 +95,7 @@ export default {
osName,
osId,
osVersion,
osSettings,
diskEncryptionStatus,
bootstrapPackageStatus,
}),

View file

@ -14,6 +14,7 @@ import {
DiskEncryptionStatus,
BootstrapPackageStatus,
IMdmSolution,
MdmProfileStatus,
} from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
@ -32,7 +33,10 @@ export interface ILoadHostsResponse {
// the source of truth for the filter option names.
// there are used on many other pages but we define them here.
// TODO: add other filter options here.
export const DISK_ENCRYPTION_QUERY_PARAM_NAME = "os_settings_disk_encryption";
export const HOSTS_QUERY_PARAMS = {
OS_SETTINGS: "os_settings",
DISK_ENCRYPTION: "os_settings_disk_encryption",
} as const;
export interface ILoadHostsQueryKey extends ILoadHostsOptions {
scope: "hosts";
@ -62,6 +66,7 @@ export interface ILoadHostsOptions {
device_mapping?: boolean;
columns?: string;
visibleColumns?: string;
osSettings?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
bootstrapPackageStatus?: BootstrapPackageStatus;
}
@ -88,6 +93,7 @@ export interface IExportHostsOptions {
device_mapping?: boolean;
columns?: string;
visibleColumns?: string;
osSettings?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
}
@ -179,6 +185,7 @@ export default {
const visibleColumns = options?.visibleColumns;
const label = getLabelParam(selectedLabels);
const munkiIssueId = options?.munkiIssueId;
const osSettings = options?.osSettings;
const diskEncryptionStatus = options?.diskEncryptionStatus;
if (!sortBy.length) {
@ -189,7 +196,11 @@ export default {
order_key: sortBy[0].key,
order_direction: sortBy[0].direction,
query: globalFilter,
...reconcileMutuallyInclusiveHostParams({ teamId, macSettingsStatus }),
...reconcileMutuallyInclusiveHostParams({
teamId,
macSettingsStatus,
osSettings,
}),
...reconcileMutuallyExclusiveHostParams({
label,
policyId,
@ -199,6 +210,7 @@ export default {
munkiIssueId,
softwareId,
lowDiskSpaceHosts,
osSettings,
diskEncryptionStatus,
}),
status,
@ -233,6 +245,7 @@ export default {
device_mapping,
selectedLabels,
sortBy,
osSettings,
diskEncryptionStatus,
bootstrapPackageStatus,
}: ILoadHostsOptions): Promise<ILoadHostsResponse> => {
@ -250,6 +263,7 @@ export default {
...reconcileMutuallyInclusiveHostParams({
teamId,
macSettingsStatus,
osSettings,
}),
...reconcileMutuallyExclusiveHostParams({
label,
@ -264,6 +278,7 @@ export default {
osName,
osVersion,
diskEncryptionStatus,
osSettings,
bootstrapPackageStatus,
}),
};

View file

@ -1,8 +1,12 @@
import { isEmpty, reduce, omitBy, Dictionary } from "lodash";
import { DiskEncryptionStatus, BootstrapPackageStatus } from "interfaces/mdm";
import {
DISK_ENCRYPTION_QUERY_PARAM_NAME,
DiskEncryptionStatus,
BootstrapPackageStatus,
MdmProfileStatus,
} from "interfaces/mdm";
import {
HOSTS_QUERY_PARAMS,
MacSettingsStatusQueryParam,
} from "services/entities/hosts";
@ -14,6 +18,7 @@ type FilteredQueryParams = Record<string, FilteredQueryValues>;
interface IMutuallyInclusiveHostParams {
teamId?: number;
macSettingsStatus?: MacSettingsStatusQueryParam;
osSettings?: MdmProfileStatus;
}
interface IMutuallyExclusiveHostParams {
@ -28,6 +33,7 @@ interface IMutuallyExclusiveHostParams {
osId?: number;
osName?: string;
osVersion?: string;
osSettings?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
bootstrapPackageStatus?: BootstrapPackageStatus;
}
@ -70,11 +76,17 @@ export const buildQueryStringFromParams = (queryParams: QueryParams) => {
export const reconcileMutuallyInclusiveHostParams = ({
teamId,
macSettingsStatus,
}: IMutuallyInclusiveHostParams): Record<string, unknown> => {
osSettings,
}: IMutuallyInclusiveHostParams) => {
// ensure macos_settings filter is always applied in
// conjuction with a team_id, 0 (no teams) by default
const reconciled = { macos_settings: macSettingsStatus, team_id: teamId };
const reconciled: Record<string, unknown> = { team_id: teamId };
if (macSettingsStatus) {
reconciled.macos_settings = macSettingsStatus;
reconciled.team_id = teamId ?? 0;
}
if (osSettings) {
reconciled[HOSTS_QUERY_PARAMS.OS_SETTINGS] = osSettings;
reconciled.team_id = teamId ?? 0;
}
return reconciled;
@ -91,6 +103,7 @@ export const reconcileMutuallyExclusiveHostParams = ({
osId,
osName,
osVersion,
osSettings,
diskEncryptionStatus,
bootstrapPackageStatus,
}: IMutuallyExclusiveHostParams): Record<string, unknown> => {
@ -126,8 +139,11 @@ export const reconcileMutuallyExclusiveHostParams = ({
return { os_name: osName, os_version: osVersion };
case !!lowDiskSpaceHosts:
return { low_disk_space: lowDiskSpaceHosts };
case !!osSettings:
return { [HOSTS_QUERY_PARAMS.OS_SETTINGS]: osSettings };
case !!diskEncryptionStatus:
return { [DISK_ENCRYPTION_QUERY_PARAM_NAME]: diskEncryptionStatus };
return { [HOSTS_QUERY_PARAMS.DISK_ENCRYPTION]: diskEncryptionStatus };
case !!bootstrapPackageStatus:
return { bootstrap_package: bootstrapPackageStatus };
default: