diff --git a/changes/17897-api-resend-mdm-profile b/changes/17897-api-resend-mdm-profile
new file mode 100644
index 0000000000..8bbdf7dd1a
--- /dev/null
+++ b/changes/17897-api-resend-mdm-profile
@@ -0,0 +1 @@
+- Added API to support resending MDM profiles.
diff --git a/changes/issue-17896-ui-resend-profile b/changes/issue-17896-ui-resend-profile
new file mode 100644
index 0000000000..3911edd2bf
--- /dev/null
+++ b/changes/issue-17896-ui-resend-profile
@@ -0,0 +1 @@
+- add UI for resending a profile for a host on the host details page in the OS Settings modal
diff --git a/docs/Using Fleet/Audit-logs.md b/docs/Using Fleet/Audit-logs.md
index 700f2e869a..d0bda3d634 100644
--- a/docs/Using Fleet/Audit-logs.md
+++ b/docs/Using Fleet/Audit-logs.md
@@ -1108,6 +1108,25 @@ This activity contains the following fields:
}
```
+## resent_configuration_profile
+
+Generated when a user resends an MDM configuration profile to a host.
+
+This activity contains the following fields:
+- "host_id": The ID of the host.
+- "host_display_name": The display name of the host.
+- "profile_name": The name of the configuration profile.
+
+#### Example
+
+```json
+{
+ "host_id": 1,
+ "host_display_name": "Anna's MacBook Pro",
+ "profile_name": "Passcode requirements"
+}
+```
+
diff --git a/frontend/components/TableContainer/DataTable/TooltipTruncatedTextCell/TooltipTruncatedTextCell.tsx b/frontend/components/TableContainer/DataTable/TooltipTruncatedTextCell/TooltipTruncatedTextCell.tsx
index 64dda5d065..0a78718146 100644
--- a/frontend/components/TableContainer/DataTable/TooltipTruncatedTextCell/TooltipTruncatedTextCell.tsx
+++ b/frontend/components/TableContainer/DataTable/TooltipTruncatedTextCell/TooltipTruncatedTextCell.tsx
@@ -9,14 +9,16 @@ import { COLORS } from "styles/var/colors";
interface ITooltipTruncatedTextCellProps {
value: React.ReactNode;
/** Tooltip to dispay. If this is provided then this will be rendered as the tooltip content. If
- * not the value will be displayed as the tooltip content. Defaults to `undefined` */
+ * not, the value will be displayed as the tooltip content. Defaults to `undefined` */
tooltip?: React.ReactNode;
/** If set to `true` the text inside the tooltip will break on words instead of any character.
* By default the tooltip text breaks on any character.
* Default is `false`.
*/
tooltipBreakOnWord?: boolean;
+ /** @deprecated use the prop `className` in order to add custom classes to this component */
classes?: string;
+ className?: string;
}
const baseClass = "tooltip-truncated-cell";
@@ -26,8 +28,9 @@ const TooltipTruncatedTextCell = ({
tooltip,
tooltipBreakOnWord = false,
classes = "w250",
+ className,
}: ITooltipTruncatedTextCellProps): JSX.Element => {
- const classNames = classnames(baseClass, classes, {
+ const classNames = classnames(baseClass, classes, className, {
"tooltip-break-on-word": tooltipBreakOnWord,
});
diff --git a/frontend/components/TableContainer/TableContainer.tsx b/frontend/components/TableContainer/TableContainer.tsx
index 4435260157..a78c023c1f 100644
--- a/frontend/components/TableContainer/TableContainer.tsx
+++ b/frontend/components/TableContainer/TableContainer.tsx
@@ -1,6 +1,6 @@
import React, { useState, useCallback, useRef, useEffect } from "react";
import classnames from "classnames";
-import { Row, UseExpandedRowProps } from "react-table";
+import { Row } from "react-table";
import ReactTooltip from "react-tooltip";
import useDeepEffect from "hooks/useDeepEffect";
diff --git a/frontend/interfaces/activity.ts b/frontend/interfaces/activity.ts
index dd871d7be6..841c326ab2 100644
--- a/frontend/interfaces/activity.ts
+++ b/frontend/interfaces/activity.ts
@@ -70,6 +70,7 @@ export enum ActivityType {
CreatedDeclarationProfile = "created_declaration_profile",
DeletedDeclarationProfile = "deleted_declaration_profile",
EditedDeclarationProfile = "edited_declaration_profile",
+ ResentConfigurationProfile = "resent_configuration_profile",
}
// This is a subset of ActivityType that are shown only for the host past activities
diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
index 9998278692..9827afc2ec 100644
--- a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
+++ b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
@@ -810,6 +810,16 @@ const TAGGED_TEMPLATES = {
>
);
},
+
+ resentConfigProfile: (activity: IActivity) => {
+ return (
+ <>
+ {" "}
+ resent {activity.details?.profile_name} configuration profile to{" "}
+ {activity.details?.host_display_name}.
+ >
+ );
+ },
};
const getDetail = (
@@ -980,6 +990,9 @@ const getDetail = (
case ActivityType.EditedDeclarationProfile: {
return TAGGED_TEMPLATES.editedDeclarationProfile(activity, isPremiumTier);
}
+ case ActivityType.ResentConfigurationProfile: {
+ return TAGGED_TEMPLATES.resentConfigProfile(activity);
+ }
default: {
return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
diff --git a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
index b9fa3811bc..629f4956b5 100644
--- a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
+++ b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
@@ -445,10 +445,12 @@ const DeviceUserPage = ({
policy={selectedPolicy}
/>
)}
- {showOSSettingsModal && (
+ {!!host && showOSSettingsModal && (
)}
diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
index 6a2839035e..5370d3f6b9 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
@@ -932,9 +932,12 @@ const HostDetailsPage = ({
)}
{showOSSettingsModal && (
)}
{showUnenrollMdmModal && !!host && (
diff --git a/frontend/pages/hosts/details/OSSettingsModal/OSSettingsModal.tsx b/frontend/pages/hosts/details/OSSettingsModal/OSSettingsModal.tsx
index cc2db63d17..a9c916f570 100644
--- a/frontend/pages/hosts/details/OSSettingsModal/OSSettingsModal.tsx
+++ b/frontend/pages/hosts/details/OSSettingsModal/OSSettingsModal.tsx
@@ -7,17 +7,27 @@ import OSSettingsTable from "./OSSettingsTable";
import { generateTableData } from "./OSSettingsTable/OSSettingsTableConfig";
interface IOSSettingsModalProps {
- platform?: string;
- hostMDMData?: IHostMdmData;
+ hostId: number;
+ platform: string;
+ hostMDMData: IHostMdmData;
+ /** controls showing the action for a user to resend a profile. Defaults to `false` */
+ canResendProfiles?: boolean;
onClose: () => void;
+ /** handler that fires when a profile was reset. Requires `canResendProfiles` prop
+ * to be `true`, otherwise has no effect.
+ */
+ onProfileResent?: () => void;
}
const baseClass = "os-settings-modal";
const OSSettingsModal = ({
+ hostId,
platform,
hostMDMData,
+ canResendProfiles = false,
onClose,
+ onProfileResent,
}: IOSSettingsModalProps) => {
// the caller should ensure that hostMDMData is not undefined and that platform is "windows" or
// "darwin", otherwise we will allow an empty modal will be rendered.
@@ -36,7 +46,12 @@ const OSSettingsModal = ({
width="large"
>
<>
-
+