diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts index 49b94eaf5c6a4..42c9894da9a8b 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts @@ -119,7 +119,6 @@ journey('AlertingDefaults', async ({ page, params }) => { await page.click('.euiForm'); await page.click('text=To: Email is required for selected email connector'); }); - step('Fill email fields', async () => { await page .getByTestId('toEmailAddressInput') diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts index c57d154e6f079..3e2a4467bd61a 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts @@ -115,7 +115,7 @@ journey('ProjectMonitorReadOnly', async ({ page, params }) => { step('Monitor can be deleted', async () => { await page.click('text="Delete monitor"'); await page.click('[data-test-subj="confirmModalConfirmButton"]'); - await page.waitForSelector(`text='Deleted "${monitorName}"'`); + await page.waitForSelector(`text='Deleted "${monitorName}" monitor successfully.'`); }); after(async () => { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx index db416a713887d..6ce09a521c126 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx @@ -36,9 +36,7 @@ export const ActionBar = ({ formState: { defaultValues, isValid }, } = useFormContext(); - const [monitorPendingDeletion, setMonitorPendingDeletion] = useState( - null - ); + const [monitorsPendingDeletion, setMonitorsPendingDeletion] = useState([]); const [monitorData, setMonitorData] = useState(undefined); @@ -66,7 +64,7 @@ export const ActionBar = ({ data-test-subj="syntheticsActionBarButton" color="danger" onClick={() => { - setMonitorPendingDeletion(defaultValues as SyntheticsMonitor); + setMonitorsPendingDeletion([monitorId]); }} isDisabled={!canEditSynthetics || !canUsePublicLocations} > @@ -107,14 +105,14 @@ export const ActionBar = ({ - {monitorPendingDeletion && ( + {monitorsPendingDeletion.length > 0 && ( { history.push(MONITORS_ROUTE); }} - setMonitorPendingDeletion={setMonitorPendingDeletion} + setMonitorPendingDeletion={setMonitorsPendingDeletion} isProjectMonitor={defaultValues?.[ConfigKey.MONITOR_SOURCE_TYPE] === SourceType.PROJECT} /> )} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/bulk_operations.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/bulk_operations.tsx new file mode 100644 index 0000000000000..41c2dc7ea8de4 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/bulk_operations.tsx @@ -0,0 +1,46 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { + ConfigKey, + EncryptedSyntheticsSavedMonitor, +} from '../../../../../../../common/runtime_types'; + +export const BulkOperations = ({ + selectedItems, + setMonitorPendingDeletion, +}: { + selectedItems: EncryptedSyntheticsSavedMonitor[]; + setMonitorPendingDeletion: (val: string[]) => void; +}) => { + const onDeleted = () => { + setMonitorPendingDeletion(selectedItems.map((item) => item[ConfigKey.CONFIG_ID])); + }; + + if (selectedItems.length === 0) { + return null; + } + + return ( + + {i18n.translate('xpack.synthetics.bulkOperationPopover.clickMeToLoadButtonLabel', { + defaultMessage: + 'Delete {monitorCount, number} selected {monitorCount, plural, one {monitor} other {monitors}}', + values: { monitorCount: selectedItems.length }, + })} + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx index 1369fb086adab..90c14131a7cb0 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx @@ -45,7 +45,7 @@ export function useMonitorListColumns({ }: { loading: boolean; overviewStatus: OverviewStatusState | null; - setMonitorPendingDeletion: (config: EncryptedSyntheticsSavedMonitor) => void; + setMonitorPendingDeletion: (configs: string[]) => void; }): Array> { const history = useHistory(); const { http } = useKibana().services; @@ -239,7 +239,7 @@ export function useMonitorListColumns({ enabled: (fields) => canEditSynthetics && !isActionLoading(fields) && isPublicLocationsAllowed(fields), onClick: (fields) => { - setMonitorPendingDeletion(fields); + setMonitorPendingDeletion([fields[ConfigKey.CONFIG_ID]]); }, }, { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx index dab31650aec4d..1c01da7a21af5 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx @@ -19,15 +19,15 @@ import * as labels from './labels'; export const DeleteMonitor = ({ name, reloadPage, - configId, + configIds, isProjectMonitor, setMonitorPendingDeletion, }: { - configId: string; + configIds: string[]; name: string; isProjectMonitor: boolean; reloadPage: () => void; - setMonitorPendingDeletion: (val: null) => void; + setMonitorPendingDeletion: (val: string[]) => void; }) => { const [isDeleting, setIsDeleting] = useState(false); @@ -37,9 +37,9 @@ export const DeleteMonitor = ({ const { status: monitorDeleteStatus } = useFetcher(() => { if (isDeleting) { - return fetchDeleteMonitor({ configId }); + return fetchDeleteMonitor({ configIds }); } - }, [configId, isDeleting]); + }, [configIds, isDeleting]); useEffect(() => { const { coreStart, toasts } = kibanaService; @@ -64,13 +64,19 @@ export const DeleteMonitor = ({ { title: toMountPoint(

- {i18n.translate( - 'xpack.synthetics.monitorManagement.monitorDeleteSuccessMessage.name', - { - defaultMessage: 'Deleted "{name}"', - values: { name }, - } - )} + {configIds.length === 1 + ? i18n.translate( + 'xpack.synthetics.monitorManagement.monitorDeleteSuccessMessage.name', + { + defaultMessage: 'Deleted "{name}" monitor successfully.', + values: { name }, + } + ) + : i18n.translate('xpack.synthetics.monitorManagement.successDeletion', { + defaultMessage: + 'Deleted {monitorCount, number} {monitorCount, plural, one {monitor} other {monitors}} successfully.', + values: { monitorCount: configIds.length }, + })}

, coreStart ), @@ -83,17 +89,33 @@ export const DeleteMonitor = ({ monitorDeleteStatus === FETCH_STATUS.FAILURE ) { setIsDeleting(false); - setMonitorPendingDeletion(null); + setMonitorPendingDeletion([]); } - }, [setIsDeleting, isDeleting, reloadPage, monitorDeleteStatus, setMonitorPendingDeletion, name]); + }, [ + setIsDeleting, + isDeleting, + reloadPage, + monitorDeleteStatus, + setMonitorPendingDeletion, + name, + configIds.length, + ]); return ( setMonitorPendingDeletion(null)} + title={ + configIds.length === 1 + ? i18n.translate('xpack.synthetics.monitorManagement.deleteMonitorNameLabel', { + defaultMessage: 'Delete "{name}" monitor?', + values: { name }, + }) + : i18n.translate('xpack.synthetics.monitorManagement.deleteMonitorNameLabel', { + defaultMessage: + 'Delete {monitorCount, number} selected {monitorCount, plural, one {monitor} other {monitors}}?', + values: { monitorCount: configIds.length }, + }) + } + onCancel={() => setMonitorPendingDeletion([])} onConfirm={handleConfirmDelete} cancelButtonText={labels.NO_LABEL} confirmButtonText={labels.YES_LABEL} @@ -118,7 +140,7 @@ export const DeleteMonitor = ({ export const PROJECT_MONITOR_TITLE = i18n.translate( 'xpack.synthetics.monitorManagement.monitorList.disclaimer.title', { - defaultMessage: 'Deleting this monitor will not remove it from the project source', + defaultMessage: 'Deleting project monitor will not remove it from the project source', } ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx index 7ae4196b5e318..03835881a2c55 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx @@ -15,6 +15,8 @@ import { useIsWithinMinBreakpoint, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; +import { MonitorListHeader } from './monitor_list_header'; import type { MonitorListSortField } from '../../../../../../../common/runtime_types/monitor_management/sort_field'; import { DeleteMonitor } from './delete_monitor'; import { IHttpSerializedFetchError } from '../../../../state/utils/http_error'; @@ -51,8 +53,7 @@ export const MonitorList = ({ }: Props) => { const isXl = useIsWithinMinBreakpoint('xxl'); - const [monitorPendingDeletion, setMonitorPendingDeletion] = - useState(null); + const [monitorPendingDeletion, setMonitorPendingDeletion] = useState([]); const handleOnChange = useCallback( ({ @@ -98,10 +99,24 @@ export const MonitorList = ({ setMonitorPendingDeletion, }); + const [selectedItems, setSelectedItems] = useState([]); + const onSelectionChange = (selItems: EncryptedSyntheticsSavedMonitor[]) => { + setSelectedItems(selItems); + }; + + const selection: EuiTableSelectionType = { + onSelectionChange, + initialSelected: selectedItems, + }; + return ( <> - {recordRangeLabel} + - {monitorPendingDeletion && ( + {monitorPendingDeletion.length > 0 && ( mon[ConfigKey.CONFIG_ID] === monitorPendingDeletion[0] + )?.[ConfigKey.NAME] ?? '' + } setMonitorPendingDeletion={setMonitorPendingDeletion} isProjectMonitor={ - monitorPendingDeletion[ConfigKey.MONITOR_SOURCE_TYPE] === SourceType.PROJECT + syntheticsMonitors.find( + (mon) => mon[ConfigKey.CONFIG_ID] === monitorPendingDeletion[0] + )?.[ConfigKey.MONITOR_SOURCE_TYPE] === SourceType.PROJECT } reloadPage={reloadPage} /> diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list_header.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list_header.tsx new file mode 100644 index 0000000000000..f838008898385 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list_header.tsx @@ -0,0 +1,36 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import React from 'react'; +import { BulkOperations } from './bulk_operations'; +import { EncryptedSyntheticsSavedMonitor } from '../../../../../../../common/runtime_types'; + +export const MonitorListHeader = ({ + selectedItems, + recordRangeLabel, + setMonitorPendingDeletion, +}: { + recordRangeLabel: JSX.Element; + selectedItems: EncryptedSyntheticsSavedMonitor[]; + setMonitorPendingDeletion: (val: string[]) => void; +}) => { + return ( + + + {recordRangeLabel} + + + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/api.ts index 12ab246beacb9..4994921598997 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/api.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/api.ts @@ -49,12 +49,12 @@ export const fetchMonitorManagementList = async ( }); }; -export const fetchDeleteMonitor = async ({ configId }: { configId: string }): Promise => { +export const fetchDeleteMonitor = async ({ configIds }: { configIds: string[] }): Promise => { return await apiService.delete( SYNTHETICS_API_URLS.SYNTHETICS_MONITORS, { version: INITIAL_REST_VERSION }, { - ids: [configId], + ids: configIds, } ); }; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index cc0f7067d89e0..da06991ed2beb 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -44939,7 +44939,6 @@ "xpack.synthetics.monitorManagement.deleteLocation": "Supprimer l’emplacement", "xpack.synthetics.monitorManagement.deleteLocationLabel": "Supprimer l’emplacement", "xpack.synthetics.monitorManagement.deleteLocationName": "Supprimer \"{location}\"", - "xpack.synthetics.monitorManagement.deleteMonitorNameLabel": "Supprimer le moniteur \"{name}\"", "xpack.synthetics.monitorManagement.disabled.label": "Désactivé", "xpack.synthetics.monitorManagement.discardLabel": "Annuler", "xpack.synthetics.monitorManagement.editMonitorCrumb": "Modifier le moniteur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 519c9f2a428a9..5d63aaa7da1b1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -44921,7 +44921,6 @@ "xpack.synthetics.monitorManagement.deleteLocation": "場所を削除", "xpack.synthetics.monitorManagement.deleteLocationLabel": "場所を削除", "xpack.synthetics.monitorManagement.deleteLocationName": "\"{location}\"を削除", - "xpack.synthetics.monitorManagement.deleteMonitorNameLabel": "\"{name}\"モニターを削除しますか?", "xpack.synthetics.monitorManagement.disabled.label": "無効", "xpack.synthetics.monitorManagement.discardLabel": "キャンセル", "xpack.synthetics.monitorManagement.editMonitorCrumb": "モニターを編集", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 328008ebf3952..4caba8c275a4b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -44969,7 +44969,6 @@ "xpack.synthetics.monitorManagement.deleteLocation": "删除位置", "xpack.synthetics.monitorManagement.deleteLocationLabel": "删除位置", "xpack.synthetics.monitorManagement.deleteLocationName": "删除“{location}”", - "xpack.synthetics.monitorManagement.deleteMonitorNameLabel": "删除“{name}”监测?", "xpack.synthetics.monitorManagement.disabled.label": "已禁用", "xpack.synthetics.monitorManagement.discardLabel": "取消", "xpack.synthetics.monitorManagement.editMonitorCrumb": "编辑监测",