diff --git a/x-pack/plugins/observability/public/context/has_data_context.test.tsx b/x-pack/plugins/observability/public/context/has_data_context.test.tsx index f2f550e35ac6b..b51091e7eb973 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.test.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.test.tsx @@ -79,11 +79,11 @@ describe('HasDataContextProvider', () => { }); describe('when no plugin has registered', () => { - it('hasAnyData returns false and all apps return undefined', async () => { + it('hasAnyData returns undefined and all apps return undefined', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toMatchObject({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -132,7 +132,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -188,7 +188,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -246,7 +246,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -301,7 +301,7 @@ describe('HasDataContextProvider', () => { }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -346,7 +346,7 @@ describe('HasDataContextProvider', () => { }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -406,7 +406,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -484,7 +484,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), @@ -534,7 +534,7 @@ describe('HasDataContextProvider', () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ hasDataMap: {}, - hasAnyData: false, + hasAnyData: undefined, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), diff --git a/x-pack/plugins/observability/public/context/has_data_context.tsx b/x-pack/plugins/observability/public/context/has_data_context.tsx index 047a596ea349e..2c12b9f96f0db 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { uniqueId } from 'lodash'; +import { isEmpty, uniqueId } from 'lodash'; import React, { createContext, useEffect, useState } from 'react'; import { useRouteMatch } from 'react-router-dom'; import { Alert } from '../../../alerting/common'; @@ -31,7 +31,7 @@ export type HasDataMap = Record< export interface HasDataContextValue { hasDataMap: Partial; - hasAnyData: boolean; + hasAnyData?: boolean; isAllRequestsComplete: boolean; onRefreshTimeRange: () => void; forceUpdate: string; @@ -153,7 +153,7 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode { diff --git a/x-pack/plugins/observability/public/pages/landing/index.tsx b/x-pack/plugins/observability/public/pages/landing/index.tsx index 28d3784c65c4a..98b0b128b49d0 100644 --- a/x-pack/plugins/observability/public/pages/landing/index.tsx +++ b/x-pack/plugins/observability/public/pages/landing/index.tsx @@ -5,32 +5,14 @@ * 2.0. */ -import { - EuiButton, - EuiCard, - EuiFlexGrid, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; -import styled, { ThemeContext } from 'styled-components'; -import { FleetPanel } from '../../components/app/fleet_panel'; -import { ObservabilityHeaderMenu } from '../../components/app/header'; +import React from 'react'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useTrackPageview } from '../../hooks/use_track_metric'; -import { appsSection } from '../home/section'; +import { getNoDataConfig } from '../../utils/no_data_config'; import './styles.scss'; -const EuiCardWithoutPadding = styled(EuiCard)` - padding: 0; -`; - export function LandingPage() { useTrackPageview({ app: 'observability-overview', path: 'landing' }); useTrackPageview({ app: 'observability-overview', path: 'landing', delay: 15000 }); @@ -43,96 +25,20 @@ export function LandingPage() { ]); const { core, ObservabilityPageTemplate } = usePluginContext(); - const theme = useContext(ThemeContext); - - return ( - - - - {/* title and description */} - - -

- {i18n.translate('xpack.observability.home.sectionTitle', { - defaultMessage: 'Unified visibility across your entire ecosystem', - })} -

-
- - - {i18n.translate('xpack.observability.home.sectionsubtitle', { - defaultMessage: - 'Monitor, analyze, and react to events happening anywhere in your environment by bringing logs, metrics, and traces together at scale in a single stack.', - })} - -
- - {/* Apps sections */} - - - - - - {appsSection.map((app) => ( - - -

{app.title}

- - } - description={app.description} - /> -
- ))} -
-
- - - -
-
- + const noDataConfig = getNoDataConfig({ + // Set it to false because the landing page is only visible when there's no data + hasData: false, + basePath: core.http.basePath, + docsLink: core.docLinks.links.observability.guide, + }); - {/* Get started button */} - - - - - {i18n.translate('xpack.observability.home.getStatedButton', { - defaultMessage: 'Get started', - })} - - - - - - - - - - - - - - -
-
+ return ( + ); } diff --git a/x-pack/plugins/observability/public/pages/overview/index.tsx b/x-pack/plugins/observability/public/pages/overview/index.tsx index 71c52c3a51d3b..26dbf5ece400c 100644 --- a/x-pack/plugins/observability/public/pages/overview/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/index.tsx @@ -24,6 +24,7 @@ import { useTimeRange } from '../../hooks/use_time_range'; import { RouteParams } from '../../routes'; import { getNewsFeed } from '../../services/get_news_feed'; import { getBucketSize } from '../../utils/get_bucket_size'; +import { getNoDataConfig } from '../../utils/no_data_config'; import { DataSections } from './data_sections'; import { LoadingObservability } from './loading_observability'; @@ -57,12 +58,20 @@ export function OverviewPage({ routeParams }: Props) { const { data: newsFeed } = useFetcher(() => getNewsFeed({ core }), [core]); - const { hasDataMap, hasAnyData } = useHasData(); + const { hasDataMap, hasAnyData, isAllRequestsComplete } = useHasData(); if (hasAnyData === undefined) { return ; } + const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); + + const noDataConfig = getNoDataConfig({ + hasData, + basePath: core.http.basePath, + docsLink: core.docLinks.links.observability.guide, + }); + const alerts = (hasDataMap.alert?.hasData as Alert[]) || []; const { refreshInterval = 10000, refreshPaused = true } = routeParams.query; @@ -74,44 +83,53 @@ export function OverviewPage({ routeParams }: Props) { return ( , - ], - }} + noDataConfig={noDataConfig} + pageHeader={ + hasData + ? { + pageTitle: overviewPageTitle, + rightSideItems: [ + , + ], + } + : undefined + } > - - - - {/* Data sections */} - {hasAnyData && } - - + {hasData && ( + <> + - - {/* Resources / What's New sections */} - - - - {!!newsFeed?.items?.length && } - + + {/* Data sections */} + {hasAnyData && } + + + + + {/* Resources / What's New sections */} + + + + {!!newsFeed?.items?.length && } + + + {!!alerts.length && ( + + + + + + )} + - {!!alerts.length && ( - - - - - - )} - - + + )} ); } diff --git a/x-pack/plugins/observability/public/pages/overview/loading_observability.tsx b/x-pack/plugins/observability/public/pages/overview/loading_observability.tsx index e2d691c647acc..1183e3e9d8e43 100644 --- a/x-pack/plugins/observability/public/pages/overview/loading_observability.tsx +++ b/x-pack/plugins/observability/public/pages/overview/loading_observability.tsx @@ -15,7 +15,7 @@ export function LoadingObservability() { const { ObservabilityPageTemplate } = usePluginContext(); return ( - + diff --git a/x-pack/plugins/observability/public/utils/no_data_config.ts b/x-pack/plugins/observability/public/utils/no_data_config.ts new file mode 100644 index 0000000000000..1e16fb145bdce --- /dev/null +++ b/x-pack/plugins/observability/public/utils/no_data_config.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 { i18n } from '@kbn/i18n'; +import { IBasePath } from '../../../../../src/core/public'; +import { KibanaPageTemplateProps } from '../../../../../src/plugins/kibana_react/public'; + +export function getNoDataConfig({ + docsLink, + basePath, + hasData, +}: { + docsLink: string; + basePath: IBasePath; + hasData?: boolean; +}): KibanaPageTemplateProps['noDataConfig'] { + if (hasData === false) { + return { + solution: i18n.translate('xpack.observability.noDataConfig.solutionName', { + defaultMessage: 'Observability', + }), + actions: { + beats: { + description: i18n.translate('xpack.observability.noDataConfig.beatsCard.description', { + defaultMessage: + 'Use Beats and APM agents to send observability data to Elasticsearch. We make it easy with support for many popular systems, apps, and languages.', + }), + href: basePath.prepend(`/app/home#/tutorial_directory/logging`), + }, + }, + docsLink, + }; + } +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 04bcc951c5d53..336b04392c636 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -19241,9 +19241,6 @@ "xpack.observability.formatters.secondsTimeUnitLabel": "s", "xpack.observability.formatters.secondsTimeUnitLabelExtended": "秒", "xpack.observability.home.addData": "データの追加", - "xpack.observability.home.getStatedButton": "使ってみる", - "xpack.observability.home.sectionsubtitle": "ログ、メトリック、トレースを大規模に、1つのスタックにまとめて、環境内のあらゆる場所で生じるイベントの監視、分析、対応を行います。", - "xpack.observability.home.sectionTitle": "エコシステム全体の一元的な可視性", "xpack.observability.navigation.newBadge": "新規", "xpack.observability.news.readFullStory": "詳細なストーリーを読む", "xpack.observability.news.title": "新機能", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 18a38ae138f42..bf43dd5b2e282 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -19518,9 +19518,6 @@ "xpack.observability.formatters.secondsTimeUnitLabel": "s", "xpack.observability.formatters.secondsTimeUnitLabelExtended": "秒", "xpack.observability.home.addData": "添加数据", - "xpack.observability.home.getStatedButton": "开始使用", - "xpack.observability.home.sectionsubtitle": "通过根据需要将日志、指标和跟踪都置于单个堆栈上,来监测、分析和响应环境中任何位置发生的事件。", - "xpack.observability.home.sectionTitle": "整个生态系统的统一可见性", "xpack.observability.navigation.newBadge": "新建", "xpack.observability.news.readFullStory": "详细了解", "xpack.observability.news.title": "最新动态",