diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/definition.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/definition.tsx index 30d4403106a66..4c98db62b996e 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/definition.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/definition.tsx @@ -27,6 +27,7 @@ import { ApmIndicatorOverview } from '../overview/apm_indicator_overview'; import { DisplayQuery } from '../overview/display_query'; import { DefinitionItem } from './definition_item'; import { SyntheticsIndicatorOverview } from '../overview/synthetics_indicator_overview'; +import { LinkedDashboards } from './linked_dashboards'; export interface Props { slo: SLOWithSummaryResponse; @@ -153,6 +154,12 @@ export function Definition({ slo }: Props) { })} subtitle={slo.settings.frequency} /> + } + /> ); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/linked_dashboards.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/linked_dashboards.tsx new file mode 100644 index 0000000000000..b3d8d400b44c5 --- /dev/null +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/definition/linked_dashboards.tsx @@ -0,0 +1,91 @@ +/* + * 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, EuiLink, EuiLoadingSpinner, EuiText } from '@elastic/eui'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; +import React, { useMemo } from 'react'; +import type { DashboardLocatorParams } from '@kbn/dashboard-plugin/common'; +import { useQuery } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import type { DashboardStart } from '@kbn/dashboard-plugin/public'; +import type { SLODefinition } from '../../../../../server/domain/models'; +import { useKibana } from '../../../../hooks/use_kibana'; + +interface Props { + dashboards: NonNullable['dashboards']>; +} + +interface LinkedDashboard { + id: string; + title: string; +} + +const getDashboards = async ( + dashboardsIds: string[], + dashboardStart: DashboardStart +): Promise => { + if (dashboardsIds.length === 0) { + return []; + } + + const findDashboardsService = await dashboardStart.findDashboardsService(); + + const dashboards = await findDashboardsService.findByIds(dashboardsIds); + + return dashboards.flatMap((dashboard) => + dashboard.status === 'success' ? [{ id: dashboard.id, title: dashboard.attributes.title }] : [] + ); +}; + +export function LinkedDashboards({ dashboards }: Props) { + const { + services: { share }, + services: { dashboard: dashboardStart }, + } = useKibana(); + + const dashboardLocator = share.url.locators.get(DASHBOARD_APP_LOCATOR); + + const dashboardsIds = useMemo(() => dashboards.map((dashboard) => dashboard.id), [dashboards]); + + const { data, isLoading } = useQuery({ + queryKey: ['SLO-dashboards', ...dashboardsIds], + queryFn: () => getDashboards(dashboardsIds, dashboardStart), + refetchOnWindowFocus: false, + }); + + if (isLoading) { + return ; + } + + if (!data || data.length === 0) { + return ( + + {i18n.translate('xpack.slo.sloDetails.overview.noDashboards', { + defaultMessage: 'No linked dashboards', + })} + + ); + } + + return ( + + {data.map((dashboardAsset) => { + return ( + + + {dashboardAsset.title} + + + ); + })} + + ); +} diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx index f150599bc3838..1673dc0554e66 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx @@ -17,6 +17,8 @@ import { import { i18n } from '@kbn/i18n'; import React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; +import { DashboardsSelector } from '@kbn/dashboards-selector'; +import { useKibana } from '../../../hooks/use_kibana'; import { useFetchSLOSuggestions } from '../hooks/use_fetch_suggestions'; import type { CreateSLOForm } from '../types'; import { OptionalText } from './common/optional_text'; @@ -29,6 +31,7 @@ export function SloEditFormDescriptionSection() { const tagsId = useGeneratedHtmlId({ prefix: 'tags' }); const { suggestions } = useFetchSLOSuggestions(); + const { services } = useKibana(); return ( } > + } + > + ( + field.onChange(selected.map((d) => ({ id: d.value })))} + /> + )} + /> + ); } @@ -157,3 +182,7 @@ function generateTagOptions(tags: string[] = []) { 'data-test-subj': `${tag}Option`, })); } + +const DASHBOARDS_COMBOBOX_PLACEHOLDER = i18n.translate('xpack.slo.sloEdit.dashboards.placeholder', { + defaultMessage: 'Add dashboards', +}); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts index 9033b0cf7be64..ed5172eff61ed 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts @@ -61,6 +61,9 @@ export function transformSloResponseToCreateSloForm( : SETTINGS_DEFAULT_VALUES.frequency, syncField: values.settings?.syncField ?? null, }, + artifacts: { + dashboards: values.artifacts?.dashboards || [], + }, }; } @@ -93,6 +96,9 @@ export function transformCreateSLOFormToCreateSLOInput(values: CreateSLOForm): C frequency: `${values.settings.frequency ?? SETTINGS_DEFAULT_VALUES.frequency}m`, syncField: values.settings.syncField, }, + artifacts: { + dashboards: values.artifacts?.dashboards || [], + }, }; } @@ -125,6 +131,9 @@ export function transformValuesToUpdateSLOInput(values: CreateSLOForm): UpdateSL frequency: `${values.settings.frequency ?? SETTINGS_DEFAULT_VALUES.frequency}m`, syncField: values.settings.syncField, }, + artifacts: { + dashboards: values.artifacts?.dashboards || [], + }, }; } diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/types.ts b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/types.ts index 9d887eaa303a5..a9018ef5c2017 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/types.ts @@ -29,4 +29,7 @@ export interface CreateSLOForm { frequency: number; // in minutes syncField: string | null; }; + artifacts?: { + dashboards?: { id: string }[]; + }; } diff --git a/x-pack/solutions/observability/plugins/slo/public/types.ts b/x-pack/solutions/observability/plugins/slo/public/types.ts index 85b096ebf9bd6..a80d0aef484ef 100644 --- a/x-pack/solutions/observability/plugins/slo/public/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/types.ts @@ -54,6 +54,7 @@ import type { import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin-types-public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import type { SLORouteRepository } from '../server/routes/get_slo_server_route_repository'; import type { SLOPlugin } from './plugin'; @@ -106,6 +107,7 @@ export interface SLOPublicPluginsStart { security?: SecurityPluginStart; fieldsMetadata: FieldsMetadataPublicStart; apmSourcesAccess: ApmSourceAccessPluginStart; + contentManagement: ContentManagementPublicStart; } export type SLOPublicSetup = ReturnType; diff --git a/x-pack/solutions/observability/plugins/slo/tsconfig.json b/x-pack/solutions/observability/plugins/slo/tsconfig.json index e1c5a8552cd65..7a9f09203890c 100644 --- a/x-pack/solutions/observability/plugins/slo/tsconfig.json +++ b/x-pack/solutions/observability/plugins/slo/tsconfig.json @@ -108,5 +108,8 @@ "@kbn/lock-manager", "@kbn/object-utils", "@kbn/licensing-types", + "@kbn/deeplinks-analytics", + "@kbn/content-management-plugin", + "@kbn/dashboards-selector", ] }