From bc455f8aabc4bbdbc4503009ee795aaf24d1fba3 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 22 Jan 2024 17:42:30 +0100 Subject: [PATCH] Improve global alerts page composition and filtering, move to correct menu --- .../triggers_actions_ui/common/index.ts | 1 - .../alert_table_config_registry.ts | 2 +- .../public/application/alerts_app.tsx | 52 +-- .../public/application/app.tsx | 1 - .../public/application/constants/index.ts | 8 +- .../public/application/home.tsx | 4 +- .../public/application/lib/breadcrumb.ts | 19 +- .../public/application/lib/doc_title.ts | 5 + .../public/application/lib/search_filters.ts | 38 ++ .../url_synced_alerts_search_bar.tsx | 40 +- .../alerts_table/alerts_table_state.tsx | 6 +- .../sections/alerts_table/configuration.tsx | 67 ++- .../sections/alerts_table/constants.ts | 73 +--- .../sections/global_alerts/index.tsx | 412 ------------------ .../global_alerts_page/global_alerts_page.tsx | 239 ++++++++++ .../hooks/use_rule_stats.tsx | 155 +++++++ .../sections/global_alerts_page/index.tsx} | 6 +- .../application/sections/translations.ts | 64 +++ .../public/common/constants/index.ts | 1 + .../triggers_actions_ui/public/plugin.ts | 54 ++- 20 files changed, 639 insertions(+), 608 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/search_filters.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts/index.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/global_alerts_page.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/hooks/use_rule_stats.tsx rename x-pack/plugins/triggers_actions_ui/{common/alert_config.ts => public/application/sections/global_alerts_page/index.tsx} (62%) diff --git a/x-pack/plugins/triggers_actions_ui/common/index.ts b/x-pack/plugins/triggers_actions_ui/common/index.ts index cd049f3620a29..142f6e8ac9c81 100644 --- a/x-pack/plugins/triggers_actions_ui/common/index.ts +++ b/x-pack/plugins/triggers_actions_ui/common/index.ts @@ -13,4 +13,3 @@ export const BASE_TRIGGERS_ACTIONS_UI_API_PATH = '/internal/triggers_actions_ui' export * from './parse_interval'; export * from './experimental_features'; export * from './normalized_field_types'; -export * from './alert_config'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/alert_table_config_registry.ts b/x-pack/plugins/triggers_actions_ui/public/application/alert_table_config_registry.ts index 6d5ddc28303fb..37b2ddbd3b4e0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/alert_table_config_registry.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/alert_table_config_registry.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { noop } from 'lodash'; -import { ALERT_TABLE_GENERIC_CONFIG_ID } from '../../common/alert_config'; +import { ALERT_TABLE_GENERIC_CONFIG_ID } from './constants'; import { AlertsTableConfigurationRegistry, AlertsTableConfigurationRegistryWithActions, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/alerts_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/alerts_app.tsx index cd0fb55b55e2d..9bf267fb9852a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/alerts_app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/alerts_app.tsx @@ -7,60 +7,20 @@ import React, { lazy } from 'react'; import { Router, Routes, Route } from '@kbn/shared-ux-router'; -import { ChromeBreadcrumb, CoreStart, CoreTheme, ScopedHistory } from '@kbn/core/public'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n-react'; -import useObservable from 'react-use/lib/useObservable'; -import { Observable } from 'rxjs'; -import { KibanaFeature } from '@kbn/features-plugin/common'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { PluginStartContract as AlertingStart } from '@kbn/alerting-plugin/public'; -import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; -import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public'; -import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; -import { - ActionTypeRegistryContract, - AlertsTableConfigurationRegistryContract, - RuleTypeRegistryContract, -} from '../types'; import { setDataViewsService } from '../common/lib/data_apis'; import { KibanaContextProvider } from '../common/lib/kibana'; +import type { TriggersAndActionsUiServices } from './app'; -const GlobalAlerts = lazy(() => import('./sections/global_alerts')); +const GlobalAlertsPage = lazy(() => import('./sections/global_alerts_page')); -export interface TriggersAndActionsUiServices extends CoreStart { - actions: ActionsPublicPluginSetup; - data: DataPublicPluginStart; - dataViews: DataViewsPublicPluginStart; - dataViewEditor: DataViewEditorStart; - dashboard: DashboardStart; - charts: ChartsPluginStart; - alerting?: AlertingStart; - spaces?: SpacesPluginStart; - storage?: Storage; - isCloud: boolean; - setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; - actionTypeRegistry: ActionTypeRegistryContract; - ruleTypeRegistry: RuleTypeRegistryContract; - alertsTableConfigurationRegistry: AlertsTableConfigurationRegistryContract; - history: ScopedHistory; - kibanaFeatures: KibanaFeature[]; - element: HTMLElement; - theme$: Observable; - unifiedSearch: UnifiedSearchPublicPluginStart; -} - -export const renderApp = (deps: Partial) => { +export const renderApp = (deps: TriggersAndActionsUiServices) => { const { element } = deps; render(, element); return () => { @@ -69,8 +29,8 @@ export const renderApp = (deps: Partial) => { }; export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => { - const { dataViews, uiSettings, theme$ } = deps; - const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); + const { dataViews, theme, theme$ } = deps; + const isDarkMode = theme.getTheme().darkMode; setDataViewsService(dataViews); return ( @@ -80,7 +40,7 @@ export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => { - + diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 585f65954254a..11115822ccfca 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -140,7 +140,6 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = /> - ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 649af1c9fa07a..95896b47f9df3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -10,6 +10,7 @@ import { ES_QUERY_ID, OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, ML_ANOMALY_DETECTION_RULE_TYPE_ID, + AlertConsumers, } from '@kbn/rule-data-utils'; export { BASE_ALERTING_API_PATH, @@ -23,8 +24,7 @@ export const routeToHome = `/`; export const routeToConnectors = `/connectors`; export const routeToRules = `/rules`; export const routeToLogs = `/logs`; -export const routeToInternalAlerts = `/alerts`; -export const legacyRouteToRules = `/alerts`; +export const routeToAlerts = `/alerts`; export const legacyRouteToRuleDetails = `/alert/:alertId`; export const recoveredActionGroupMessage = i18n.translate( @@ -129,3 +129,7 @@ export const MULTI_CONSUMER_RULE_TYPE_IDS = [ ES_QUERY_ID, ML_ANOMALY_DETECTION_RULE_TYPE_ID, ]; + +export const ALERT_TABLE_GENERIC_CONFIG_ID = `${AlertConsumers.STACK_ALERTS}-generic-alerts-table`; + +export const ALERT_TABLE_GLOBAL_CONFIG_ID = `${AlertConsumers.STACK_ALERTS}-global-alerts-table`; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 585327ff340c9..97163920f94c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -13,7 +13,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiPageTemplate } from '@elastic/eui'; import { getIsExperimentalFeatureEnabled } from '../common/get_experimental_features'; -import { Section, routeToRules, routeToInternalAlerts, routeToLogs } from './constants'; +import { Section, routeToRules, routeToAlerts, routeToLogs } from './constants'; import { getAlertingSectionBreadcrumb } from './lib/breadcrumb'; import { getCurrentDocTitle } from './lib/doc_title'; @@ -144,7 +144,7 @@ export const TriggersActionsUIHome: React.FunctionComponent ( {suspendedComponentWithProps(AlertsPage, 'xl')({})} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts index c0b9551968870..066f37e70dbdb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts @@ -6,7 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import { routeToHome, routeToConnectors, routeToRules, routeToLogs } from '../constants'; +import { + routeToHome, + routeToConnectors, + routeToRules, + routeToLogs, + routeToAlerts, +} from '../constants'; export const getAlertingSectionBreadcrumb = ( type: string, @@ -47,6 +53,17 @@ export const getAlertingSectionBreadcrumb = ( } : {}), }; + case 'alerts': + return { + text: i18n.translate('xpack.triggersActionsUI.alerts.breadcrumbTitle', { + defaultMessage: 'Alerts', + }), + ...(returnHref + ? { + href: `${routeToAlerts}`, + } + : {}), + }; default: return { text: i18n.translate('xpack.triggersActionsUI.home.breadcrumbTitle', { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/doc_title.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/doc_title.ts index 16699e750d223..e4821307bbd06 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/doc_title.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/doc_title.ts @@ -26,6 +26,11 @@ export const getCurrentDocTitle = (page: string): string => { defaultMessage: 'Rules', }); break; + case 'alerts': + updatedTitle = i18n.translate('xpack.triggersActionsUI.alerts.breadcrumbTitle', { + defaultMessage: 'Alerts', + }); + break; default: updatedTitle = i18n.translate('xpack.triggersActionsUI.home.breadcrumbTitle', { defaultMessage: 'Rules', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/search_filters.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/search_filters.ts new file mode 100644 index 0000000000000..c2f5372d294b7 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/search_filters.ts @@ -0,0 +1,38 @@ +/* + * 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 { ALERT_RULE_PRODUCER, AlertConsumers, DefaultAlertFieldName } from '@kbn/rule-data-utils'; +import { FILTERS, PhraseFilter } from '@kbn/es-query'; + +/** + * Creates a match_phrase filter without an index pattern + */ +export const createMatchPhraseFilter = (field: DefaultAlertFieldName, value: unknown) => + ({ + meta: { + field, + type: FILTERS.PHRASE, + key: field, + alias: null, + disabled: false, + index: undefined, + negate: false, + params: { query: value }, + value: undefined, + }, + query: { + match_phrase: { + [field]: value, + }, + }, + } as PhraseFilter); + +/** + * Creates a match_phrase filter targeted to filtering alerts by producer + */ +export const createRuleProducerFilter = (producer: AlertConsumers) => + createMatchPhraseFilter(ALERT_RULE_PRODUCER, producer); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx index 7b3d2eb6a2001..98a1d8ca1c00c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx @@ -8,26 +8,24 @@ import React, { useCallback, useEffect } from 'react'; import { BoolQuery, FILTERS, PhraseFilter } from '@kbn/es-query'; import { ALERT_RULE_PRODUCER, AlertConsumers } from '@kbn/rule-data-utils'; -import { buildEsQuery } from '../global_alerts'; import { useKibana } from '../../..'; import { useAlertSearchBarStateContainer } from './use_alert_search_bar_state_container'; -import { ALERTS_URL_STORAGE_KEY, NON_SIEM_FEATURE_IDS } from './constants'; +import { ALERTS_URL_STORAGE_KEY } from './constants'; import { AlertsSearchBarProps } from './types'; import AlertsSearchBar from './alerts_search_bar'; +import { buildEsQuery } from '../global_alerts_page/global_alerts_page'; export type UrlSyncedAlertsSearchBarProps = Omit< AlertsSearchBarProps, 'query' | 'rangeFrom' | 'rangeTo' | 'filters' | 'onQuerySubmit' > & { onEsQueryChange: (esQuery: { bool: BoolQuery }) => void; - onFeatureIdsChange: (featureIds: AlertConsumers[]) => void; - onFilteringBySolutionChange?: (value: boolean) => void; + onActiveFeatureFiltersChange?: (value: AlertConsumers[]) => void; }; export const UrlSyncedAlertsSearchBar = ({ onEsQueryChange, - onFeatureIdsChange, - onFilteringBySolutionChange, + onActiveFeatureFiltersChange, ...rest }: UrlSyncedAlertsSearchBarProps) => { const { @@ -55,15 +53,17 @@ export const UrlSyncedAlertsSearchBar = ({ f.meta.key === ALERT_RULE_PRODUCER && (f.meta.type === FILTERS.PHRASE || f.meta.type === FILTERS.PHRASES) ); - onFilteringBySolutionChange?.(solutionFilters.length > 0); - onFeatureIdsChange( - !solutionFilters.length || - solutionFilters.filter( - (f) => (f as PhraseFilter).meta.params?.query !== AlertConsumers.SIEM - ).length > 0 - ? NON_SIEM_FEATURE_IDS - : [AlertConsumers.SIEM] - ); + onActiveFeatureFiltersChange?.([ + ...new Set( + solutionFilters + .flatMap((f) => + f.meta.type === FILTERS.PHRASES + ? (f.meta.params as AlertConsumers[]) + : [(f as PhraseFilter).meta.params?.query as AlertConsumers] + ) + .filter(Boolean) + ), + ]); const newQuery = buildEsQuery({ timeRange: { to: rangeTo, @@ -76,15 +76,7 @@ export const UrlSyncedAlertsSearchBar = ({ } catch (e) { // TODO show message? } - }, [ - filters, - kuery, - onEsQueryChange, - onFeatureIdsChange, - onFilteringBySolutionChange, - rangeFrom, - rangeTo, - ]); + }, [filters, kuery, onActiveFeatureFiltersChange, onEsQueryChange, rangeFrom, rangeTo]); useEffect(() => { syncEsQuery(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx index 1e98865664c5a..17cef9505d6f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx @@ -37,6 +37,7 @@ import { Alert, Alerts, AlertsTableConfigurationRegistry, + AlertsTableConfigurationRegistryContract, AlertsTableProps, RowSelectionState, TableUpdateHandlerArgs, @@ -49,7 +50,6 @@ import { alertsTableQueryClient } from './query_client'; import { useBulkGetCases } from './hooks/use_bulk_get_cases'; import { useBulkGetMaintenanceWindows } from './hooks/use_bulk_get_maintenance_windows'; import { CasesService } from './types'; -import { AlertTableConfigRegistry } from '../../alert_table_config_registry'; import { AlertsTableContext, AlertsTableQueryContext } from './contexts/alerts_table_context'; const DefaultPagination = { @@ -58,7 +58,7 @@ const DefaultPagination = { }; export type AlertsTableStateProps = { - alertsTableConfigurationRegistry: AlertTableConfigRegistry; + alertsTableConfigurationRegistry: AlertsTableConfigurationRegistryContract; configurationId: string; id: string; featureIds: ValidFeatureId[]; @@ -255,8 +255,6 @@ const AlertsTableStateWithQueryProvider = ({ skip: false, }); - console.log('Alerts', alerts); - const { data: mutedAlerts } = useGetMutedAlerts([ ...new Set(alerts.map((a) => a['kibana.alert.rule.uuid']![0])), ]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx index d1c9c28a554a7..c91fa283eac7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx @@ -10,17 +10,19 @@ import { ALERT_DURATION, ALERT_MAINTENANCE_WINDOW_IDS, ALERT_REASON, + ALERT_RULE_PRODUCER, ALERT_STATUS, TIMESTAMP, } from '@kbn/rule-data-utils'; -import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { i18n } from '@kbn/i18n'; +import { FEATURE_LABEL } from '../translations'; import { getDefaultAlertFlyout } from './alerts_flyout/default_alerts_flyout'; import { AlertActionsCell } from './row_actions/alert_actions_cell'; import { AlertsTableConfigurationRegistry, RenderCustomActionsRowArgs } from '../../../types'; import { getAlertFormatters, getRenderCellValue } from './cells/render_cell_value'; -import { ALERT_TABLE_GENERIC_CONFIG_ID } from '../../../../common'; +import { ALERT_TABLE_GENERIC_CONFIG_ID, ALERT_TABLE_GLOBAL_CONFIG_ID } from '../../constants'; const columns = [ { @@ -31,6 +33,12 @@ const columns = [ id: ALERT_STATUS, initialWidth: 140, }, + { + displayAsText: FEATURE_LABEL, + id: ALERT_RULE_PRODUCER, + schema: 'string', + initialWidth: 180, + }, { columnHeaderType: 'not-filtered', displayAsText: i18n.translate( @@ -73,25 +81,40 @@ const columns = [ }, ]; -export const getAlertsTableConfiguration = ( +const sort: SortCombinations[] = [ + { + [TIMESTAMP]: { + order: 'desc', + }, + }, +]; + +const useActionsColumn = () => ({ + renderCustomActionsRow: (props: RenderCustomActionsRowArgs) => { + return ; + }, +}); + +export const createGenericAlertsTableConfigurations = ( fieldFormats: FieldFormatsRegistry -): AlertsTableConfigurationRegistry => { - return { - id: ALERT_TABLE_GENERIC_CONFIG_ID, - columns, - getRenderCellValue: getRenderCellValue(fieldFormats), - useInternalFlyout: getDefaultAlertFlyout(columns, getAlertFormatters(fieldFormats)), - sort: [ - { - [TIMESTAMP]: { - order: 'desc' as SortOrder, - }, - }, - ], - useActionsColumn: () => ({ - renderCustomActionsRow: (props: RenderCustomActionsRowArgs) => { - return ; - }, - }), - }; +): AlertsTableConfigurationRegistry[] => { + const [firstColumn, _, ...genericColumns] = columns; + return [ + { + id: ALERT_TABLE_GENERIC_CONFIG_ID, + columns: [firstColumn, ...genericColumns], + getRenderCellValue: getRenderCellValue(fieldFormats), + useInternalFlyout: getDefaultAlertFlyout(columns, getAlertFormatters(fieldFormats)), + sort, + useActionsColumn, + }, + { + id: ALERT_TABLE_GLOBAL_CONFIG_ID, + columns, + getRenderCellValue: getRenderCellValue(fieldFormats), + useInternalFlyout: getDefaultAlertFlyout(columns, getAlertFormatters(fieldFormats)), + sort, + useActionsColumn, + }, + ]; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/constants.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/constants.ts index 576d5edd25794..792cb4efa0204 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/constants.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/constants.ts @@ -6,72 +6,25 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { i18n } from '@kbn/i18n'; import { AlertConsumers } from '@kbn/rule-data-utils'; - -export const OBSERVABILITY_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.observability', - { - defaultMessage: 'Observability', - } -); - -export const SECURITY_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.security', - { - defaultMessage: 'Security', - } -); - -export const STACK_MANAGEMENT_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.stackManagement', - { - defaultMessage: 'Stack management', - } -); - -export const UPTIME_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.uptime', - { - defaultMessage: 'Uptime', - } -); - -export const APM_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.apm', { - defaultMessage: 'APM', -}); - -export const INFRASTRUCTURE_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.infrastructure', - { - defaultMessage: 'Infrastructure', - } -); - -export const SLO_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.slos', - { - defaultMessage: 'SLOs', - } -); - -export const LOGS_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.logs', - { - defaultMessage: 'Logs', - } -); - -export const ML_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.ml', { - defaultMessage: 'Machine Learning', -}); +import { + APM_DISPLAY_NAME, + INFRASTRUCTURE_DISPLAY_NAME, + LOGS_DISPLAY_NAME, + ML_DISPLAY_NAME, + OBSERVABILITY_DISPLAY_NAME, + SECURITY_DISPLAY_NAME, + SLO_DISPLAY_NAME, + STACK_MANAGEMENT_DISPLAY_NAME, + UPTIME_DISPLAY_NAME, +} from '../translations'; interface AlertProducerData { displayName: string; icon: EuiIconType; } -export const observabilityProducers = [ +export const observabilityFeatureIds: AlertConsumers[] = [ AlertConsumers.OBSERVABILITY, AlertConsumers.APM, AlertConsumers.INFRASTRUCTURE, @@ -80,7 +33,7 @@ export const observabilityProducers = [ AlertConsumers.UPTIME, ]; -export const [_, ...observabilityApps] = observabilityProducers; +export const [_, ...observabilityApps] = observabilityFeatureIds; export const alertProducersData: Record = { [AlertConsumers.OBSERVABILITY]: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts/index.tsx deleted file mode 100644 index b5fad6b6c4839..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts/index.tsx +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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, { lazy, useEffect, useMemo, useState } from 'react'; -import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiDataGridColumn, EuiFlexGroup, EuiStat } from '@elastic/eui'; -import styled from '@emotion/styled'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { - ALERT_RULE_PRODUCER, - ALERT_STATUS, - ALERT_STATUS_ACTIVE, - ALERT_STATUS_RECOVERED, - ALERT_STATUS_UNTRACKED, - ALERT_TIME_RANGE, - AlertConsumers, - DefaultAlertFieldName, -} from '@kbn/rule-data-utils'; -import { QueryClientProvider } from '@tanstack/react-query'; -import { - BoolQuery, - buildEsQuery as kbnBuildEsQuery, - EsQueryConfig, - Filter, - FILTERS, - PhraseFilter, - PhrasesFilter, - Query, - TimeRange, -} from '@kbn/es-query'; -import { getTime } from '@kbn/data-plugin/common'; -import { QuickFiltersMenuItem } from '@kbn/unified-search-plugin/public/query_string_input/quick_filters'; -import { NON_SIEM_FEATURE_IDS } from '../alerts_search_bar/constants'; -import { - alertProducersData, - observabilityApps, - observabilityProducers, -} from '../alerts_table/constants'; -import { UrlSyncedAlertsSearchBar } from '../alerts_search_bar/url_synced_alerts_search_bar'; -import { ALERT_TABLE_GENERIC_CONFIG_ID } from '../../../../common'; -import { AlertTableConfigRegistry } from '../../alert_table_config_registry'; -import { loadRuleAggregations } from '../../..'; -import { useKibana } from '../../../common/lib/kibana'; -import { alertsTableQueryClient } from '../alerts_table/query_client'; -import { - alertSearchBarStateContainer, - Provider, -} from '../alerts_search_bar/use_alert_search_bar_state_container'; -const AlertsTable = lazy(() => import('../alerts_table/alerts_table_state')); - -const Stat = styled(EuiStat)` - .euiText { - line-height: 1; - } -`; - -interface StatsProps { - stats: { - total: number; - disabled: number; - muted: number; - error: number; - snoozed: number; - }; - loading: boolean; - manageRulesHref: string; -} - -const Divider = styled.div` - border-right: 1px solid ${euiThemeVars.euiColorLightShade}; - height: 100%; -`; - -const SOLUTION_OR_APP_TITLE = i18n.translate( - 'xpack.triggersActionsUI.sections.globalAlerts.quickFilters.solutionOrApp', - { - defaultMessage: 'Solution/app', - } -); - -const getStatNodes = ({ stats, loading, manageRulesHref }: StatsProps) => { - const disabledStatsComponent = ( - 0 ? 'primary' : ''} - titleSize="xs" - isLoading={loading} - data-test-subj="statDisabled" - /> - ); - - const snoozedStatsComponent = ( - 0 ? 'primary' : ''} - titleSize="xs" - isLoading={loading} - data-test-subj="statMuted" - /> - ); - - const errorStatsComponent = ( - 0 ? 'primary' : ''} - titleSize="xs" - isLoading={loading} - data-test-subj="statErrors" - /> - ); - - return [ - , - disabledStatsComponent, - snoozedStatsComponent, - errorStatsComponent, - , - - {i18n.translate('xpack.triggersActionsUI.globalAlerts.manageRulesButtonLabel', { - defaultMessage: 'Manage Rules', - })} - , - ].reverse(); -}; - -interface BuildEsQueryArgs { - timeRange?: TimeRange; - kuery?: string; - queries?: Query[]; - config?: EsQueryConfig; - filters?: Filter[]; -} - -export function buildEsQuery({ - timeRange, - kuery, - filters = [], - queries = [], - config = {}, -}: BuildEsQueryArgs) { - const timeFilter = - timeRange && - getTime(undefined, timeRange, { - fieldName: ALERT_TIME_RANGE, - }); - const filtersToUse = [...(timeFilter ? [timeFilter] : []), ...filters]; - const kueryFilter = kuery ? [{ query: kuery, language: 'kuery' }] : []; - const queryToUse = [...kueryFilter, ...queries]; - return kbnBuildEsQuery(undefined, queryToUse, filtersToUse, config); -} - -const createMatchPhraseFilter = (field: DefaultAlertFieldName, value: unknown) => - ({ - meta: { - field, - type: FILTERS.PHRASE, - key: field, - alias: null, - disabled: false, - index: undefined, - negate: false, - params: { query: value }, - value: undefined, - }, - query: { - match_phrase: { - [field]: value, - }, - }, - } as PhraseFilter); - -const createRuleProducerFilter = (producer: AlertConsumers) => - createMatchPhraseFilter(ALERT_RULE_PRODUCER, producer); - -export const GlobalAlerts = () => { - const { - http, - chrome: { docTitle, setBreadcrumbs }, - notifications: { toasts }, - alertsTableConfigurationRegistry, - } = useKibana().services; - const [esQuery, setEsQuery] = useState({ bool: {} } as { bool: BoolQuery }); - const [statsLoading, setStatsLoading] = useState(false); - const [featureIds, setFeatureIds] = useState(NON_SIEM_FEATURE_IDS); - const [filteringBySolution, setFilteringBySolution] = useState(false); - const [stats, setStats] = useState({ - total: 0, - disabled: 0, - muted: 0, - error: 0, - snoozed: 0, - }); - const manageRulesHref = http.basePath.prepend( - '/app/management/insightsAndAlerting/triggersActions/rules' - ); - const browsingSiem = useMemo( - () => featureIds.length === 1 && featureIds[0] === AlertConsumers.SIEM, - [featureIds] - ); - const quickFilters = useMemo( - () => [ - { - groupName: SOLUTION_OR_APP_TITLE, - items: [ - { - name: alertProducersData[AlertConsumers.SIEM].displayName, - icon: 'logoSecurity', - filter: createRuleProducerFilter(AlertConsumers.SIEM), - disabled: filteringBySolution && !browsingSiem, - }, - { - name: alertProducersData[AlertConsumers.OBSERVABILITY].displayName, - icon: 'logoObservability', - disabled: filteringBySolution && browsingSiem, - filter: { - meta: { - field: ALERT_RULE_PRODUCER, - type: FILTERS.PHRASES, - key: ALERT_RULE_PRODUCER, - alias: null, - disabled: false, - index: undefined, - negate: false, - params: observabilityProducers, - value: undefined, - }, - query: { - bool: { - minimum_should_match: 1, - should: observabilityProducers.map((p) => ({ - match_phrase: { - [ALERT_RULE_PRODUCER]: p, - }, - })), - }, - }, - } as PhrasesFilter, - }, - ...observabilityApps.map((oa) => { - const { displayName, icon } = alertProducersData[oa]; - return { - name: displayName, - icon, - filter: createRuleProducerFilter(oa), - disabled: filteringBySolution && browsingSiem, - }; - }), - { - name: alertProducersData[AlertConsumers.ML].displayName, - icon: 'machineLearningApp', - filter: createRuleProducerFilter('ml'), - disabled: filteringBySolution && browsingSiem, - }, - { - name: alertProducersData[AlertConsumers.STACK_ALERTS].displayName, - icon: 'managementApp', - filter: createRuleProducerFilter('stackAlerts'), - disabled: filteringBySolution && browsingSiem, - }, - ], - }, - { - groupName: i18n.translate( - 'xpack.triggersActionsUI.sections.globalAlerts.quickFilters.status', - { - defaultMessage: 'Status', - } - ), - items: [ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED].map((s) => ({ - name: s, - filter: createMatchPhraseFilter(ALERT_STATUS, s), - })), - }, - ], - [browsingSiem, filteringBySolution] - ); - const columns = useMemo(() => { - const [first, ...otherCols] = alertsTableConfigurationRegistry.get( - ALERT_TABLE_GENERIC_CONFIG_ID - ).columns; - return [ - first, - { - displayAsText: SOLUTION_OR_APP_TITLE, - id: ALERT_RULE_PRODUCER, - schema: 'string', - initialWidth: 180, - }, - ...otherCols, - ]; - }, [alertsTableConfigurationRegistry]); - - async function loadRuleStats() { - setStatsLoading(true); - try { - const response = await loadRuleAggregations({ - http, - }); - const { ruleExecutionStatus, ruleMutedStatus, ruleEnabledStatus, ruleSnoozedStatus } = - response; - if (ruleExecutionStatus && ruleMutedStatus && ruleEnabledStatus && ruleSnoozedStatus) { - const total = Object.values(ruleExecutionStatus).reduce((acc, value) => acc + value, 0); - const { disabled } = ruleEnabledStatus; - const { muted } = ruleMutedStatus; - const { error } = ruleExecutionStatus; - const { snoozed } = ruleSnoozedStatus; - setStats({ - ...stats, - total, - disabled, - muted, - error, - snoozed, - }); - } - setStatsLoading(false); - } catch (_e) { - toasts.addDanger({ - title: i18n.translate('xpack.triggersActionsUI.globalAlerts.alertStats.loadError', { - defaultMessage: 'Unable to load rule stats', - }), - }); - setStatsLoading(false); - } - } - - useEffect(() => { - loadRuleStats(); - setBreadcrumbs([ - { - text: i18n.translate('xpack.triggersActionsUI.globalAlerts.alerts', { - defaultMessage: 'Alerts', - }), - }, - ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - - - {i18n.translate('xpack.triggersActionsUI.globalAlerts.title', { - defaultMessage: 'Alerts', - })}{' '} - - ), - rightSideItems: getStatNodes({ stats, loading: statsLoading, manageRulesHref }), - }} - > - - - - - - - - - - ); -}; - -// eslint-disable-next-line import/no-default-export -export default GlobalAlerts; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/global_alerts_page.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/global_alerts_page.tsx new file mode 100644 index 0000000000000..3bfcac02644c5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/global_alerts_page.tsx @@ -0,0 +1,239 @@ +/* + * 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, { lazy, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiPageTemplate, EuiSpacer } from '@elastic/eui'; +import { + ALERT_RULE_PRODUCER, + ALERT_STATUS, + ALERT_STATUS_ACTIVE, + ALERT_STATUS_RECOVERED, + ALERT_STATUS_UNTRACKED, + ALERT_TIME_RANGE, + AlertConsumers, +} from '@kbn/rule-data-utils'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { + BoolQuery, + buildEsQuery as kbnBuildEsQuery, + EsQueryConfig, + Filter, + FILTERS, + PhrasesFilter, + Query, + TimeRange, +} from '@kbn/es-query'; +import { getTime } from '@kbn/data-plugin/common'; +import { QuickFiltersMenuItem } from '@kbn/unified-search-plugin/public/query_string_input/quick_filters'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { ALERT_TABLE_GLOBAL_CONFIG_ID } from '../../constants'; +import { FEATURE_LABEL } from '../translations'; +import { useRuleStats } from './hooks/use_rule_stats'; +import { getAlertingSectionBreadcrumb } from '../../lib/breadcrumb'; +import { NON_SIEM_FEATURE_IDS } from '../alerts_search_bar/constants'; +import { + alertProducersData, + observabilityApps, + observabilityFeatureIds, +} from '../alerts_table/constants'; +import { UrlSyncedAlertsSearchBar } from '../alerts_search_bar/url_synced_alerts_search_bar'; +import { useKibana } from '../../../common/lib/kibana'; +import { alertsTableQueryClient } from '../alerts_table/query_client'; +import { + alertSearchBarStateContainer, + Provider, +} from '../alerts_search_bar/use_alert_search_bar_state_container'; +import { getCurrentDocTitle } from '../../lib/doc_title'; +import { createMatchPhraseFilter, createRuleProducerFilter } from '../../lib/search_filters'; +const AlertsTable = lazy(() => import('../alerts_table/alerts_table_state')); + +interface BuildEsQueryArgs { + timeRange?: TimeRange; + kuery?: string; + queries?: Query[]; + config?: EsQueryConfig; + filters?: Filter[]; +} + +export function buildEsQuery({ + timeRange, + kuery, + filters = [], + queries = [], + config = {}, +}: BuildEsQueryArgs) { + const timeFilter = + timeRange && + getTime(undefined, timeRange, { + fieldName: ALERT_TIME_RANGE, + }); + const filtersToUse = [...(timeFilter ? [timeFilter] : []), ...filters]; + const kueryFilter = kuery ? [{ query: kuery, language: 'kuery' }] : []; + const queryToUse = [...kueryFilter, ...queries]; + return kbnBuildEsQuery(undefined, queryToUse, filtersToUse, config); +} + +/** + * A unified view for all types of alerts + */ +export const GlobalAlertsPage = () => { + const { + chrome: { docTitle }, + setBreadcrumbs, + alertsTableConfigurationRegistry, + } = useKibana().services; + const [esQuery, setEsQuery] = useState({ bool: {} } as { bool: BoolQuery }); + const [activeFeatureFilters, setActiveFeatureFilters] = useState([]); + + const ruleStats = useRuleStats(); + const browsingSiem = useMemo( + () => activeFeatureFilters.length === 1 && activeFeatureFilters[0] === AlertConsumers.SIEM, + [activeFeatureFilters] + ); + const filteringBySolution = useMemo( + () => activeFeatureFilters.length > 0, + [activeFeatureFilters.length] + ); + const featureIds = useMemo( + () => (browsingSiem ? [AlertConsumers.SIEM] : NON_SIEM_FEATURE_IDS), + [browsingSiem] + ); + const quickFilters = useMemo( + () => [ + { + groupName: FEATURE_LABEL, + items: [ + { + name: alertProducersData[AlertConsumers.SIEM].displayName, + icon: 'logoSecurity', + filter: createRuleProducerFilter(AlertConsumers.SIEM), + disabled: filteringBySolution && !browsingSiem, + }, + { + name: alertProducersData[AlertConsumers.OBSERVABILITY].displayName, + icon: 'logoObservability', + disabled: filteringBySolution && browsingSiem, + filter: { + meta: { + field: ALERT_RULE_PRODUCER, + type: FILTERS.PHRASES, + key: ALERT_RULE_PRODUCER, + alias: null, + disabled: false, + index: undefined, + negate: false, + params: observabilityFeatureIds, + value: undefined, + }, + query: { + bool: { + minimum_should_match: 1, + should: observabilityFeatureIds.map((p) => ({ + match_phrase: { + [ALERT_RULE_PRODUCER]: p, + }, + })), + }, + }, + } as PhrasesFilter, + }, + ...observabilityApps.map((oa) => { + const { displayName, icon } = alertProducersData[oa]; + return { + name: displayName, + icon, + filter: createRuleProducerFilter(oa), + disabled: filteringBySolution && browsingSiem, + }; + }), + { + name: alertProducersData[AlertConsumers.ML].displayName, + icon: 'machineLearningApp', + filter: createRuleProducerFilter('ml'), + disabled: filteringBySolution && browsingSiem, + }, + { + name: alertProducersData[AlertConsumers.STACK_ALERTS].displayName, + icon: 'managementApp', + filter: createRuleProducerFilter('stackAlerts'), + disabled: filteringBySolution && browsingSiem, + }, + ], + }, + { + groupName: i18n.translate( + 'xpack.triggersActionsUI.sections.globalAlerts.quickFilters.status', + { + defaultMessage: 'Status', + } + ), + items: [ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED].map((s) => ({ + name: s, + filter: createMatchPhraseFilter(ALERT_STATUS, s), + })), + }, + ], + [browsingSiem, filteringBySolution] + ); + const tableConfigurationId = useMemo( + () => + /* browsingSiem + ? 'securitySolution-alerts-page' + : activeFeatureFilters.every((featureId) => observabilityFeatureIds.includes(featureId)) + ? AlertConsumers.OBSERVABILITY + : */ ALERT_TABLE_GLOBAL_CONFIG_ID, + [ + /* activeFeatureFilters, browsingSiem*/ + ] + ); + + useEffect(() => { + setBreadcrumbs([getAlertingSectionBreadcrumb('alerts')]); + docTitle.change(getCurrentDocTitle('alerts')); + }, [docTitle, setBreadcrumbs]); + + return ( + + + + + + } + rightSideItems={ruleStats} + /> + + + + + + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/hooks/use_rule_stats.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/hooks/use_rule_stats.tsx new file mode 100644 index 0000000000000..06e371979d980 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/hooks/use_rule_stats.tsx @@ -0,0 +1,155 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; +import styled from '@emotion/styled'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { loadRuleAggregations, useKibana } from '../../../..'; + +const Stat = styled(EuiStat)` + .euiText { + line-height: 1; + } +`; + +const Divider = styled.div` + border-right: 1px solid ${euiThemeVars.euiColorLightShade}; + height: 100%; +`; + +export const useRuleStats = () => { + const { + http, + notifications: { toasts }, + } = useKibana().services; + const [loading, setLoading] = useState(false); + const [stats, setStats] = useState({ + total: 0, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }); + const manageRulesHref = useMemo( + () => http.basePath.prepend('/app/management/insightsAndAlerting/triggersActions/rules'), + [http.basePath] + ); + + const loadRuleStats = useCallback(async () => { + setLoading(true); + try { + const response = await loadRuleAggregations({ + http, + }); + const { ruleExecutionStatus, ruleMutedStatus, ruleEnabledStatus, ruleSnoozedStatus } = + response; + if (ruleExecutionStatus && ruleMutedStatus && ruleEnabledStatus && ruleSnoozedStatus) { + const total = Object.values(ruleExecutionStatus).reduce((acc, value) => acc + value, 0); + const { disabled } = ruleEnabledStatus; + const { muted } = ruleMutedStatus; + const { error } = ruleExecutionStatus; + const { snoozed } = ruleSnoozedStatus; + setStats((oldStats) => ({ + ...oldStats, + total, + disabled, + muted, + error, + snoozed, + })); + } + } catch (_e) { + toasts.addDanger({ + title: i18n.translate('xpack.triggersActionsUI.globalAlerts.alertStats.loadError', { + defaultMessage: 'Unable to load rule stats', + }), + }); + } finally { + setLoading(false); + } + }, [http, toasts]); + + useEffect(() => { + loadRuleStats(); + }, [loadRuleStats]); + + return useMemo(() => { + const disabledStatsComponent = ( + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={loading} + data-test-subj="statDisabled" + /> + ); + + const snoozedStatsComponent = ( + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={loading} + data-test-subj="statMuted" + /> + ); + + const errorStatsComponent = ( + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={loading} + data-test-subj="statErrors" + /> + ); + + return [ + , + disabledStatsComponent, + snoozedStatsComponent, + errorStatsComponent, + , + + {i18n.translate('xpack.triggersActionsUI.globalAlerts.manageRulesButtonLabel', { + defaultMessage: 'Manage Rules', + })} + , + ].reverse(); + }, [ + loading, + manageRulesHref, + stats.disabled, + stats.error, + stats.muted, + stats.snoozed, + stats.total, + ]); +}; diff --git a/x-pack/plugins/triggers_actions_ui/common/alert_config.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/index.tsx similarity index 62% rename from x-pack/plugins/triggers_actions_ui/common/alert_config.ts rename to x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/index.tsx index 4c812c4fa4260..4444c84baf4fc 100644 --- a/x-pack/plugins/triggers_actions_ui/common/alert_config.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/global_alerts_page/index.tsx @@ -5,6 +5,6 @@ * 2.0. */ -import { AlertConsumers } from '@kbn/rule-data-utils'; - -export const ALERT_TABLE_GENERIC_CONFIG_ID = `${AlertConsumers.STACK_ALERTS}-generic-alert-table`; +import { GlobalAlertsPage } from './global_alerts_page'; +// eslint-disable-next-line import/no-default-export +export default GlobalAlertsPage; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/translations.ts index cc8cc4a1616cb..6570524ea8454 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/translations.ts @@ -17,3 +17,67 @@ export const MAINTENANCE_WINDOWS = i18n.translate( defaultMessage: 'Maintenance Windows', } ); + +export const OBSERVABILITY_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.observability', + { + defaultMessage: 'Observability', + } +); + +export const SECURITY_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.security', + { + defaultMessage: 'Security', + } +); + +export const STACK_MANAGEMENT_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.stackManagement', + { + defaultMessage: 'Stack management', + } +); + +export const UPTIME_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.uptime', + { + defaultMessage: 'Uptime', + } +); + +export const APM_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.apm', { + defaultMessage: 'APM', +}); + +export const INFRASTRUCTURE_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.infrastructure', + { + defaultMessage: 'Infrastructure', + } +); + +export const SLO_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.slos', + { + defaultMessage: 'SLOs', + } +); + +export const LOGS_DISPLAY_NAME = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.logs', + { + defaultMessage: 'Logs', + } +); + +export const ML_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.ml', { + defaultMessage: 'Machine Learning', +}); + +export const FEATURE_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.globalAlerts.quickFilters.feature', + { + defaultMessage: 'Feature', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts index 261780d6dcf03..0b18f1ae8a2ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts @@ -17,5 +17,6 @@ export * from './action_frequency_types'; export const VIEW_LICENSE_OPTIONS_LINK = 'https://www.elastic.co/subscriptions'; export const PLUGIN_ID = 'triggersActions'; +export const ALERTS_PLUGIN_ID = 'alerts'; export const CONNECTORS_PLUGIN_ID = 'triggersActionsConnectors'; export * from './i18n_weekdays'; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index fcd33c9630efc..04dbaabe3ee36 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -5,13 +5,7 @@ * 2.0. */ -import { - AppMountParameters, - CoreSetup, - CoreStart, - DEFAULT_APP_CATEGORIES, - Plugin as CorePlugin, -} from '@kbn/core/public'; +import { CoreSetup, CoreStart, Plugin as CorePlugin } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { ReactElement } from 'react'; @@ -85,7 +79,7 @@ import type { } from './types'; import { TriggersActionsUiConfigType } from '../common/types'; import { registerAlertsTableConfiguration } from './application/sections/alerts_table/alerts_page/register_alerts_table_configuration'; -import { PLUGIN_ID, CONNECTORS_PLUGIN_ID } from './common/constants'; +import { PLUGIN_ID, CONNECTORS_PLUGIN_ID, ALERTS_PLUGIN_ID } from './common/constants'; import type { AlertsTableStateProps } from './application/sections/alerts_table/alerts_table_state'; import { getAlertsTableStateLazy } from './common/get_alerts_table_state'; import { getAlertsSearchBarLazy } from './common/get_alerts_search_bar'; @@ -234,7 +228,7 @@ export class Plugin const alertsFeatureDescription = i18n.translate( 'xpack.triggersActionsUI.managementSection.alerts.displayDescription', { - defaultMessage: 'TODO', // TODO + defaultMessage: 'Monitor all of your alerts in one place', } ); const connectorsFeatureTitle = i18n.translate( @@ -251,15 +245,15 @@ export class Plugin ); if (plugins.home) { - // plugins.home.featureCatalogue.register({ - // id: 'alerts', - // title: alertsFeatureTitle, - // description: alertsFeatureDescription, - // icon: 'watchesApp', - // path: '/app/management', - // showOnHomePage: false, - // category: 'admin', - // }); + plugins.home.featureCatalogue.register({ + id: ALERTS_PLUGIN_ID, + title: alertsFeatureTitle, + description: alertsFeatureDescription, + icon: 'watchesApp', + path: triggersActionsRoute, + showOnHomePage: false, + category: 'admin', + }); plugins.home.featureCatalogue.register({ id: PLUGIN_ID, title: featureTitle, @@ -280,14 +274,11 @@ export class Plugin }); } - core.application.register({ - id: 'alerts', + plugins.management.sections.section.insightsAndAlerting.registerApp({ + id: ALERTS_PLUGIN_ID, title: alertsFeatureTitle, - order: 8080, - euiIconType: 'watchesApp', - category: DEFAULT_APP_CATEGORIES.management, - // updater$: this.appUpdater, - async mount(params: AppMountParameters) { + order: 0, + async mount(params: ManagementAppMountParams) { const { renderApp } = await import('./application/alerts_app'); const [coreStart, pluginsStart] = (await core.getStartServices()) as [ CoreStart, @@ -316,11 +307,16 @@ export class Plugin element: params.element, theme$: params.theme$, storage: new Storage(window.localStorage), + setBreadcrumbs: params.setBreadcrumbs, history: params.history, actionTypeRegistry, ruleTypeRegistry, alertsTableConfigurationRegistry, kibanaFeatures, + licensing: pluginsStart.licensing, + expressions: pluginsStart.expressions, + isServerless: !!pluginsStart.serverless, + fieldFormats: pluginsStart.fieldFormats, }); }, }); @@ -328,7 +324,7 @@ export class Plugin plugins.management.sections.section.insightsAndAlerting.registerApp({ id: PLUGIN_ID, title: featureTitle, - order: 0, + order: 1, async mount(params: ManagementAppMountParams) { const [coreStart, pluginsStart] = (await core.getStartServices()) as [ CoreStart, @@ -440,9 +436,9 @@ export class Plugin public start(_: CoreStart, plugins: PluginsStart): TriggersAndActionsUIPublicPluginStart { import('./application/sections/alerts_table/configuration').then( - ({ getAlertsTableConfiguration }) => { - this.alertsTableConfigurationRegistry.register( - getAlertsTableConfiguration(plugins.fieldFormats) + ({ createGenericAlertsTableConfigurations }) => { + createGenericAlertsTableConfigurations(plugins.fieldFormats).forEach((c) => + this.alertsTableConfigurationRegistry.register(c) ); } );