Feat UI add verifying status to mdm (#11311)

This commit is contained in:
Gabriel Hernandez 2023-04-26 19:31:38 +01:00 committed by GitHub
parent 4d1beef728
commit 4866bccb3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 289 additions and 203 deletions

View File

@ -0,0 +1 @@
- add verifying status for mdm profiles

View File

@ -9,10 +9,15 @@ import { COLORS } from "styles/var/colors";
const baseClass = "status-indicator-with-icon";
type Status = "success" | "pending" | "error";
export type IndicatorStatus =
| "success"
| "successPartial"
| "pending"
| "pendingPartial"
| "error";
interface IStatusIndicatorWithIconProps {
status: Status;
status: IndicatorStatus;
value: string;
tooltip?: {
tooltipText: string | JSX.Element;
@ -21,9 +26,11 @@ interface IStatusIndicatorWithIconProps {
className?: string;
}
const statusIconNameMapping: Record<Status, IconNames> = {
const statusIconNameMapping: Record<IndicatorStatus, IconNames> = {
success: "success",
successPartial: "success-partial",
pending: "pending",
pendingPartial: "pending-partial",
error: "error",
};

View File

@ -0,0 +1,20 @@
import React from "react";
const PendingPartial = () => {
return (
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="9" cy="9" r="8" stroke="#8B8FA2" strokeWidth="2" />
<circle cx="5.6665" cy="9" r="1" fill="#8B8FA2" />
<circle cx="8.6665" cy="9" r="1" fill="#8B8FA2" />
<circle cx="11.6665" cy="9" r="1" fill="#8B8FA2" />
</svg>
);
};
export default PendingPartial;

View File

@ -0,0 +1,24 @@
import React from "react";
const SuccessPartial = () => {
return (
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<ellipse cx="9" cy="9" rx="8" ry="8" stroke="#3DB67B" strokeWidth="2" />
<path
d="M6 10L8 12L12 6"
stroke="#3DB67B"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default SuccessPartial;

View File

@ -16,7 +16,6 @@ import EmptyTeams from "./EmptyTeams";
import ExternalLink from "./ExternalLink";
import Issue from "./Issue";
import Plus from "./Plus";
import Pending from "./Pending";
import PremiumFeature from "./PremiumFeature";
import LowDiskSpaceHosts from "./LowDiskSpaceHosts";
@ -37,8 +36,12 @@ import ApplePurple from "./ApplePurple";
import LinuxGreen from "./LinuxGreen";
import WindowsBlue from "./WindowsBlue";
import Error from "./Error";
// Status Icons
import Success from "./Success";
import SuccessPartial from "./SuccessPartial";
import Pending from "./Pending";
import PendingPartial from "./PendingPartial";
import Error from "./Error";
import Clipboard from "./Clipboard";
import Eye from "./Eye";
@ -79,9 +82,11 @@ export const ICON_MAP = {
clipboard: Clipboard,
eye: Eye,
pencil: Pencil,
pending: Pending,
trash: TrashCan,
success: Success,
"success-partial": SuccessPartial,
pending: Pending,
"pending-partial": PendingPartial,
error: Error,
darwin: Apple,
macOS: Apple,

View File

@ -7,7 +7,7 @@ import softwareInterface, { ISoftware } from "./software";
import hostQueryResult from "./campaign";
import queryStatsInterface, { IQueryStats } from "./query_stats";
import { ILicense, IDeviceGlobalConfig } from "./config";
import { IMacSettings, MdmEnrollmentStatus } from "./mdm";
import { IHostMacMdmProfile, MdmEnrollmentStatus } from "./mdm";
export default PropTypes.shape({
created_at: PropTypes.string,
@ -107,7 +107,7 @@ export interface IHostMdmData {
name?: string;
server_url: string;
id?: number;
profiles: IMacSettings;
profiles: IHostMacMdmProfile[];
macos_settings: IMdmMacOsSettings;
}

View File

@ -68,33 +68,38 @@ export interface IMdmProfilesResponse {
profiles: IMdmProfile[] | null;
}
export type MacMdmProfileStatus = "applied" | "pending" | "failed";
export enum MdmProfileStatus {
VERIFYING = "verifying",
PENDING = "pending",
FAILED = "failed",
}
export type MacMdmProfileOperationType = "remove" | "install";
export interface IHostMacMdmProfile {
profile_id: number;
name: string;
operation_type: MacMdmProfileOperationType;
status: MacMdmProfileStatus;
status: MdmProfileStatus;
detail: string;
}
export type IMacSettings = IHostMacMdmProfile[];
export type MacSettingsStatus = "Failing" | "Latest" | "Pending";
export interface IAggregateMacSettingsStatus {
latest: number;
pending: number;
failing: number;
}
export interface IDiskEncryptionStatusAggregate {
applied: number;
export interface IFileVaultSummaryResponse {
verifying: number;
action_required: number;
enforcing: number;
failed: number;
removing_enforcement: number;
}
export enum FileVaultProfileStatus {
VERIFYING = "verifying",
ACTION_REQUIRED = "action_required",
ENFORCING = "enforcing",
FAILED = "failed",
REMOVING_ENFORCEMENT = "removing_enforcement",
}
// TODO: update when we have API
export interface IMdmScript {
id: number;

View File

@ -1,4 +1,5 @@
import { IAggregateMacSettingsStatus } from "interfaces/mdm";
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";
@ -8,6 +9,39 @@ import { buildQueryStringFromParams } from "utilities/url";
const baseClass = "aggregate-mac-settings-indicators";
interface IAggregateDisplayOption {
value: MdmProfileStatus;
text: string;
iconName: IconNames;
tooltipText: string;
}
const AGGREGATE_STATUS_DISPLAY_OPTIONS: IAggregateDisplayOption[] = [
{
value: MdmProfileStatus.VERIFYING,
text: "Verifying",
iconName: "success-partial",
tooltipText:
"Hosts that told Fleet all settings are enforced. Fleet is verifying.",
},
{
value: MdmProfileStatus.PENDING,
text: "Pending",
iconName: "pending-partial",
tooltipText:
"Hosts that will have settings enforced when the hosts come online.",
},
{
value: MdmProfileStatus.FAILED,
text: "Failed",
iconName: "error",
tooltipText:
"Hosts that failed to apply settings. Click on a host to view error(s).",
},
];
type ProfileSummaryResponse = Record<MdmProfileStatus, number>;
interface AggregateMacSettingsIndicatorsProps {
teamId: number;
}
@ -15,52 +49,22 @@ interface AggregateMacSettingsIndicatorsProps {
const AggregateMacSettingsIndicators = ({
teamId,
}: AggregateMacSettingsIndicatorsProps) => {
const AGGREGATE_STATUS_DISPLAY_OPTIONS = {
latest: {
text: "Latest",
iconName: "success",
tooltipText: "Hosts that applied the latest settings.",
},
pending: {
text: "Pending",
iconName: "pending",
tooltipText:
"Hosts that havent applied the latest settings because they are asleep, disconnected from the internet, or require action.",
},
failing: {
text: "Failing",
iconName: "error",
tooltipText:
"Hosts that failed to apply the latest settings. View hosts to see errors.",
},
} as const;
const {
data: aggregateProfileStatusesResponse,
} = useQuery<IAggregateMacSettingsStatus>(
} = useQuery<ProfileSummaryResponse>(
["aggregateProfileStatuses", teamId],
() => mdmAPI.getAggregateProfileStatuses(teamId),
{ refetchOnWindowFocus: false }
);
const DISPLAY_ORDER = ["latest", "pending", "failing"] as const;
const orderedResponseKVArr: [
keyof IAggregateMacSettingsStatus,
number
][] = aggregateProfileStatusesResponse
? DISPLAY_ORDER.map((key) => {
return [key, aggregateProfileStatusesResponse[key]];
})
: [];
if (!aggregateProfileStatusesResponse) return null;
const indicators = orderedResponseKVArr.map(([status, count]) => {
const { text, iconName, tooltipText } = AGGREGATE_STATUS_DISPLAY_OPTIONS[
status
];
const indicators = AGGREGATE_STATUS_DISPLAY_OPTIONS.map((status) => {
const { value, text, iconName, tooltipText } = status;
const count = aggregateProfileStatusesResponse[value];
return (
<div className="aggregate-mac-settings-indicator">
{/* NOTE - below will be renamed as a general component and moved into the components dir by Gabe */}
<MacSettingsIndicator
indicatorText={text}
iconName={iconName}
@ -69,7 +73,7 @@ const AggregateMacSettingsIndicators = ({
<a
href={`${paths.MANAGE_HOSTS}?${buildQueryStringFromParams({
team_id: teamId,
macos_settings: status,
macos_settings: value,
})}`}
>
{count} hosts

View File

@ -1,7 +1,7 @@
import React from "react";
import { useQuery } from "react-query";
import { IDiskEncryptionStatusAggregate } from "interfaces/mdm";
import { IFileVaultSummaryResponse } from "interfaces/mdm";
import mdmAPI from "services/entities/mdm";
import TableContainer from "components/TableContainer";
@ -24,9 +24,9 @@ const DEFAULT_SORT_DIRECTION = "asc";
const DiskEncryptionTable = ({ currentTeamId }: IDiskEncryptionTableProps) => {
const { data, error } = useQuery<
IDiskEncryptionStatusAggregate,
IFileVaultSummaryResponse,
Error,
IDiskEncryptionStatusAggregate
IFileVaultSummaryResponse
>(
["disk-encryption-summary", currentTeamId],
() => mdmAPI.getDiskEncryptionAggregate(currentTeamId),

View File

@ -1,17 +1,20 @@
import React from "react";
import { IDiskEncryptionStatusAggregate } from "interfaces/mdm";
import { DiskEncryptionStatus } from "utilities/constants";
import {
FileVaultProfileStatus,
IFileVaultSummaryResponse,
} from "interfaces/mdm";
import TextCell from "components/TableContainer/DataTable/TextCell";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
import StatusIndicatorWithIcon from "components/StatusIndicatorWithIcon";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import { IndicatorStatus } from "components/StatusIndicatorWithIcon/StatusIndicatorWithIcon";
interface IStatusCellValue {
displayName: string;
statusName: "success" | "pending" | "error";
value: DiskEncryptionStatus;
statusName: IndicatorStatus;
value: FileVaultProfileStatus;
tooltip?: string | JSX.Element;
}
@ -100,7 +103,7 @@ const defaultTableHeaders: IDataColumn[] = [
},
];
type StatusNames = keyof IDiskEncryptionStatusAggregate;
type StatusNames = keyof IFileVaultSummaryResponse;
type StatusEntry = [StatusNames, number];
@ -108,17 +111,17 @@ export const generateTableHeaders = (): IDataColumn[] => {
return defaultTableHeaders;
};
const STATUS_CELL_VALUES: Record<StatusNames, IStatusCellValue> = {
applied: {
displayName: "Applied",
statusName: "success",
value: DiskEncryptionStatus.APPLIED,
tooltip: "Disk encryption on and key stored in Fleet.",
const STATUS_CELL_VALUES: Record<FileVaultProfileStatus, IStatusCellValue> = {
verifying: {
displayName: "Verifying",
statusName: "successPartial",
value: FileVaultProfileStatus.VERIFYING,
tooltip: "Disk encryption on and key stored in Fleet. Fleet will verify.",
},
action_required: {
displayName: "Action required (pending)",
statusName: "pending",
value: DiskEncryptionStatus.ACTION_REQUIRED,
statusName: "pendingPartial",
value: FileVaultProfileStatus.ACTION_REQUIRED,
tooltip: (
<>
Ask the end user to follow <b>Disk encryption</b> instructions on their{" "}
@ -128,25 +131,25 @@ const STATUS_CELL_VALUES: Record<StatusNames, IStatusCellValue> = {
},
enforcing: {
displayName: "Enforcing (pending)",
statusName: "pending",
value: DiskEncryptionStatus.ENFORCING,
statusName: "pendingPartial",
value: FileVaultProfileStatus.ENFORCING,
tooltip: "Setting will be enforced when the hosts come online.",
},
failed: {
displayName: "Failed",
statusName: "error",
value: DiskEncryptionStatus.FAILED,
value: FileVaultProfileStatus.FAILED,
},
removing_enforcement: {
displayName: "Removing enforcement (pending)",
statusName: "pending",
value: DiskEncryptionStatus.REMOVING_ENFORCEMENT,
statusName: "pendingPartial",
value: FileVaultProfileStatus.REMOVING_ENFORCEMENT,
tooltip: "Enforcement will be removed when the hosts come online.",
},
};
export const generateTableData = (
data?: IDiskEncryptionStatusAggregate,
data?: IFileVaultSummaryResponse,
currentTeamId?: number
) => {
if (!data) return [];

View File

@ -48,10 +48,10 @@ import { IOperatingSystemVersion } from "interfaces/operating_system";
import { IPolicy, IStoredPolicyResponse } from "interfaces/policy";
import { ITeam } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";
import { FileVaultProfileStatus } from "interfaces/mdm";
import sortUtils from "utilities/sort";
import {
DiskEncryptionStatus,
HOSTS_SEARCH_BOX_PLACEHOLDER,
HOSTS_SEARCH_BOX_TOOLTIP,
PolicyResponse,
@ -245,7 +245,7 @@ const ManageHostsPage = ({
? parseInt(queryParams.low_disk_space, 10)
: undefined;
const missingHosts = queryParams?.status === "missing";
const diskEncryptionStatus: DiskEncryptionStatus | undefined =
const diskEncryptionStatus: FileVaultProfileStatus | undefined =
queryParams?.macos_settings_disk_encryption;
// ========= routeParams
@ -562,7 +562,7 @@ const ManageHostsPage = ({
};
const handleChangeDiskEncryptionStatusFilter = (
newStatus: DiskEncryptionStatus
newStatus: FileVaultProfileStatus
) => {
handleResetPageIndex();

View File

@ -1,44 +1,44 @@
import React from "react";
import { IDropdownOption } from "interfaces/dropdownOption";
import { DiskEncryptionStatus } from "utilities/constants";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import { FileVaultProfileStatus } from "interfaces/mdm";
const baseClass = "disk-encryption-status-filter";
const DISK_ENCRYPTION_STATUS_OPTIONS: IDropdownOption[] = [
{
disabled: false,
label: "Applied",
value: DiskEncryptionStatus.APPLIED,
label: "Verifying",
value: FileVaultProfileStatus.VERIFYING,
},
{
disabled: false,
label: "Action required",
value: DiskEncryptionStatus.ACTION_REQUIRED,
value: FileVaultProfileStatus.ACTION_REQUIRED,
},
{
disabled: false,
label: "Enforcing",
value: DiskEncryptionStatus.ENFORCING,
value: FileVaultProfileStatus.ENFORCING,
},
{
disabled: false,
label: "Failed",
value: DiskEncryptionStatus.FAILED,
value: FileVaultProfileStatus.FAILED,
},
{
disabled: false,
label: "Removing enforcement",
value: DiskEncryptionStatus.REMOVING_ENFORCEMENT,
value: FileVaultProfileStatus.REMOVING_ENFORCEMENT,
},
];
interface IDiskEncryptionStatusFilterProps {
diskEncryptionStatus: DiskEncryptionStatus;
onChange: (value: DiskEncryptionStatus) => void;
diskEncryptionStatus: FileVaultProfileStatus;
onChange: (value: FileVaultProfileStatus) => void;
}
const DiskEncryptionStatusFilter = ({

View File

@ -6,16 +6,17 @@ import {
formatOperatingSystemDisplayName,
IOperatingSystemVersion,
} from "interfaces/operating_system";
import { IMdmSolution, MDM_ENROLLMENT_STATUS } from "interfaces/mdm";
import {
FileVaultProfileStatus,
IMdmSolution,
MDM_ENROLLMENT_STATUS,
} from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
import { ISoftware } from "interfaces/software";
import { IPolicy } from "interfaces/policy";
// TODO: should this be in interfaces hosts?
import { MacSettingsStatusQueryParam } from "services/entities/hosts";
import {
DiskEncryptionStatus,
PLATFORM_LABEL_DISPLAY_NAMES,
PolicyResponse,
} from "utilities/constants";
@ -60,14 +61,16 @@ interface IHostsFilterBlockProps {
osVersions?: IOperatingSystemVersion[];
softwareDetails: ISoftware | null;
mdmSolutionDetails: IMdmSolution | null;
diskEncryptionStatus?: DiskEncryptionStatus;
diskEncryptionStatus?: FileVaultProfileStatus;
};
selectedLabel?: ILabel;
isOnlyObserver?: boolean;
handleClearRouteParam: () => void;
handleClearFilter: (omitParams: string[]) => void;
onChangePoliciesFilter: (response: PolicyResponse) => void;
onChangeDiskEncryptionStatusFilter: (response: DiskEncryptionStatus) => void;
onChangeDiskEncryptionStatusFilter: (
response: FileVaultProfileStatus
) => void;
onChangeMacSettingsFilter: (
newMacSettingsStatus: MacSettingsStatusQueryParam
) => void;
@ -265,7 +268,6 @@ const HostsFilterBlock = ({
if (!mdmEnrollmentStatus) return null;
const label = `MDM status: ${
// TODO: move MDM_ENROLLMENT_STATUS to util file
invert(MDM_ENROLLMENT_STATUS)[mdmEnrollmentStatus]
}`;

View File

@ -1,3 +1,5 @@
import { MdmProfileStatus } from "interfaces/mdm";
export const LABEL_SLUG_PREFIX = "labels/";
export const DEFAULT_SORT_HEADER = "display_name";
@ -41,17 +43,17 @@ export const HOST_SELECT_STATUSES = [
export const MAC_SETTINGS_FILTER_OPTIONS = [
{
disabled: false,
label: "Latest",
value: "latest",
label: "Verifying",
value: MdmProfileStatus.VERIFYING,
},
{
disabled: false,
label: "Pending",
value: "pending",
value: MdmProfileStatus.PENDING,
},
{
disabled: false,
label: "Failing",
value: "failing",
label: "Failed",
value: MdmProfileStatus.FAILED,
},
];

View File

@ -1,11 +1,11 @@
import React from "react";
import Button from "components/buttons/Button";
import Modal from "components/Modal";
import { IMacSettings } from "interfaces/mdm";
import { IHostMacMdmProfile } from "interfaces/mdm";
import MacSettingsTable from "./MacSettingsTable";
interface IMacSettingsModalProps {
hostMacSettings?: IMacSettings;
hostMacSettings?: IHostMacMdmProfile[];
onClose: () => void;
}

View File

@ -1,26 +1,23 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { createCustomRenderer } from "test/test-utils";
import {
MacMdmProfileOperationType,
MacMdmProfileStatus,
} from "interfaces/mdm";
import { MacMdmProfileOperationType, MdmProfileStatus } from "interfaces/mdm";
import MacSettingStatusCell from "./MacSettingStatusCell";
describe("Mac setting status cell", () => {
it("Correctly displays the status text of a profile", () => {
const status: MacMdmProfileStatus = "applied";
const status = MdmProfileStatus.VERIFYING;
const operationType: MacMdmProfileOperationType = "install";
render(
<MacSettingStatusCell status={status} operationType={operationType} />
);
expect(screen.getByText("Applied")).toBeInTheDocument();
expect(screen.getByText("Verifying")).toBeInTheDocument();
});
it("Correctly displays the tooltip text for a profile", async () => {
const status: MacMdmProfileStatus = "applied";
const status = MdmProfileStatus.VERIFYING;
const operationType: MacMdmProfileOperationType = "install";
const customRender = createCustomRenderer();
@ -29,7 +26,7 @@ describe("Mac setting status cell", () => {
<MacSettingStatusCell status={status} operationType={operationType} />
);
const statusText = screen.getByText("Applied");
const statusText = screen.getByText("Verifying");
await user.hover(statusText);

View File

@ -1,33 +1,35 @@
import Icon from "components/Icon";
import TextCell from "components/TableContainer/DataTable/TextCell";
import {
MacMdmProfileOperationType,
MacMdmProfileStatus,
} from "interfaces/mdm";
import { IconNames } from "components/icons";
import { MacMdmProfileOperationType, MdmProfileStatus } from "interfaces/mdm";
import _ from "lodash";
import React from "react";
import ReactTooltip from "react-tooltip";
const baseClass = "mac-setting-status-cell";
interface IMacSettingStatusCellProps {
status: MacMdmProfileStatus;
operationType: MacMdmProfileOperationType;
}
const MacSettingStatusCell = ({
status,
operationType,
}: IMacSettingStatusCellProps): JSX.Element => {
const PROFILE_DISPLAY_CONFIG = {
type ProfileDisplayOption = {
statusText: string;
iconName: IconNames;
tooltipText?: string;
} | null;
type OperationTypeOption = Record<MdmProfileStatus, ProfileDisplayOption>;
type ProfileDisplayConfig = Record<
MacMdmProfileOperationType,
OperationTypeOption
>;
const PROFILE_DISPLAY_CONFIG: ProfileDisplayConfig = {
install: {
pending: {
statusText: "Enforcing (pending)",
iconName: "pending",
iconName: "pending-partial",
tooltipText: "Setting will be enforced when the host comes online.",
},
applied: {
statusText: "Applied",
iconName: "success",
verifying: {
statusText: "Verifying",
iconName: "success-partial",
tooltipText: "Host applied the setting.",
},
failed: {
@ -39,18 +41,26 @@ const MacSettingStatusCell = ({
remove: {
pending: {
statusText: "Removing enforcement (pending)",
iconName: "pending",
iconName: "pending-partial",
tooltipText: "Enforcement will be removed when the host comes online.",
},
applied: null, // should not be reached
verifying: null, // should not be reached
failed: {
statusText: "Failed",
iconName: "error",
tooltipText: undefined,
},
},
} as const;
};
interface IMacSettingStatusCellProps {
status: MdmProfileStatus;
operationType: MacMdmProfileOperationType;
}
const MacSettingStatusCell = ({
status,
operationType,
}: IMacSettingStatusCellProps): JSX.Element => {
const options = PROFILE_DISPLAY_CONFIG[operationType]?.[status];
if (options) {

View File

@ -1,13 +1,13 @@
import React from "react";
import TableContainer from "components/TableContainer";
import { IMacSettings } from "interfaces/mdm";
import { IHostMacMdmProfile } from "interfaces/mdm";
import tableHeaders from "./MacSettingsTableConfig";
const baseClass = "macsettings-table";
interface IMacSettingsTableProps {
hostMacSettings?: IMacSettings;
hostMacSettings?: IHostMacMdmProfile[];
}
const MacSettingsTable = ({ hostMacSettings }: IMacSettingsTableProps) => {

View File

@ -9,7 +9,7 @@ import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
import { humanHostMemory, wrapFleetHelper } from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import StatusIndicator from "components/StatusIndicator";
import { IMacSettings } from "interfaces/mdm";
import { IHostMacMdmProfile } from "interfaces/mdm";
import getHostStatusTooltipText from "pages/hosts/helpers";
import IssueIcon from "../../../../../../assets/images/icon-issue-fleet-black-50-16x16@2x.png";
import MacSettingsIndicator from "./MacSettingsIndicator";
@ -30,7 +30,7 @@ interface IHostSummaryProps {
isOnlyObserver?: boolean;
toggleOSPolicyModal?: () => void;
toggleMacSettingsModal?: () => void;
hostMacSettings?: IMacSettings;
hostMacSettings?: IHostMacMdmProfile[];
mdmName?: string;
showRefetchSpinner: boolean;
onRefetchHost: (

View File

@ -1,26 +1,32 @@
import React from "react";
import ReactTooltip from "react-tooltip";
import { IHostMacMdmProfile, MdmProfileStatus } from "interfaces/mdm";
import Icon from "components/Icon";
import Button from "components/buttons/Button";
import { IMacSettings, MacSettingsStatus } from "interfaces/mdm";
import { IconNames } from "components/icons";
const baseClass = "mac-settings-indicator";
interface IMacSettingsIndicatorProps {
profiles: IMacSettings;
onClick?: () => void;
type MacSettingsStatus = "Failing" | "Verifying" | "Pending";
interface IStatusDisplayOption {
iconName: Extract<
IconNames,
"success" | "success-partial" | "pending" | "pending-partial" | "error"
>;
tooltipText: string;
}
const MacSettingsIndicator = ({
profiles,
onClick,
}: IMacSettingsIndicatorProps): JSX.Element => {
const STATUS_DISPLAY_OPTIONS = {
Latest: {
iconName: "success",
type StatusDisplayOptions = Record<MacSettingsStatus, IStatusDisplayOption>;
const STATUS_DISPLAY_OPTIONS: StatusDisplayOptions = {
Verifying: {
iconName: "success-partial",
tooltipText: "Host applied the latest settings",
},
Pending: {
iconName: "pending",
iconName: "pending-partial",
tooltipText: "Host will apply the latest settings when it comes online",
},
Failing: {
@ -28,21 +34,29 @@ const MacSettingsIndicator = ({
tooltipText:
"Host failed to apply the latest settings. Click to view error(s).",
},
} as const;
};
const getMacSettingsStatus = (
hostMacSettings: IMacSettings | undefined
): MacSettingsStatus => {
const getMacSettingsStatus = (
hostMacSettings?: IHostMacMdmProfile[]
): MacSettingsStatus => {
const statuses = hostMacSettings?.map((setting) => setting.status);
if (statuses?.includes("failed")) {
if (statuses?.includes(MdmProfileStatus.FAILED)) {
return "Failing";
}
if (statuses?.includes("pending")) {
if (statuses?.includes(MdmProfileStatus.PENDING)) {
return "Pending";
}
return "Latest";
};
return "Verifying";
};
interface IMacSettingsIndicatorProps {
profiles: IHostMacMdmProfile[];
onClick?: () => void;
}
const MacSettingsIndicator = ({
profiles,
onClick,
}: IMacSettingsIndicatorProps): JSX.Element => {
const macSettingsStatus = getMacSettingsStatus(profiles);
const iconName = STATUS_DISPLAY_OPTIONS[macSettingsStatus].iconName;

View File

@ -2,13 +2,14 @@
import sendRequest from "services";
import endpoints from "utilities/endpoints";
import { HostStatus } from "interfaces/host";
import { FileVaultProfileStatus } from "interfaces/mdm";
import {
buildQueryStringFromParams,
getLabelParam,
reconcileMutuallyExclusiveHostParams,
reconcileMutuallyInclusiveHostParams,
} from "utilities/url";
import { DiskEncryptionStatus } from "utilities/constants";
import { MacSettingsStatusQueryParam } from "./hosts";
export interface ISortOption {
@ -42,7 +43,7 @@ export interface IHostCountLoadOptions {
osId?: number;
osName?: string;
osVersion?: string;
diskEncryptionStatus?: DiskEncryptionStatus;
diskEncryptionStatus?: FileVaultProfileStatus;
}
export default {

View File

@ -9,9 +9,8 @@ import {
reconcileMutuallyInclusiveHostParams,
} from "utilities/url";
import { ISelectedPlatform } from "interfaces/platform";
import { DiskEncryptionStatus } from "utilities/constants";
import { ISoftware } from "interfaces/software";
import { IMdmSolution } from "interfaces/mdm";
import { FileVaultProfileStatus, IMdmSolution } from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
export interface ISortOption {
@ -54,7 +53,7 @@ export interface ILoadHostsOptions {
device_mapping?: boolean;
columns?: string;
visibleColumns?: string;
diskEncryptionStatus?: DiskEncryptionStatus;
diskEncryptionStatus?: FileVaultProfileStatus;
}
export interface IExportHostsOptions {
@ -79,7 +78,7 @@ export interface IExportHostsOptions {
device_mapping?: boolean;
columns?: string;
visibleColumns?: string;
diskEncryptionStatus?: DiskEncryptionStatus;
diskEncryptionStatus?: FileVaultProfileStatus;
}
export interface IActionByFilter {

View File

@ -10,14 +10,6 @@ export enum PolicyResponse {
FAILING = "failing",
}
export enum DiskEncryptionStatus {
APPLIED = "applied",
ACTION_REQUIRED = "action_required",
ENFORCING = "enforcing",
FAILED = "failed",
REMOVING_ENFORCEMENT = "removing_enforcement",
}
export const DEFAULT_GRAVATAR_LINK =
"https://fleetdm.com/images/permanent/icon-avatar-default-transparent-64x64%402x.png";

View File

@ -1,6 +1,6 @@
import { FileVaultProfileStatus } from "interfaces/mdm";
import { isEmpty, reduce, omitBy, Dictionary } from "lodash";
import { MacSettingsStatusQueryParam } from "services/entities/hosts";
import { DiskEncryptionStatus } from "utilities/constants";
type QueryValues = string | number | boolean | undefined | null;
export type QueryParams = Record<string, QueryValues>;
@ -24,7 +24,7 @@ interface IMutuallyExclusiveHostParams {
osId?: number;
osName?: string;
osVersion?: string;
diskEncryptionStatus?: DiskEncryptionStatus;
diskEncryptionStatus?: FileVaultProfileStatus;
}
const reduceQueryParams = (

View File

@ -384,7 +384,7 @@ type MDMAppleConfigProfilesSummary struct {
Pending uint `json:"pending" db:"pending"`
// Failed includes each host that has failed to apply one or more of the profiles currently
// applicable to the host.
Failed uint `json:"failing" db:"failed"`
Failed uint `json:"failed" db:"failed"`
}
// MDMAppleFileVaultSummary reports the number of macOS hosts being managed with Apples disk