diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_dependencies.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_dependencies.png new file mode 100644 index 0000000000000..63377ac85431f Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_dependencies.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_errors.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_errors.png new file mode 100644 index 0000000000000..0cfea1c544a02 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_errors.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_infrastructure.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_infrastructure.png new file mode 100644 index 0000000000000..1a8f2cabbc0c1 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_infrastructure.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_metrics.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_metrics.png new file mode 100644 index 0000000000000..799e41821effb Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_metrics.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_overview.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_overview.png new file mode 100644 index 0000000000000..7f24ebaefb8c6 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_overview.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_service_map.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_service_map.png new file mode 100644 index 0000000000000..19ec7072b227c Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_service_map.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_transactions.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_transactions.png new file mode 100644 index 0000000000000..97de4c3a20ec9 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/dark/service_tab_empty_state_transactions.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_dependencies.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_dependencies.png new file mode 100644 index 0000000000000..1cf18b76670bf Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_dependencies.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_errors.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_errors.png new file mode 100644 index 0000000000000..df9a1c61cb6fe Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_errors.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_infrastructure.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_infrastructure.png new file mode 100644 index 0000000000000..287862fd9c30f Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_infrastructure.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_metrics.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_metrics.png new file mode 100644 index 0000000000000..7455f5a532a1e Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_metrics.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_overview.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_overview.png new file mode 100644 index 0000000000000..2fd6807b209e8 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_overview.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_service_map.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_service_map.png new file mode 100644 index 0000000000000..ae899d735988b Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_service_map.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_transactions.png b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_transactions.png new file mode 100644 index 0000000000000..bb3183a5584e8 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/service_tab_empty_state/light/service_tab_empty_state_transactions.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_overview/index.tsx index 5024bfad1fc99..486af9c64310e 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_overview/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_overview/index.tsx @@ -15,9 +15,12 @@ import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group import { FailedTransactionRateChart } from '../../shared/charts/failed_transaction_rate_chart'; import { ErrorDistribution } from '../error_group_details/distribution'; import { ErrorGroupList } from './error_group_list'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; export function ErrorGroupOverview() { const { serviceName } = useApmServiceContext(); + const { serviceEntitySummary } = useApmServiceContext(); const { query: { environment, kuery, comparisonEnabled }, @@ -30,6 +33,13 @@ export function ErrorGroupOverview() { kuery, }); + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } + return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/infra_overview/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/infra_overview/index.tsx index 2b7f55d0ba690..a7db6bdd09a80 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/infra_overview/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/infra_overview/index.tsx @@ -6,9 +6,21 @@ */ import { EuiPanel } from '@elastic/eui'; import React from 'react'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; import { InfraTabs } from './infra_tabs'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; export function InfraOverview() { + const { serviceEntitySummary } = useApmServiceContext(); + + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } + return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/metrics/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/index.tsx index b3eaa74ff1d80..e89bffeaf5b5d 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/metrics/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/metrics/index.tsx @@ -18,11 +18,22 @@ import { JvmMetricsOverview } from './jvm_metrics_overview'; import { JsonMetricsDashboard } from './static_dashboard'; import { hasDashboardFile } from './static_dashboard/helper'; import { useAdHocApmDataView } from '../../../hooks/use_adhoc_apm_data_view'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; export function Metrics() { const { agentName, runtimeName, serverlessType } = useApmServiceContext(); const isAWSLambda = isAWSLambdaAgentName(serverlessType); const { dataView } = useAdHocApmDataView(); + const { serviceEntitySummary } = useApmServiceContext(); + + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } + if (isAWSLambda) { return ; } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_dependencies/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_dependencies/index.tsx index 30df2be2188fa..7aa010c276fcd 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_dependencies/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_dependencies/index.tsx @@ -9,11 +9,23 @@ import { EuiTitle } from '@elastic/eui'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; import { ServiceOverviewDependenciesTable } from '../service_overview/service_overview_dependencies_table'; import { ServiceDependenciesBreakdownChart } from './service_dependencies_breakdown_chart'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; export function ServiceDependencies() { + const { serviceEntitySummary } = useApmServiceContext(); + + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } + return ( <> diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_map/index.tsx index 14c5167128725..4536406a21bd5 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_map/index.tsx @@ -29,6 +29,9 @@ import { useApmParams, useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { Environment } from '../../../../common/environment_rt'; import { useTimeRange } from '../../../hooks/use_time_range'; import { DisabledPrompt } from './disabled_prompt'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; function PromptContainer({ children }: { children: ReactNode }) { return ( @@ -76,6 +79,14 @@ export function ServiceMapServiceDetail() { '/mobile-services/{serviceName}/service-map' ); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const { serviceEntitySummary } = useApmServiceContext(); + + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } return ; } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/index.tsx index e04f90b81798d..e406eba65f678 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/index.tsx @@ -14,9 +14,12 @@ import { ChartPointerEventContextProvider } from '../../../context/chart_pointer import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useTimeRange } from '../../../hooks/use_time_range'; -import { isApmSignal, isLogsSignal } from '../../../utils/get_signal_type'; +import { isApmSignal, isLogsSignal, isLogsOnlySignal } from '../../../utils/get_signal_type'; import { ApmOverview } from './apm_overview'; import { LogsOverview } from './logs_overview'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; +import { useLocalStorage } from '../../../hooks/use_local_storage'; +import { SearchBar } from '../../shared/search_bar/search_bar'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; /** * The height a chart should be if it's next to a table with 5 rows and a title. @@ -49,11 +52,19 @@ export function ServiceOverview() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const [dismissedLogsOnlyEmptyState, setDismissedLogsOnlyEmptyState] = useLocalStorage( + `apm.dismissedLogsOnlyEmptyState.overview`, + false + ); + const hasSignal = serviceEntitySummary?.dataStreamTypes && serviceEntitySummary?.dataStreamTypes?.length > 0; const hasLogsSignal = hasSignal && isLogsSignal(serviceEntitySummary.dataStreamTypes); + const hasLogsOnlySignal = hasSignal && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + // Shows APM overview when entity has APM signal or when Entity centric is not enabled const hasApmSignal = hasSignal && isApmSignal(serviceEntitySummary.dataStreamTypes); // Shows APM overview when entity has APM signal or when Entity centric is not enabled or when entity has no signal @@ -69,23 +80,36 @@ export function ServiceOverview() { } return ( - - - - {showApmOverview ? : null} - {/* Only shows Logs overview when entity has Logs signal */} - {hasLogsSignal ? ( - - - - ) : null} - - - + <> + + + + + {showApmOverview ? : null} + {/* Only shows Logs overview when entity has Logs signal */} + {hasLogsSignal ? ( + <> + {hasLogsOnlySignal && !dismissedLogsOnlyEmptyState && ( + + setDismissedLogsOnlyEmptyState(true)} + /> + + )} + + + + + ) : null} + + + + ); } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/logs_overview/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/logs_overview/index.tsx index 163b5aa11898a..b51a95e3ccabc 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/logs_overview/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/logs_overview/index.tsx @@ -5,44 +5,21 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; -import { useLocalStorage } from '../../../../hooks/use_local_storage'; -import { AddAPMCallOut } from '../../entities/logs/add_apm_callout'; import { LogRateChart } from '../../entities/charts/log_rate_chart'; import { LogErrorRateChart } from '../../entities/charts/log_error_rate_chart'; import { chartHeight } from '..'; -interface Props { - hasApmSignal?: boolean; -} - -export function LogsOverview({ hasApmSignal }: Props) { - const [isLogsApmCalloutEnabled, setIsLogsApmCalloutEnabled] = useLocalStorage( - 'apm.isLogsApmCalloutEnabled', - true - ); - +export function LogsOverview() { return ( - <> - {!hasApmSignal && isLogsApmCalloutEnabled ? ( - <> - { - setIsLogsApmCalloutEnabled(false); - }} - /> - - - ) : null} - - - - - - - - - + + + + + + + + ); } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.stories.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.stories.tsx index bd04f82396b1c..6bbad7a95e114 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.stories.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.stories.tsx @@ -42,6 +42,7 @@ const stories: Meta<{}> = { transactionType, transactionTypeStatus, serviceEntitySummary, + transactionTypes: ['type'], } as unknown as APMServiceContextValue; return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.test.tsx index 35a5e48307147..98cdc1e65c3a2 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.test.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_overview/service_overview.test.tsx @@ -9,10 +9,28 @@ import { composeStories } from '@storybook/testing-react'; import { render, screen } from '@testing-library/react'; import React from 'react'; import * as stories from './service_overview.stories'; +import * as useAdHocApmDataView from '../../../hooks/use_adhoc_apm_data_view'; const { Example } = composeStories(stories); describe('ServiceOverview', () => { + let useAdHocApmDataViewSpy: jest.SpyInstance; + + beforeAll(() => { + useAdHocApmDataViewSpy = jest.spyOn(useAdHocApmDataView, 'useAdHocApmDataView'); + + useAdHocApmDataViewSpy.mockImplementation(() => { + return { + dataView: { + id: 'foo-1', + }, + }; + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); it('renders', async () => { render(); diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/constants.ts b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/constants.ts new file mode 100644 index 0000000000000..8165ad991cfe2 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/constants.ts @@ -0,0 +1,95 @@ +/* + * 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'; + +export type EmptyStateKey = + | 'serviceOverview' + | 'serviceDependencies' + | 'infraOverview' + | 'serviceMap' + | 'transactionOverview' + | 'metrics' + | 'errorGroupOverview'; + +interface EmptyStateContent { + title: string; + content: string; + imgName?: string; +} + +export const emptyStateDefinitions: Record = { + serviceOverview: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.overviewTitle', { + defaultMessage: 'Detect and resolve issues faster with deep visibility into your application', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.overviewContent', { + defaultMessage: + 'Understanding your application performance, relationships and dependencies by instrumenting with APM.', + }), + }, + serviceDependencies: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.dependenciesTitle', { + defaultMessage: 'Understand the dependencies for your service', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.dependenciesContent', { + defaultMessage: + 'See your services dependencies on both internal and third-party services by instrumenting with APM.', + }), + imgName: 'service_tab_empty_state_dependencies.png', + }, + infraOverview: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.infrastructureTitle', { + defaultMessage: 'Understand what your service is running on', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.infrastructureContent', { + defaultMessage: + 'Troubleshoot service problems by seeing the infrastructure your service is running on.', + }), + imgName: 'service_tab_empty_state_infrastructure.png', + }, + serviceMap: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.serviceMapTitle', { + defaultMessage: 'Visualise the dependencies between your services', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.serviceMapContent', { + defaultMessage: + 'See your services dependencies at a glance to help identify dependencies that may be affecting your service.', + }), + imgName: 'service_tab_empty_state_service_map.png', + }, + transactionOverview: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.transactionsTitle', { + defaultMessage: 'Troubleshoot latency, throughput and errors', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.transactionsContent', { + defaultMessage: + "Troubleshoot your service's performance by analysing latency, throughput and errors down to the specific transaction.", + }), + imgName: 'service_tab_empty_state_transactions.png', + }, + metrics: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.metricsTitle', { + defaultMessage: 'View core metrics for your application', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.metricsContent', { + defaultMessage: + 'View metric trends for the instances of your service to identify performance bottlenecks that could be affecting your users.', + }), + imgName: 'service_tab_empty_state_metrics.png', + }, + errorGroupOverview: { + title: i18n.translate('xpack.apm.serviceTabEmptyState.errorGroupOverviewTitle', { + defaultMessage: 'Identify transaction errors with your applications', + }), + content: i18n.translate('xpack.apm.serviceTabEmptyState.errorGroupOverviewContent', { + defaultMessage: + 'Analyse errors down to the specific transaction to pin-point specific errors within your service.', + }), + imgName: 'service_tab_empty_state_errors.png', + }, +}; diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/index.tsx new file mode 100644 index 0000000000000..8f4f86e2b92ed --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/index.tsx @@ -0,0 +1,138 @@ +/* + * 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, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiImage, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useKibanaUrl } from '../../../hooks/use_kibana_url'; +import { AddApmData } from '../../shared/add_data_buttons/buttons'; +import { emptyStateDefinitions, EmptyStateKey } from './constants'; + +export interface ServiceTabEmptyStateProps { + id: EmptyStateKey; + onDissmiss?: () => void; +} + +const tryItNowButton = { + label: i18n.translate('xpack.apm.serviceTabEmptyState.tryItNowButtonLabel', { + defaultMessage: 'Try it now in our demo cluster', + }), + href: 'https://ela.st/demo-apm-try-it', +}; + +const learnMoreLink = { + label: i18n.translate('xpack.apm.serviceTabEmptyState.learnMoreLinkLabel', { + defaultMessage: 'Learn more', + }), + href: 'https://www.elastic.co/observability/application-performance-monitoring', +}; + +const baseImgFolder = '/plugins/apm/assets/service_tab_empty_state'; + +export function ServiceTabEmptyState({ id, onDissmiss }: ServiceTabEmptyStateProps) { + const { euiTheme } = useEuiTheme(); + const { core } = useApmPluginContext(); + + const imgFolder = `${baseImgFolder}/${ + core.uiSettings.get('theme:darkMode') === 'enabled' ? 'dark' : 'light' + }`; + const imgName = emptyStateDefinitions[id].imgName; + const imgSrc = useKibanaUrl( + `${imgFolder}/${imgName ? imgName : 'service_tab_empty_state_overview.png'}` + ); + + return ( + <> + + + + +

{emptyStateDefinitions[id].title}

+
+ + {emptyStateDefinitions[id].content} + + + + + + + + {tryItNowButton.label} + + + + + {learnMoreLink.label} + + + +
+ {!emptyStateDefinitions[id].imgName && ( + + + + )} + + {onDissmiss && ( + + )} +
+
+ {emptyStateDefinitions[id].imgName && ( + <> + + + + )} + + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/service_tab_empty_state.stories.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/service_tab_empty_state.stories.tsx new file mode 100644 index 0000000000000..1e85abd2a5b62 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_tab_empty_state/service_tab_empty_state.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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, { ComponentProps, ComponentType } from 'react'; +import { ServiceTabEmptyState } from '.'; +import { MockApmPluginStorybook } from '../../../context/apm_plugin/mock_apm_plugin_storybook'; + +export default { + title: 'APP/ServiceTabEmptyState', + component: ServiceTabEmptyState, + decorators: [ + (Story: ComponentType) => ( + + + + ), + ], +}; + +export function Default({ id }: ComponentProps) { + return ; +} + +Default.args = { + id: 'infraOverview', +} as ComponentProps; diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_overview/index.tsx index 1ff58538b293a..84da9489964c6 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_overview/index.tsx @@ -19,6 +19,8 @@ import { TransactionCharts } from '../../shared/charts/transaction_charts'; import { replace } from '../../shared/links/url_helpers'; import { SloCallout } from '../../shared/slo_callout'; import { TransactionsTable } from '../../shared/transactions_table'; +import { isLogsOnlySignal } from '../../../utils/get_signal_type'; +import { ServiceTabEmptyState } from '../service_tab_empty_state'; export function TransactionOverview() { const { @@ -60,6 +62,15 @@ export function TransactionOverview() { }); }, [setScreenContext, serviceName, transactionType]); + const { serviceEntitySummary } = useApmServiceContext(); + + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + + if (hasLogsOnlySignal) { + return ; + } + return ( <> {!sloCalloutDismissed && ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/service_detail/index.tsx index ea5a94a47ccd0..d746e0464fd40 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/service_detail/index.tsx @@ -155,8 +155,7 @@ export const serviceDetailRoute = { defaultMessage: 'Overview', }), searchBarOptions: { - showTransactionTypeSelector: true, - showTimeComparison: true, + hidden: true, }, }), params: t.partial({ diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx index 6c2fdaea96687..0e095694cd538 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingLogo, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; import { useHistory, useLocation } from 'react-router-dom'; +import { isLogsOnlySignal } from '../../../../utils/get_signal_type'; import { isMobileAgentName } from '../../../../../common/agent_name'; import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -54,7 +55,7 @@ function TemplateWithContext({ title, children, selectedTab, searchBarOptions }: const tabs = useTabs({ selectedTab }); - const { agentName, serviceAgentStatus } = useApmServiceContext(); + const { agentName, serviceAgentStatus, serviceEntitySummary } = useApmServiceContext(); const isPendingServiceAgent = !agentName && isPending(serviceAgentStatus); @@ -75,6 +76,9 @@ function TemplateWithContext({ title, children, selectedTab, searchBarOptions }: }); } + const hasLogsOnlySignal = + serviceEntitySummary?.dataStreamTypes && isLogsOnlySignal(serviceEntitySummary.dataStreamTypes); + return ( ) : ( <> - + {!hasLogsOnlySignal && } {children} diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/add_data_buttons/buttons.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/add_data_buttons/buttons.tsx index db2cb9d41237b..e3fc828b24803 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/add_data_buttons/buttons.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/add_data_buttons/buttons.tsx @@ -8,7 +8,7 @@ // Disabling it for now until the EUI team fixes it /* eslint-disable @elastic/eui/href-or-on-click */ -import { EuiButton } from '@elastic/eui'; +import { EuiButton, EuiButtonSize } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; @@ -34,22 +34,24 @@ export const collectServiceLogs = { link: '/app/observabilityOnboarding/customLogs/?category=logs', }; -export function AddApmData({ - onClick, - ...props -}: { +interface AddApmDataProps { onClick?: () => void; 'data-test-subj': string; -}) { + fill?: boolean; + size?: EuiButtonSize; +} + +export function AddApmData({ fill = false, size = 's', ...props }: AddApmDataProps) { const { core } = useApmPluginContext(); const { basePath } = core.http; return ( {addApmData.name} diff --git a/x-pack/plugins/observability_solution/apm/public/context/apm_plugin/mock_apm_plugin_storybook.tsx b/x-pack/plugins/observability_solution/apm/public/context/apm_plugin/mock_apm_plugin_storybook.tsx index 0c851093a5bb1..a342f84e9c5c3 100644 --- a/x-pack/plugins/observability_solution/apm/public/context/apm_plugin/mock_apm_plugin_storybook.tsx +++ b/x-pack/plugins/observability_solution/apm/public/context/apm_plugin/mock_apm_plugin_storybook.tsx @@ -120,11 +120,44 @@ const mockCore = { get: (key: string) => uiSettings[key], get$: (key: string) => of(mockCore.uiSettings.get(key)), }, + unifiedSearch: { + autocomplete: { + hasQuerySuggestions: () => Promise.resolve(false), + getQuerySuggestions: () => [], + getValueSuggestions: () => + new Promise((resolve) => { + setTimeout(() => { + resolve([]); + }, 300); + }), + }, + }, + data: { + query: { + queryString: { getQuery: jest.fn(), setQuery: jest.fn(), clearQuery: jest.fn() }, + timefilter: { + timefilter: { + setTime: jest.fn(), + setRefreshInterval: jest.fn(), + }, + }, + }, + }, + dataViews: { + create: jest.fn(), + }, +}; + +const mockUnifiedSearchBar = { + ui: { + SearchBar: () =>
, + }, }; const mockApmPluginContext = { core: mockCore, plugins: mockPlugin, + unifiedSearch: mockUnifiedSearchBar, observabilityAIAssistant: { service: { setScreenContext: () => noop }, }, diff --git a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts index 4a73368c00e85..89d5c3ff49114 100644 --- a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts +++ b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts @@ -16,3 +16,7 @@ export function isApmSignal(dataStreamTypes: EntityDataStreamType[]) { export function isLogsSignal(dataStreamTypes: EntityDataStreamType[]) { return dataStreamTypes.includes(EntityDataStreamType.LOGS); } + +export function isLogsOnlySignal(signalTypes: EntityDataStreamType[]) { + return !isApmSignal(signalTypes) && isLogsSignal(signalTypes); +}