diff --git a/changes/issue-18848-include-all-exclude-any-custom-profile b/changes/issue-18848-include-all-exclude-any-custom-profile new file mode 100644 index 0000000000..ef065303f3 --- /dev/null +++ b/changes/issue-18848-include-all-exclude-any-custom-profile @@ -0,0 +1,2 @@ +- add UI for uploading custom profiles with a target of hosts that include all/exclude + any selected labels diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts index ebb286630c..21ac95dbcd 100644 --- a/frontend/interfaces/config.ts +++ b/frontend/interfaces/config.ts @@ -29,6 +29,12 @@ export interface IMacOsMigrationSettings { webhook_url: string; } +interface ICustomSetting { + path: string; + labels_include_all?: string[]; + labels_exclude_any?: string[]; +} + export interface IMdmConfig { enable_disk_encryption: boolean; enabled_and_configured: boolean; @@ -42,7 +48,7 @@ export interface IMdmConfig { deadline: string | null; }; macos_settings: { - custom_settings: null; + custom_settings: null | ICustomSetting[]; enable_disk_encryption: boolean; }; macos_setup: { diff --git a/frontend/interfaces/dropdownOption.ts b/frontend/interfaces/dropdownOption.ts index a8895d48b9..67b1bacd4d 100644 --- a/frontend/interfaces/dropdownOption.ts +++ b/frontend/interfaces/dropdownOption.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import PropTypes from "prop-types"; export default PropTypes.shape({ @@ -10,6 +11,7 @@ export interface IDropdownOption { disabled?: boolean; label: string | JSX.Element; value: string | number; + helpText?: ReactNode; premiumOnly?: boolean; tooltipContent?: string | JSX.Element; } diff --git a/frontend/interfaces/mdm.ts b/frontend/interfaces/mdm.ts index 4b3a8791e6..a911f0c9e7 100644 --- a/frontend/interfaces/mdm.ts +++ b/frontend/interfaces/mdm.ts @@ -71,7 +71,8 @@ export type ProfilePlatform = "darwin" | "windows"; export interface IProfileLabel { name: string; - broken: boolean; + id?: number; // id is only present when the label is not broken + broken?: boolean; } export interface IMdmProfile { @@ -83,7 +84,8 @@ export interface IMdmProfile { created_at: string; updated_at: string; checksum: string | null; // null for windows profiles - labels?: IProfileLabel[]; + labels_include_all?: IProfileLabel[]; + labels_exclude_any?: IProfileLabel[]; } export type MdmProfileStatus = "verified" | "verifying" | "pending" | "failed"; diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx index 8298a10647..89887e9b83 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx @@ -20,7 +20,7 @@ import Pagination from "pages/ManageControlsPage/components/Pagination"; import UploadList from "../../../components/UploadList"; import AddProfileCard from "./components/ProfileUploader/components/AddProfileCard"; -import AddProfileModal from "./components/ProfileUploader/components/AddProfileModal"; +import AddProfileModal from "./components/ProfileUploader/components/AddProfileModal/AddProfileModal"; import DeleteProfileModal from "./components/DeleteProfileModal/DeleteProfileModal"; import ProfileLabelsModal from "./components/ProfileLabelsModal/ProfileLabelsModal"; import ProfileListItem from "./components/ProfileListItem"; @@ -101,7 +101,7 @@ const CustomSettings = ({ onMutation(); renderFlash("success", "Successfully deleted!"); } catch (e) { - renderFlash("error", "Couldn’t delete. Please try again."); + renderFlash("error", "Couldn't delete. Please try again."); } finally { selectedProfile.current = null; setShowDeleteProfileModal(false); @@ -169,6 +169,10 @@ const CustomSettings = ({ ); }; + const hasLabels = + !!profileLabelsModalData?.labels_include_all?.length || + !!profileLabelsModalData?.labels_exclude_any?.length; + return (
@@ -189,7 +193,6 @@ const CustomSettings = ({ )} {showAddProfileModal && ( )} - {!!isPremiumTier && !!profileLabelsModalData?.labels?.length && ( + {isPremiumTier && hasLabels && ( ( -
- {profileName} will only be applied to hosts that have all these - labels: -
-); + targetType, +}: IModalDescriptionProps) => { + const targetTypeText = + targetType === "includeAll" ? ( + <> + have all + + ) : ( + <> + don't have any + + ); + + return ( +
+ {profileName} profile only applies to hosts that {targetTypeText}{" "} + of these labels: +
+ ); +}; const BrokenLabelWarning = () => ( The configuration profile is{" "} broken @@ -67,7 +84,14 @@ const ProfileLabelsModal = ({ profile, setModalData, }: IProfileLabelsModalProps) => { - if (!profile?.labels?.length) { + if (!profile) { + return null; + } + + const { name, labels_include_all, labels_exclude_any } = profile; + const labels = labels_include_all || labels_exclude_any; + + if (!labels?.length) { // caller ensures this never happens return null; } @@ -75,9 +99,13 @@ const ProfileLabelsModal = ({ return ( setModalData(null)}>
- {profile.labels.some((label) => label.broken) && } - - + {labels.some((label) => label.broken) && } + +