diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/index.tsx
index 53e218b01705d..3b41a20c32e35 100644
--- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/index.tsx
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/index.tsx
@@ -50,6 +50,15 @@ export const RuleTagBadge = suspendedComponentWithProps(
export const RuleStatusPanel = suspendedComponentWithProps(
lazy(() => import('./rule_details/components/rule_status_panel'))
);
+
+export const UntrackAlertsModal = suspendedComponentWithProps(
+ lazy(() =>
+ import('./common/components/untrack_alerts_modal').then((module) => ({
+ default: module.UntrackAlertsModal,
+ }))
+ )
+);
+
export const GlobalRuleEventLogList = suspendedComponentWithProps(
lazy(() => import('./rule_details/components/global_rule_event_log_list'))
);
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_untrack_modal.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_untrack_modal.tsx
new file mode 100644
index 0000000000000..3e51f40d1296f
--- /dev/null
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_untrack_modal.tsx
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { UntrackAlertsModal } from '../application/sections/common/components/untrack_alerts_modal';
+import type { UntrackAlertsModalProps } from '../application/sections/common/components/untrack_alerts_modal';
+
+export const getUntrackModalLazy = (props: UntrackAlertsModalProps) => {
+ return ;
+};
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts
index 53a2ee91e2414..1db7fa448f5bc 100644
--- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts
@@ -42,6 +42,9 @@ import { getRuleStatusPanelLazy } from './common/get_rule_status_panel';
import { getRuleSnoozeModalLazy } from './common/get_rule_snooze_modal';
import { getRulesSettingsLinkLazy } from './common/get_rules_settings_link';
import type { AlertSummaryWidgetDependencies } from './application/sections/alert_summary_widget/types';
+import { isRuleSnoozed } from './application/lib';
+import { getNextRuleSnoozeSchedule } from './application/sections/rules_list/components/notify_badge/helpers';
+import { getUntrackModalLazy } from './common/get_untrack_modal';
function createStartMock(): TriggersAndActionsUIPublicPluginStart {
const actionTypeRegistry = new TypeRegistry();
@@ -122,6 +125,20 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart {
getRulesSettingsLink: () => {
return getRulesSettingsLinkLazy();
},
+ getUntrackModal: (props) => {
+ return getUntrackModalLazy(props);
+ },
+ getRuleHelpers: (rule) => {
+ return {
+ isRuleSnoozed: isRuleSnoozed({
+ isSnoozedUntil: rule.isSnoozedUntil,
+ muteAll: rule.muteAll,
+ }),
+ getNextRuleSnoozeSchedule: getNextRuleSnoozeSchedule({
+ snoozeSchedule: rule.snoozeSchedule,
+ }),
+ };
+ },
};
}
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts
index 912ae58aef551..92b131920302a 100644
--- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts
@@ -30,11 +30,11 @@ import type { ExpressionsStart } from '@kbn/expressions-plugin/public';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
-import type { RuleAction } from '@kbn/alerting-plugin/common';
+import type { RRuleParams, RuleAction, RuleTypeParams } from '@kbn/alerting-plugin/common';
import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry';
import type { CloudSetup } from '@kbn/cloud-plugin/public';
import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public';
-import type { RuleUiAction } from './types';
+import type { Rule, RuleUiAction } from './types';
import type { AlertsSearchBarProps } from './application/sections/alerts_search_bar';
import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout';
@@ -84,6 +84,10 @@ import type {
RulesListNotifyBadgePropsWithApi,
RulesListProps,
} from './types';
+import type { UntrackAlertsModalProps } from './application/sections/common/components/untrack_alerts_modal';
+import { isRuleSnoozed } from './application/lib';
+import { getNextRuleSnoozeSchedule } from './application/sections/rules_list/components/notify_badge/helpers';
+import { getUntrackModalLazy } from './common/get_untrack_modal';
export interface TriggersAndActionsUIPublicPluginSetup {
actionTypeRegistry: TypeRegistry;
@@ -122,7 +126,17 @@ export interface TriggersAndActionsUIPublicPluginStart {
getRuleStatusPanel: (props: RuleStatusPanelProps) => ReactElement;
getAlertSummaryWidget: (props: AlertSummaryWidgetProps) => ReactElement;
getRuleSnoozeModal: (props: RuleSnoozeModalProps) => ReactElement;
+ getUntrackModal: (props: UntrackAlertsModalProps) => ReactElement;
getRulesSettingsLink: () => ReactElement;
+ getRuleHelpers: (rule: Rule) => {
+ isRuleSnoozed: boolean;
+ getNextRuleSnoozeSchedule: {
+ duration: number;
+ rRule: RRuleParams;
+ id?: string | undefined;
+ skipRecurrences?: string[] | undefined;
+ } | null;
+ };
getGlobalRuleEventLogList: (
props: GlobalRuleEventLogListProps
) => ReactElement;
@@ -493,9 +507,23 @@ export class Plugin
getRuleSnoozeModal: (props: RuleSnoozeModalProps) => {
return getRuleSnoozeModalLazy(props);
},
+ getUntrackModal: (props: UntrackAlertsModalProps) => {
+ return getUntrackModalLazy(props);
+ },
getRulesSettingsLink: () => {
return getRulesSettingsLinkLazy();
},
+ getRuleHelpers: (rule: Rule) => {
+ return {
+ isRuleSnoozed: isRuleSnoozed({
+ isSnoozedUntil: rule.isSnoozedUntil,
+ muteAll: rule.muteAll,
+ }),
+ getNextRuleSnoozeSchedule: getNextRuleSnoozeSchedule({
+ snoozeSchedule: rule.snoozeSchedule,
+ }),
+ };
+ },
};
}
diff --git a/x-pack/solutions/observability/plugins/observability/public/hooks/use_disable_rule.ts b/x-pack/solutions/observability/plugins/observability/public/hooks/use_disable_rule.ts
new file mode 100644
index 0000000000000..92753e7c98287
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/observability/public/hooks/use_disable_rule.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { i18n } from '@kbn/i18n';
+import { useKibana } from '../utils/kibana_react';
+
+export function useDisableRule() {
+ const {
+ http,
+ notifications: { toasts },
+ } = useKibana().services;
+
+ const queryClient = useQueryClient();
+
+ const disableRule = useMutation(
+ ['disableRule'],
+ ({ id, untrack }) => {
+ const body = JSON.stringify({
+ ...(untrack ? { untrack } : {}),
+ });
+ try {
+ return http.post(`/api/alerting/rule/${id}/_disable`, { body });
+ } catch (e) {
+ throw new Error(`Unable to parse id: ${e}`);
+ }
+ },
+ {
+ onError: (_err) => {
+ toasts.addDanger(
+ i18n.translate(
+ 'xpack.observability.rules.disableErrorModal.errorNotification.descriptionText',
+ {
+ defaultMessage: 'Failed to disable rule',
+ }
+ )
+ );
+ },
+
+ onSuccess: (_, variables) => {
+ queryClient.invalidateQueries({ queryKey: ['fetchRule', variables.id], exact: false });
+ toasts.addSuccess(
+ i18n.translate(
+ 'xpack.observability.rules.disableConfirmationModal.successNotification.descriptionText',
+ {
+ defaultMessage: 'Disabled rule',
+ }
+ )
+ );
+ },
+ }
+ );
+
+ return disableRule;
+}
diff --git a/x-pack/solutions/observability/plugins/observability/public/hooks/use_enable_rule.ts b/x-pack/solutions/observability/plugins/observability/public/hooks/use_enable_rule.ts
new file mode 100644
index 0000000000000..44911c6d2e5a4
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/observability/public/hooks/use_enable_rule.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { i18n } from '@kbn/i18n';
+import { useKibana } from '../utils/kibana_react';
+
+export function useEnableRule() {
+ const {
+ http,
+ notifications: { toasts },
+ } = useKibana().services;
+
+ const queryClient = useQueryClient();
+
+ const enableRule = useMutation(
+ ['enableRule'],
+ ({ id }) => {
+ try {
+ return http.post(`/api/alerting/rule/${id}/_enable`);
+ } catch (e) {
+ throw new Error(`Unable to parse id: ${e}`);
+ }
+ },
+ {
+ onError: (_err) => {
+ toasts.addDanger(
+ i18n.translate(
+ 'xpack.observability.rules.enableErrorModal.errorNotification.descriptionText',
+ {
+ defaultMessage: 'Failed to enable rule',
+ }
+ )
+ );
+ },
+
+ onSuccess: (_, variables) => {
+ queryClient.invalidateQueries({ queryKey: ['fetchRule', variables.id], exact: false });
+ toasts.addSuccess(
+ i18n.translate(
+ 'xpack.observability.rules.enableConfirmationModal.successNotification.descriptionText',
+ {
+ defaultMessage: 'Enabled rule',
+ }
+ )
+ );
+ },
+ }
+ );
+
+ return enableRule;
+}
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/header_actions.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/header_actions.tsx
index 9fa0a7631a2ea..f267369f4f96a 100644
--- a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/header_actions.tsx
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/header_actions.tsx
@@ -15,8 +15,13 @@ import {
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-
+import { noop } from 'lodash';
+import { useFetchRule } from '../../../hooks/use_fetch_rule';
+import { useKibana } from '../../../utils/kibana_react';
+import { useEnableRule } from '../../../hooks/use_enable_rule';
+import { useDisableRule } from '../../../hooks/use_disable_rule';
interface HeaderActionsProps {
+ ruleId: string;
isLoading: boolean;
isRuleEditable: boolean;
onDeleteRule: () => void;
@@ -24,12 +29,35 @@ interface HeaderActionsProps {
}
export function HeaderActions({
+ ruleId,
isLoading,
isRuleEditable,
onDeleteRule,
onEditRule,
}: HeaderActionsProps) {
+ const { services } = useKibana();
+ const {
+ triggersActionsUi: {
+ getRuleSnoozeModal: RuleSnoozeModal,
+ getUntrackModal: UntrackAlertsModal,
+ getRuleHelpers,
+ },
+ } = services;
+
const [isRuleEditPopoverOpen, setIsRuleEditPopoverOpen] = useState(false);
+ const [snoozeModalOpen, setSnoozeModalOpen] = useState(false);
+ const [isUntrackAlertsModalOpen, setIsUntrackAlertsModalOpen] = useState(false);
+
+ const { mutateAsync: enableRule } = useEnableRule();
+ const { mutateAsync: disableRule } = useDisableRule();
+
+ const onDisableModalClose = () => {
+ setIsUntrackAlertsModalOpen(false);
+ };
+
+ const onDisableModalOpen = () => {
+ setIsUntrackAlertsModalOpen(true);
+ };
const togglePopover = () => setIsRuleEditPopoverOpen(!isRuleEditPopoverOpen);
@@ -43,57 +71,142 @@ export function HeaderActions({
onDeleteRule();
};
- return isRuleEditable ? (
-
-
-
-
-
- ) : null;
+
+ }
+ >
+
+ {
+ setSnoozeModalOpen(true);
+ }}
+ >
+
+ {i18n.translate('xpack.observability.ruleDetails.snoozeButton.snoozeSchedule', {
+ defaultMessage: 'Update snooze schedule',
+ })}
+
+
+ {rule.enabled ? (
+
+
+ {i18n.translate('xpack.observability.ruleDetails.disableRule', {
+ defaultMessage: 'Disable',
+ })}
+
+
+ ) : (
+
+
+ {i18n.translate('xpack.observability.ruleDetails.enableRule', {
+ defaultMessage: 'Enable',
+ })}
+
+
+ )}
+
+
+ {i18n.translate('xpack.observability.ruleDetails.editRule', {
+ defaultMessage: 'Edit rule',
+ })}
+
+
+
+
+ {i18n.translate('xpack.observability.ruleDetails.deleteRule', {
+ defaultMessage: 'Delete rule',
+ })}
+
+
+
+
+
+
+
+ {snoozeModalOpen && (
+ {
+ setSnoozeModalOpen(false);
+ setIsRuleEditPopoverOpen(false);
+ }}
+ onRuleChanged={async () => {
+ refetch();
+ }}
+ onLoading={noop}
+ />
+ )}
+
+ {isUntrackAlertsModalOpen && (
+
+ )}
+ >
+ );
}
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/rule_details.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/rule_details.tsx
index 449ae1009fd30..b1f6258f8fb15 100644
--- a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/rule_details.tsx
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/rule_details.tsx
@@ -206,14 +206,17 @@ export function RuleDetailsPage() {
},
children: ,
bottomBorder: false,
- rightSideItems: [
- ,
- ],
+ rightSideItems: ruleId
+ ? [
+ ,
+ ]
+ : [],
}}
>