diff --git a/src/core/utils/default_nav_groups.ts b/src/core/utils/default_nav_groups.ts index 7278734ae826..bbd38c81ffc5 100644 --- a/src/core/utils/default_nav_groups.ts +++ b/src/core/utils/default_nav_groups.ts @@ -52,8 +52,7 @@ const defaultNavGroups = { defaultMessage: 'Observability', }), description: i18n.translate('core.ui.group.observability.description', { - defaultMessage: - 'Gain visibility into system health, performance, and reliability through monitoring and analysis of logs, metrics, and traces.', + defaultMessage: 'Gain visibility into your application and infrastructure', }), order: 4000, icon: 'wsObservability', @@ -64,8 +63,7 @@ const defaultNavGroups = { defaultMessage: 'Security Analytics', }), description: i18n.translate('core.ui.group.security.analytics.description', { - defaultMessage: - 'Detect and investigate potential security threats and vulnerabilities across your systems and data.', + defaultMessage: 'Enhance your security posture with advanced analytics', }), order: 5000, icon: 'wsSecurityAnalytics', @@ -88,8 +86,7 @@ const defaultNavGroups = { defaultMessage: 'Search', }), description: i18n.translate('core.ui.group.search.description', { - defaultMessage: - "Quickly find and explore relevant information across your organization's data sources.", + defaultMessage: 'Discover and query your data with ease', }), order: 6000, icon: 'wsSearch', diff --git a/src/plugins/home/public/application/components/use_case_card.test.ts b/src/plugins/home/public/application/components/use_case_card.test.ts new file mode 100644 index 000000000000..e26e6b1fb0eb --- /dev/null +++ b/src/plugins/home/public/application/components/use_case_card.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiIcon } from '@elastic/eui'; +import { coreMock } from '../../../../../core/public/mocks'; +import { registerUseCaseCard } from './use_case_card'; +import { contentManagementPluginMocks } from '../../../../content_management/public'; + +describe('registerUseCaseCard', () => { + const registerContentProviderFn = jest.fn(); + const contentManagementStartMock = { + ...contentManagementPluginMocks.createStartContract(), + registerContentProvider: registerContentProviderFn, + }; + + const core = coreMock.createStart(); + + it('should register useCase card correctly', () => { + registerUseCaseCard(contentManagementStartMock, core, { + id: 'testId', + order: 1, + target: 'osd_homepage/get_started', + icon: 'wsObservability', + title: 'observability', + description: 'Gain visibility into your application and infrastructure', + navigateAppId: 'observability_overview', + }); + + expect(contentManagementStartMock.registerContentProvider).toHaveBeenCalledTimes(1); + + const registerCall = contentManagementStartMock.registerContentProvider.mock.calls[0][0]; + + expect(registerCall.getTargetArea()).toEqual('osd_homepage/get_started'); + + expect(registerCall.getContent()).toEqual({ + id: 'testId', + kind: 'card', + order: 1, + description: 'Gain visibility into your application and infrastructure', + title: 'observability', + cardProps: { + layout: 'horizontal', + }, + onClick: expect.any(Function), + getIcon: expect.any(Function), + }); + + const icon = registerCall.getContent().getIcon(); + expect(icon.type).toBe(EuiIcon); + expect(icon.props).toEqual({ + size: 'l', + color: 'subdued', + type: 'wsObservability', + }); + }); + + it('should be able to navigate to the expected overview page when click the card', () => { + const navigateToAppMock = jest.fn(); + core.application.navigateToApp = navigateToAppMock; + + registerUseCaseCard(contentManagementStartMock, core, { + id: 'testId', + order: 1, + target: 'osd_homepage/get_started', + icon: 'wsObservability', + title: 'observability', + description: 'Gain visibility into your application and infrastructure', + navigateAppId: 'observability_overview', + }); + + const registerCall = contentManagementStartMock.registerContentProvider.mock.calls[0][0]; + const card = registerCall.getContent(); + card.onClick(); + expect(navigateToAppMock).toHaveBeenCalledWith('observability_overview'); + }); +}); diff --git a/src/plugins/home/public/application/components/use_case_card.ts b/src/plugins/home/public/application/components/use_case_card.ts new file mode 100644 index 000000000000..b71f381358f4 --- /dev/null +++ b/src/plugins/home/public/application/components/use_case_card.ts @@ -0,0 +1,55 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { CoreStart } from 'opensearch-dashboards/public'; +import { EuiIcon } from '@elastic/eui'; +import { ContentManagementPluginStart } from '../../../../content_management/public'; + +export const registerUseCaseCard = ( + contentManagement: ContentManagementPluginStart, + core: CoreStart, + { + target, + order, + id, + title, + description, + icon, + navigateAppId, + }: { + target: string; + order: number; + id: string; + title: string; + description: string; + icon: string; + navigateAppId: string; + } +) => { + contentManagement.registerContentProvider({ + id: `home_get_started_${id}`, + getTargetArea: () => target, + getContent: () => ({ + id, + kind: 'card', + order, + description, + title, + cardProps: { + layout: 'horizontal', + }, + onClick: () => { + core.application.navigateToApp(navigateAppId); + }, + getIcon: () => + React.createElement(EuiIcon, { + size: 'l', + color: 'subdued', + type: icon, + }), + }), + }); +}; diff --git a/src/plugins/home/public/application/home_render.test.tsx b/src/plugins/home/public/application/home_render.test.tsx new file mode 100644 index 000000000000..34a5d690b44b --- /dev/null +++ b/src/plugins/home/public/application/home_render.test.tsx @@ -0,0 +1,142 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DEFAULT_NAV_GROUPS } from '../../../../core/public'; +import { + HOME_CONTENT_AREAS, + SEARCH_OVERVIEW_PAGE_ID, + OBSERVABILITY_OVERVIEW_PAGE_ID, + SECURITY_ANALYTICS_OVERVIEW_PAGE_ID, +} from '../../../../plugins/content_management/public'; +import { contentManagementPluginMocks } from '../../../../plugins/content_management/public/mocks'; +import { registerUseCaseCard } from './components/use_case_card'; +import { initHome } from './home_render'; + +import { + WHATS_NEW_CONFIG, + LEARN_OPENSEARCH_CONFIG, + registerHomeListCard, +} from './components/home_list_card'; + +jest.mock('./components/use_case_card', () => ({ + registerUseCaseCard: jest.fn(), +})); + +jest.mock('./components/home_list_card', () => ({ + registerHomeListCard: jest.fn(), +})); + +describe('initHome', () => { + const registerContentProviderFn = jest.fn(); + const contentManagementStartMock = { + ...contentManagementPluginMocks.createStartContract(), + registerContentProvider: registerContentProviderFn, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should register use case cards when workspace is enabled', () => { + const coreMock = { + createStart: jest.fn(() => ({ + application: { + capabilities: { + workspaces: { + enabled: false, + }, + }, + navigateToApp: jest.fn(), + }, + })), + }; + const core = coreMock.createStart(); + + initHome(contentManagementStartMock, core); + + expect(registerUseCaseCard).toHaveBeenCalledTimes(3); + + expect(registerUseCaseCard).toHaveBeenCalledWith(contentManagementStartMock, core, { + id: DEFAULT_NAV_GROUPS.observability.id, + order: 1, + description: DEFAULT_NAV_GROUPS.observability.description, + title: DEFAULT_NAV_GROUPS.observability.title, + target: HOME_CONTENT_AREAS.GET_STARTED, + icon: DEFAULT_NAV_GROUPS.observability.icon ?? '', + navigateAppId: OBSERVABILITY_OVERVIEW_PAGE_ID, + }); + + expect(registerUseCaseCard).toHaveBeenCalledWith(contentManagementStartMock, core, { + id: DEFAULT_NAV_GROUPS.search.id, + order: 2, + description: DEFAULT_NAV_GROUPS.search.description, + title: DEFAULT_NAV_GROUPS.search.title, + target: HOME_CONTENT_AREAS.GET_STARTED, + icon: DEFAULT_NAV_GROUPS.search.icon ?? '', + navigateAppId: SEARCH_OVERVIEW_PAGE_ID, + }); + + expect(registerUseCaseCard).toHaveBeenCalledWith(contentManagementStartMock, core, { + id: DEFAULT_NAV_GROUPS['security-analytics'].id, + order: 3, + description: DEFAULT_NAV_GROUPS['security-analytics'].description, + title: DEFAULT_NAV_GROUPS['security-analytics'].title, + target: HOME_CONTENT_AREAS.GET_STARTED, + icon: DEFAULT_NAV_GROUPS['security-analytics'].icon ?? '', + navigateAppId: SECURITY_ANALYTICS_OVERVIEW_PAGE_ID, + }); + }); + + it('should not register use case cards when workspace is disabled', () => { + const coreMock = { + createStart: jest.fn(() => ({ + application: { + capabilities: { + workspaces: { + enabled: true, + }, + }, + }, + })), + }; + const core = coreMock.createStart(); + initHome(contentManagementStartMock, core); + expect(registerUseCaseCard).not.toHaveBeenCalled(); + }); + + it('should register home list cards correctly', () => { + const coreMock = { + createStart: jest.fn(() => ({ + application: { + capabilities: { + workspaces: { + enabled: false, + }, + }, + }, + })), + }; + const core = coreMock.createStart(); + initHome(contentManagementStartMock, core); + + expect(registerHomeListCard).toHaveBeenCalledTimes(2); + + expect(registerHomeListCard).toHaveBeenCalledWith(contentManagementStartMock, { + id: 'whats_new', + order: 10, + config: WHATS_NEW_CONFIG, + target: HOME_CONTENT_AREAS.SERVICE_CARDS, + width: 16, + }); + + expect(registerHomeListCard).toHaveBeenCalledWith(contentManagementStartMock, { + id: 'learn_opensearch_new', + order: 11, + config: LEARN_OPENSEARCH_CONFIG, + target: HOME_CONTENT_AREAS.SERVICE_CARDS, + width: 16, + }); + }); +}); diff --git a/src/plugins/home/public/application/home_render.tsx b/src/plugins/home/public/application/home_render.tsx index ef73d7c44d99..f22f3560205c 100644 --- a/src/plugins/home/public/application/home_render.tsx +++ b/src/plugins/home/public/application/home_render.tsx @@ -5,12 +5,16 @@ import React from 'react'; import { CoreStart } from 'opensearch-dashboards/public'; +import { DEFAULT_NAV_GROUPS } from '../../../../core/public'; import { ContentManagementPluginSetup, ContentManagementPluginStart, HOME_PAGE_ID, SECTIONS, HOME_CONTENT_AREAS, + SEARCH_OVERVIEW_PAGE_ID, + OBSERVABILITY_OVERVIEW_PAGE_ID, + SECURITY_ANALYTICS_OVERVIEW_PAGE_ID, } from '../../../../plugins/content_management/public'; import { WHATS_NEW_CONFIG, @@ -18,16 +22,13 @@ import { registerHomeListCard, } from './components/home_list_card'; +import { registerUseCaseCard } from './components/use_case_card'; + export const setupHome = (contentManagement: ContentManagementPluginSetup) => { contentManagement.registerPage({ id: HOME_PAGE_ID, title: 'Home', sections: [ - { - id: SECTIONS.SERVICE_CARDS, - order: 3000, - kind: 'dashboard', - }, { id: SECTIONS.RECENTLY_VIEWED, order: 2000, @@ -47,10 +48,15 @@ export const setupHome = (contentManagement: ContentManagementPluginSetup) => { ); }, }, + { + id: SECTIONS.SERVICE_CARDS, + order: 3000, + kind: 'dashboard', + }, { id: SECTIONS.GET_STARTED, order: 1000, - title: 'Get started with OpenSearch’s powerful features', + title: "Get started with OpenSearch's powerful features", kind: 'card', }, ], @@ -58,9 +64,34 @@ export const setupHome = (contentManagement: ContentManagementPluginSetup) => { }; export const initHome = (contentManagement: ContentManagementPluginStart, core: CoreStart) => { + const workspaceEnabled = core.application.capabilities.workspaces.enabled; + + if (!workspaceEnabled) { + const useCases = [ + { ...DEFAULT_NAV_GROUPS.observability, navigateAppId: OBSERVABILITY_OVERVIEW_PAGE_ID }, + { ...DEFAULT_NAV_GROUPS.search, navigateAppId: SEARCH_OVERVIEW_PAGE_ID }, + { + ...DEFAULT_NAV_GROUPS['security-analytics'], + navigateAppId: SECURITY_ANALYTICS_OVERVIEW_PAGE_ID, + }, + ]; + + useCases.forEach((useCase, index) => { + registerUseCaseCard(contentManagement, core, { + id: useCase.id, + order: index + 1, + description: useCase.description, + title: useCase.title, + target: HOME_CONTENT_AREAS.GET_STARTED, + icon: useCase.icon ?? '', + navigateAppId: useCase.navigateAppId, + }); + }); + } + registerHomeListCard(contentManagement, { id: 'whats_new', - order: 3, + order: 10, config: WHATS_NEW_CONFIG, target: HOME_CONTENT_AREAS.SERVICE_CARDS, width: 16, @@ -68,7 +99,7 @@ export const initHome = (contentManagement: ContentManagementPluginStart, core: registerHomeListCard(contentManagement, { id: 'learn_opensearch_new', - order: 4, + order: 11, config: LEARN_OPENSEARCH_CONFIG, target: HOME_CONTENT_AREAS.SERVICE_CARDS, width: 16, diff --git a/src/plugins/saved_objects_management/public/management_section/recent_work.tsx b/src/plugins/saved_objects_management/public/management_section/recent_work.tsx index eb04652d6343..88cdfc142af4 100644 --- a/src/plugins/saved_objects_management/public/management_section/recent_work.tsx +++ b/src/plugins/saved_objects_management/public/management_section/recent_work.tsx @@ -257,38 +257,45 @@ export const RecentWork = (props: { core: CoreStart; workspaceEnabled?: boolean textAlign="left" href={recentNavLink.href} footer={ - <> - - - - {selectedSort === recentlyViewed - ? i18n.translate( - 'savedObjectsManagement.recentWorkSection.viewedAt', - { - defaultMessage: 'Viewed', - } - ) - : i18n.translate( - 'savedObjectsManagement.recentWorkSection.updatedAt', - { - defaultMessage: 'Updated', - } - )} - :{' '} - - - - - + + + + + {selectedSort === recentlyViewed - ? moment(recentAccessItem?.lastAccessedTime).fromNow() - : moment(recentAccessItem?.updatedAt).fromNow()} - - - - - {workspaceEnabled && ( - <> + ? i18n.translate( + 'savedObjectsManagement.recentWorkSection.viewedAt', + { + defaultMessage: 'Viewed', + } + ) + : i18n.translate( + 'savedObjectsManagement.recentWorkSection.updatedAt', + { + defaultMessage: 'Updated', + } + )} + :{' '} + + + + + + {selectedSort === recentlyViewed + ? moment(recentAccessItem?.lastAccessedTime).fromNow() + : moment(recentAccessItem?.updatedAt).fromNow()} + + + + + + {workspaceEnabled && ( + + {i18n.translate( @@ -300,15 +307,15 @@ export const RecentWork = (props: { core: CoreStart; workspaceEnabled?: boolean : - + {recentAccessItem.workspaceName || 'N/A'} - - )} - - + + + )} + } onClick={recentNavLink.onClick} /> diff --git a/src/plugins/workspace/common/constants.ts b/src/plugins/workspace/common/constants.ts index f66f7ea71752..22c746c0dbed 100644 --- a/src/plugins/workspace/common/constants.ts +++ b/src/plugins/workspace/common/constants.ts @@ -45,6 +45,7 @@ export const PRIORITY_FOR_PERMISSION_CONTROL_WRAPPER = 0; * store a static map in workspace. * */ + export const WORKSPACE_USE_CASES = Object.freeze({ observability: { id: 'observability', @@ -52,9 +53,9 @@ export const WORKSPACE_USE_CASES = Object.freeze({ defaultMessage: 'Observability', }), description: i18n.translate('workspace.usecase.observability.description', { - defaultMessage: - 'Gain visibility into system health, performance, and reliability through monitoring and analysis of logs, metrics, and traces.', + defaultMessage: 'Gain visibility into your application and infrastructure', }), + icon: 'wsObservability', features: [ 'discover', 'dashboards', @@ -78,9 +79,9 @@ export const WORKSPACE_USE_CASES = Object.freeze({ defaultMessage: 'Security Analytics', }), description: i18n.translate('workspace.usecase.analytics.description', { - defaultMessage: - 'Detect and investigate potential security threats and vulnerabilities across your systems and data.', + defaultMessage: 'Enhance your security posture with advanced analytics', }), + icon: 'wsSecurityAnalytics', features: [ 'discover', 'dashboards', @@ -102,9 +103,9 @@ export const WORKSPACE_USE_CASES = Object.freeze({ defaultMessage: 'Essentials', }), description: i18n.translate('workspace.usecase.essentials.description', { - defaultMessage: - 'Analyze data to derive insights, identify patterns and trends, and make data-driven decisions.', + defaultMessage: 'Get start with just the basics', }), + icon: 'wsEssentials', features: [ 'discover', 'dashboards', @@ -125,9 +126,9 @@ export const WORKSPACE_USE_CASES = Object.freeze({ defaultMessage: 'Search', }), description: i18n.translate('workspace.usecase.search.description', { - defaultMessage: - "Quickly find and explore relevant information across your organization's data sources.", + defaultMessage: 'Discover and query your data with ease', }), + icon: 'wsSearch', features: [ 'discover', 'dashboards', diff --git a/src/plugins/workspace/public/components/use_case_overview/setup_overview.test.tsx b/src/plugins/workspace/public/components/use_case_overview/setup_overview.test.tsx index cdcebca33e19..e1f47c9756d1 100644 --- a/src/plugins/workspace/public/components/use_case_overview/setup_overview.test.tsx +++ b/src/plugins/workspace/public/components/use_case_overview/setup_overview.test.tsx @@ -137,7 +137,7 @@ describe('Setup use case overview', () => { "cardProps": Object { "layout": "horizontal", }, - "description": "Gain visibility into system health, performance, and reliability through monitoring and analysis of logs, metrics, and traces.", + "description": "Gain visibility into your application and infrastructure", "getIcon": [Function], "id": "observability", "kind": "card",