diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 40580ef700146c..68145db3310625 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -6,6 +6,8 @@ */ import { i18n } from '@kbn/i18n'; +import { CreateSLOInput, Indicator } from '@kbn/slo-schema'; +import { encode } from '@kbn/rison'; import React from 'react'; import { @@ -14,6 +16,7 @@ import { EuiFlexItem, EuiLink, EuiPanel, + EuiSpacer, } from '@elastic/eui'; import { isRumAgentName, @@ -36,6 +39,8 @@ import { ServiceOverviewDependenciesTable } from './service_overview_dependencie import { ServiceOverviewErrorsTable } from './service_overview_errors_table'; import { ServiceOverviewInstancesChartAndTable } from './service_overview_instances_chart_and_table'; import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart'; +import { SloCallout } from '../../shared/slo_callout'; +import { useLocalStorage } from '../../../hooks/use_local_storage'; /** * The height a chart should be if it's next to a table with 5 rows and a title. * Add the height of the pagination row. @@ -49,13 +54,27 @@ export function ServiceOverview() { const { query, - query: { kuery, environment, rangeFrom, rangeTo }, + query: { kuery, environment, rangeFrom, rangeTo, transactionType }, } = useApmParams('/services/{serviceName}/overview'); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); const isRumAgent = isRumAgentName(agentName); const isServerless = isServerlessAgent(serverlessType); + const params: Partial = { + service: serviceName, + environment, + transactionType, + }; + const sloInput: CreateSLOInput = { + indicator: { + type: 'sli.apm.transactionErrorRate', + params, + }, + }; + + const sloParams = `?_a=${encode(sloInput)}`; + const dependenciesLink = router.link('/services/{serviceName}/dependencies', { path: { serviceName, @@ -76,6 +95,11 @@ export function ServiceOverview() { ? 'column' : 'row'; + const [sloCalloutDismissed, setSloCalloutDismissed] = useLocalStorage( + 'apm.sloCalloutDismissed', + false + ); + return ( + {!sloCalloutDismissed && ( + { + setSloCalloutDismissed(true); + }} + sloParams={sloParams} + /> + )} + {fallbackToTransactions && ( diff --git a/x-pack/plugins/apm/public/components/shared/slo_callout/index.tsx b/x-pack/plugins/apm/public/components/shared/slo_callout/index.tsx new file mode 100644 index 00000000000000..17dec6312bc202 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/slo_callout/index.tsx @@ -0,0 +1,69 @@ +/* + * 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 { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; + +interface Props { + dismissCallout: () => void; + sloParams: string; +} + +export function SloCallout({ dismissCallout, sloParams }: Props) { + const { core } = useApmPluginContext(); + const { basePath } = core.http; + return ( + + + +

+ +

+
+ + + + + {i18n.translate('xpack.apm.slo.callout.createButton', { + defaultMessage: 'Create SLO', + })} + + + + { + dismissCallout(); + }} + > + {i18n.translate('xpack.apm.slo.callout.dimissButton', { + defaultMessage: 'Hide this', + })} + + + + +
+
+ ); +}