diff --git a/config/serverless.es.yml b/config/serverless.es.yml index c9ff7a0c78bd..1b8ca16edbae 100644 --- a/config/serverless.es.yml +++ b/config/serverless.es.yml @@ -109,6 +109,9 @@ xpack.searchNotebooks.catalog.url: https://elastic-enterprise-search.s3.us-east- # Semantic text UI xpack.index_management.dev.enableSemanticText: true +# Search Homepage +xpack.searchHomepage.enabled: true + # AI Assistant config xpack.observabilityAIAssistant.enabled: true xpack.searchAssistant.enabled: true diff --git a/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts b/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts index fc9260e9634a..0e0262cbd4ed 100644 --- a/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts +++ b/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts @@ -193,6 +193,12 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D upgrade9x: `${ENTERPRISE_SEARCH_DOCS}upgrading-to-9-x.html`, usersAccess: `${ENTERPRISE_SEARCH_DOCS}users-access.html`, }, + searchHomepage: { + visitSearchLabs: `${ELASTIC_WEBSITE_URL}search-labs`, + notebooksExamples: `${ELASTIC_WEBSITE_URL}search-labs/tutorials/examples`, + customerEngineerRequestForm: `${ELASTIC_WEBSITE_URL}contact/ce-help`, + elasticCommunity: `${ELASTIC_WEBSITE_URL}community/`, + }, metricbeat: { base: `${ELASTIC_DOCS}reference/beats/metricbeat`, configure: `${ELASTIC_DOCS}reference/beats/metricbeat/configuring-howto-metricbeat`, diff --git a/src/platform/packages/shared/kbn-doc-links/src/types.ts b/src/platform/packages/shared/kbn-doc-links/src/types.ts index f85e7bdad0d1..1266a27a22a4 100644 --- a/src/platform/packages/shared/kbn-doc-links/src/types.ts +++ b/src/platform/packages/shared/kbn-doc-links/src/types.ts @@ -80,6 +80,12 @@ export interface DocLinks { readonly auditdModule: string; readonly systemModule: string; }; + readonly searchHomepage: { + readonly visitSearchLabs: string; + readonly notebooksExamples: string; + readonly customerEngineerRequestForm: string; + readonly elasticCommunity: string; + }; readonly metricbeat: { readonly base: string; readonly configure: string; diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 58d97b48962b..558bcf84ad43 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -33704,8 +33704,6 @@ "xpack.searchAssistant.navControl.initFailureErrorTitle": "Impossible d’initialiser l'assistant d’AI", "xpack.searchAssistant.navControl.openTheAIAssistantPopoverLabel": "Ouvrir l'assistant d'IA", "xpack.searchHomepage.appTitle": "Accueil", - "xpack.searchHomepage.consoleLink.buttonText": "Démarrez rapidement dans la Console", - "xpack.searchHomepage.header.endpointsButtonLabel": "Points de terminaison et clés d'API", "xpack.searchHomepage.pageTitle": "Bienvenue dans Search", "xpack.searchIndices.breadcrumbs.indexManagement.indices.label": "Index", "xpack.searchIndices.breadcrumbs.indexManagement.label": "Gestion des index", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index e9536122cfdd..a601b07c4441 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -33743,8 +33743,6 @@ "xpack.searchAssistant.navControl.initFailureErrorTitle": "AI Assistantを初期化できませんでした", "xpack.searchAssistant.navControl.openTheAIAssistantPopoverLabel": "AI Assistantを開く", "xpack.searchHomepage.appTitle": "ホーム", - "xpack.searchHomepage.consoleLink.buttonText": "コンソールをすばやく開始", - "xpack.searchHomepage.header.endpointsButtonLabel": "エンドポイントとAPIキー", "xpack.searchHomepage.pageTitle": "Searchへようこそ", "xpack.searchIndices.breadcrumbs.indexManagement.indices.label": "インデックス", "xpack.searchIndices.breadcrumbs.indexManagement.label": "インデックス管理", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 39f5dbb203ef..f3870518006a 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -33729,8 +33729,6 @@ "xpack.searchAssistant.navControl.initFailureErrorTitle": "无法初始化 AI 助手", "xpack.searchAssistant.navControl.openTheAIAssistantPopoverLabel": "打开 AI 助手", "xpack.searchHomepage.appTitle": "主页", - "xpack.searchHomepage.consoleLink.buttonText": "在 Console 中快速开始", - "xpack.searchHomepage.header.endpointsButtonLabel": "终端和 API 密钥", "xpack.searchHomepage.pageTitle": "欢迎使用 Search", "xpack.searchIndices.breadcrumbs.indexManagement.indices.label": "索引", "xpack.searchIndices.breadcrumbs.indexManagement.label": "索引管理", diff --git a/x-pack/solutions/search/plugins/search_homepage/common/doc_links.ts b/x-pack/solutions/search/plugins/search_homepage/common/doc_links.ts new file mode 100644 index 000000000000..ee3964578943 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/common/doc_links.ts @@ -0,0 +1,31 @@ +/* + * 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 { DocLinks } from '@kbn/doc-links'; + +class ESDocLinks { + public kibanaFeedback: string = ''; + public elasticCommunity: string = ''; + public elasticsearchDocs: string = ''; + public visitSearchLabs: string = ''; + public notebooksExamples: string = ''; + public customerEngineerRequestForm: string = ''; + public analyzeLogs: string = ''; + constructor() {} + + setDocLinks(newDocLinks: DocLinks) { + this.kibanaFeedback = newDocLinks.kibana.feedback; + this.elasticCommunity = newDocLinks.searchHomepage.elasticCommunity; + this.elasticsearchDocs = newDocLinks.elasticsearch.gettingStarted; + this.visitSearchLabs = newDocLinks.searchHomepage.visitSearchLabs; + this.notebooksExamples = newDocLinks.searchHomepage.notebooksExamples; + this.analyzeLogs = newDocLinks.serverlessSearch.integrations; + this.customerEngineerRequestForm = newDocLinks.searchHomepage.customerEngineerRequestForm; + } +} + +export const docLinks = new ESDocLinks(); diff --git a/x-pack/solutions/search/plugins/search_homepage/common/routes.ts b/x-pack/solutions/search/plugins/search_homepage/common/routes.ts new file mode 100644 index 000000000000..c284bf322292 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/common/routes.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +export const GET_STATUS_ROUTE = '/internal/search_homepage/status'; +export const GET_USER_PRIVILEGES_ROUTE = '/internal/search_homepage/start_privileges/{indexName}'; +export const GET_API_KEYS_ROUTE = '/internal/search_homepage/api_keys'; diff --git a/x-pack/solutions/search/plugins/search_homepage/common/types.ts b/x-pack/solutions/search/plugins/search_homepage/common/types.ts new file mode 100644 index 000000000000..accb9318743d --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/common/types.ts @@ -0,0 +1,25 @@ +/* + * 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 { ApiKey } from '@kbn/security-plugin-types-common'; + +export interface IndicesStatusResponse { + indexNames: string[]; +} + +export interface UserStartPrivilegesResponse { + privileges: { + canCreateApiKeys: boolean; + canManageIndex: boolean; + canDeleteDocuments: boolean; + }; +} + +export interface ApiKeysResponse { + apiKeys: ApiKey[]; + canManageOwnApiKey: boolean; +} diff --git a/x-pack/solutions/search/plugins/search_homepage/public/application.tsx b/x-pack/solutions/search/plugins/search_homepage/public/application.tsx index 5d2a04a97cc6..dd6f62d879f7 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/application.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/application.tsx @@ -12,24 +12,28 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { I18nProvider } from '@kbn/i18n-react'; import { Router } from '@kbn/shared-ux-router'; -import { SearchHomepageServicesContext } from './types'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { SearchHomepageServicesContextDeps } from './types'; import { HomepageRouter } from './router'; import { UsageTrackerContextProvider } from './contexts/usage_tracker_context'; export const renderApp = async ( core: CoreStart, - services: SearchHomepageServicesContext, - element: HTMLElement + services: SearchHomepageServicesContextDeps, + element: HTMLElement, + queryClient: QueryClient ) => { ReactDOM.render( - - - - - + + + + + + + , diff --git a/x-pack/solutions/search/plugins/search_homepage/public/assets/keyword_search.svg b/x-pack/solutions/search/plugins/search_homepage/public/assets/keyword_search.svg new file mode 100644 index 000000000000..0f4dc819627a --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/assets/keyword_search.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_dark.svg b/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_dark.svg new file mode 100644 index 000000000000..c8f5e590555e --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_dark.svg @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_light.svg b/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_light.svg new file mode 100644 index 000000000000..c22fa45c296e --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/assets/search_homepage_light.svg @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/x-pack/solutions/search/plugins/search_homepage/public/assets/semantic_search.svg b/x-pack/solutions/search/plugins/search_homepage/public/assets/semantic_search.svg new file mode 100644 index 000000000000..613236c95c65 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/assets/semantic_search.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/solutions/search/plugins/search_homepage/public/assets/vector_search.svg b/x-pack/solutions/search/plugins/search_homepage/public/assets/vector_search.svg new file mode 100644 index 000000000000..ed30c8391250 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/assets/vector_search.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_capabilities.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_capabilities.tsx new file mode 100644 index 000000000000..c6525f07d38a --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_capabilities.tsx @@ -0,0 +1,94 @@ +/* + * 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, + EuiListGroup, + EuiListGroupItem, + EuiTitle, + useCurrentEuiBreakpoint, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useState } from 'react'; + +import { KeywordSearch } from './keyword_search'; +import { SemanticSearch } from './semantic_search'; +import { VectorSearch } from './vector_search'; + +const SEARCH_CAPABILITIES = [ + { + label: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch', { + defaultMessage: 'Semantic search', + }), + value: 'semantic', + }, + { + label: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch', { + defaultMessage: 'Vector search', + }), + value: 'vector', + }, + { + label: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch', { + defaultMessage: 'Keyword search', + }), + value: 'keyword', + }, +]; + +const capabilityComponents: Record = { + semantic: SemanticSearch, + vector: VectorSearch, + keyword: KeywordSearch, +}; + +export const AISearchCapabilities: React.FC = () => { + const [selectedCapability, setSelectedCapability] = useState( + SEARCH_CAPABILITIES[0].value + ); + const currentBreakpoint = useCurrentEuiBreakpoint(); + + const SelectedComponent = capabilityComponents[selectedCapability]; + + return ( + + + +

+ {i18n.translate('xpack.searchHomepage.aiSearchCapabilities.title', { + defaultMessage: 'Explore Elastic’s AI search capabilities', + })} +

+
+
+ + + + + {SEARCH_CAPABILITIES.map((capability) => ( + setSelectedCapability(capability.value)} + isActive={selectedCapability === capability.value} + data-test-subj={`aiSearchCapabilities-item-${capability.value}`} + /> + ))} + + + + + + + +
+ ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_workflow.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_workflow.tsx new file mode 100644 index 000000000000..317993a30053 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/ai_search_workflow.tsx @@ -0,0 +1,80 @@ +/* + * 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 from 'react'; +import { + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiImage, + EuiTitle, + EuiSpacer, + EuiButton, + useCurrentEuiBreakpoint, +} from '@elastic/eui'; + +import { useKibana } from '../../hooks/use_kibana'; + +import { WorkflowFeatureBullet } from './workflow_feature_bullet'; + +interface WorkflowProps { + image: string; + imageAlt: string; + heading: string; + subheading: string; + featureBullets: string[]; + buttonLabel: string; + dataTestSubj: string; +} + +export const AISearchWorkflow = ({ capability }: { capability: WorkflowProps }) => { + const currentBreakpoint = useCurrentEuiBreakpoint(); + const { share } = useKibana().services; + const createIndexUrl = share?.url.locators.get('SEARCH_CREATE_INDEX')?.useUrl({}); + + return ( + + + + + + + + + +

{capability.heading}

+
+ + +

{capability.subheading}

+
+ + + + {capability.featureBullets.map((item: string, index: number) => ( + + + + ))} + + + + + + {capability.buttonLabel} + + +
+
+
+ ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/keyword_search.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/keyword_search.tsx new file mode 100644 index 000000000000..6b2a300a40b5 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/keyword_search.tsx @@ -0,0 +1,53 @@ +/* + * 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 from 'react'; +import { i18n } from '@kbn/i18n'; + +import { AISearchWorkflow } from './ai_search_workflow'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +export const KeywordSearch: React.FC = () => { + const assetBasePath = useAssetBasePath(); + + const keywordSearchText = { + image: `${assetBasePath}/keyword_search.svg`, + imageAlt: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch.imageAlt', { + defaultMessage: 'Keyword search', + }), + heading: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch.title', { + defaultMessage: 'Setup your application with Elasticsearch’s full-text search capabilities', + }), + subheading: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.keywordSearch.description', + { + defaultMessage: + 'Use a semantic_text field and Elastic’s built-in ELSER machine learning model.', + } + ), + featureBullets: [ + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch.firstLine', { + defaultMessage: 'Enhanced query flexibility to create complex search functionalities.', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch.secondLine', { + defaultMessage: 'Scalable architecture accommodates growing data sets.', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.keywordSearch.thirdLine', { + defaultMessage: 'Enable precise results by targeting for users or other query conditions.', + }), + ], + buttonLabel: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.keywordSearch.createKeywordIndex', + { + defaultMessage: 'Create a keyword index', + } + ), + dataTestSubj: 'createKeywordIndexButton', + }; + + return ; +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/semantic_search.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/semantic_search.tsx new file mode 100644 index 000000000000..8b085764d06b --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/semantic_search.tsx @@ -0,0 +1,53 @@ +/* + * 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 from 'react'; +import { i18n } from '@kbn/i18n'; + +import { AISearchWorkflow } from './ai_search_workflow'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +export const SemanticSearch: React.FC = () => { + const assetBasePath = useAssetBasePath(); + + const semanticSearchText = { + image: `${assetBasePath}/semantic_search.svg`, + imageAlt: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch.imageAlt', { + defaultMessage: 'Semantic search', + }), + heading: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch.title', { + defaultMessage: 'Enhance search accuracy with advanced semantic capabilities.', + }), + subheading: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.semanticSearch.description', + { + defaultMessage: + 'Leverage the semantic_text field and ELSER machine learning model for enhanced data analysis.', + } + ), + featureBullets: [ + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch.firstLine', { + defaultMessage: 'Use Elastic’s inference service or connect your own model provider', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch.SecondLine', { + defaultMessage: 'Default chunking strategies or customize your own', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.semanticSearch.ThirdLine', { + defaultMessage: 'Combine semantic capabilities with traditional search methods.', + }), + ], + buttonLabel: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.semanticSearch.createSemanticOptimizedIndex', + { + defaultMessage: 'Create a semantic optimized index', + } + ), + dataTestSubj: 'createSemanticOptimizedIndexButton', + }; + + return ; +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/vector_search.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/vector_search.tsx new file mode 100644 index 000000000000..84f86e6868d1 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/vector_search.tsx @@ -0,0 +1,53 @@ +/* + * 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 from 'react'; +import { i18n } from '@kbn/i18n'; + +import { AISearchWorkflow } from './ai_search_workflow'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +export const VectorSearch: React.FC = () => { + const assetBasePath = useAssetBasePath(); + + const vectorSearchText = { + image: `${assetBasePath}/vector_search.svg`, + imageAlt: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch.imageAlt', { + defaultMessage: 'Vector search', + }), + heading: i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch.title', { + defaultMessage: 'Store and search your vector embeddings', + }), + subheading: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.vectorSearch.description', + { + defaultMessage: + 'Use Elasticsearch as a datastore for vector embeddings and enable lightning-fast searches and insights.', + } + ), + featureBullets: [ + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch.firstLine', { + defaultMessage: 'A single solution to generate, store and search your vector embeddings.', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch.secondLine', { + defaultMessage: 'Utilize the dense_vector field type for approximate kNN searches.', + }), + i18n.translate('xpack.searchHomepage.aiSearchCapabilities.vectorSearch.thirdLine', { + defaultMessage: 'Choose from several memory quantization strategies to reduce bloat.', + }), + ], + buttonLabel: i18n.translate( + 'xpack.searchHomepage.aiSearchCapabilities.vectorSearch.createVectorIndex', + { + defaultMessage: 'Create a vector optimized index', + } + ), + dataTestSubj: 'createVectorIndexButton', + }; + + return ; +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/workflow_feature_bullet.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/workflow_feature_bullet.tsx new file mode 100644 index 000000000000..fe7be5c1167c --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/ai_search_capabilities/workflow_feature_bullet.tsx @@ -0,0 +1,22 @@ +/* + * 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 from 'react'; +import { EuiText, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; + +export const WorkflowFeatureBullet = ({ feature }: { feature: string }) => ( + + + + + + +

{feature}

+
+
+
+); diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/alternate_solutions.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/alternate_solutions.tsx new file mode 100644 index 000000000000..1076a73cb7dc --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/alternate_solutions.tsx @@ -0,0 +1,43 @@ +/* + * 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 from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + useCurrentEuiBreakpoint, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Observability } from './observability'; + +export const AlternateSolutions: React.FC = () => { + const currentBreakpoint = useCurrentEuiBreakpoint(); + + return ( + <> + +

+ {i18n.translate('xpack.searchHomepage.threatDetectionSolutions.title', { + defaultMessage: 'Looking for logs or threat detection solutions?', + })} +

+
+ + + + + + {/* TO DO: Enable the following once we have text content ready + + + */} + + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/observability.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/observability.tsx new file mode 100644 index 000000000000..e92dc052fe40 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/observability.tsx @@ -0,0 +1,124 @@ +/* + * 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 { + EuiAvatar, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useMemo } from 'react'; +import { useKibana } from '../../hooks/use_kibana'; +import { docLinks } from '../../../common/doc_links'; + +export const Observability: React.FC = () => { + const { euiTheme } = useEuiTheme(); + const { http, cloud } = useKibana().services; + + const o11yTrialLink = useMemo(() => { + if (cloud && cloud.isServerlessEnabled) { + const baseUrl = cloud?.projectsUrl ?? 'https://cloud.elastic.co/projects/'; + return `${baseUrl}create/observability/start`; + } + return http.basePath.prepend('/app/observability/onboarding'); + }, [cloud, http]); + + return ( + + + + + + + + +

+ {i18n.translate('xpack.searchHomepage.observability.title', { + defaultMessage: 'Observability', + })} +

+
+
+ + + + {i18n.translate('xpack.searchHomepage.observability.description', { + defaultMessage: + 'Consolidate your logs, metrics, application traces, and system availability with purpose-built UIs.', + })} + + + + + + + + + + + + + + {i18n.translate('xpack.searchHomepage.observability.logsTitle', { + defaultMessage: 'Collect and analyze logs', + })} + + + + + + {i18n.translate('xpack.searchHomepage.observability.exploreLogstashBeats', { + defaultMessage: 'Explore Logstash and Beats', + })} + + + + + + + + + + {i18n.translate( + 'xpack.searchHomepage.observability.performanceMonitoringTitle', + { + defaultMessage: 'Powerful performance monitoring', + } + )} + + + + + + {i18n.translate('xpack.searchHomepage.observability.observabilitySpaceLink', { + defaultMessage: 'Create an Observability project', + })} + + + + + + +
+
+
+ ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/security.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/security.tsx new file mode 100644 index 000000000000..99bdc9ba6288 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/alternate_solutions/security.tsx @@ -0,0 +1,110 @@ +/* + * 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 from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiText, + EuiSpacer, + EuiLink, + EuiAvatar, + useEuiTheme, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export const Security: React.FC = () => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + + + + + + + {i18n.translate('xpack.searchHomepage.security.title', { + defaultMessage: 'Security', + })} + + + + + + + {i18n.translate('xpack.searchHomepage.security.description', { + defaultMessage: + 'Prevent, collect, detect, and respond to threats for unified protection across your infrastructure.', + })} + + + + + + + + + + + + + + {i18n.translate('xpack.searchHomepage.security.detectThreatsTitle', { + defaultMessage: 'Detect threats in your data', + })} + + + + + + {i18n.translate('xpack.searchHomepage.security.setupSiem', { + defaultMessage: 'Setup your SIEM', + })} + + + + + + + + + + {i18n.translate('xpack.searchHomepage.security.secureCloudAssetsTitle', { + defaultMessage: 'Secure your cloud assets', + })} + + + + + + {i18n.translate( + 'xpack.searchHomepage.security.cloudSecurityPostureManagementLink', + { + defaultMessage: 'Cloud Security Posture Management', + } + )} + + + + + + + + + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch.tsx new file mode 100644 index 000000000000..5168be4a3b6f --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch.tsx @@ -0,0 +1,175 @@ +/* + * 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 { + EuiBadge, + EuiButton, + EuiButtonEmpty, + EuiButtonIcon, + EuiCopy, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React from 'react'; + +import { i18n } from '@kbn/i18n'; +import { ConnectToElasticsearchSidePanel } from './connect_to_elasticsearch_side_panel'; +import { AISearchCapabilities } from './ai_search_capabilities/ai_search_capabilities'; +import { useKibana } from '../hooks/use_kibana'; +import { useGetApiKeys } from '../hooks/api/use_api_key'; +import { useElasticsearchUrl } from '../hooks/use_elasticsearch_url'; + +export const ConnectToElasticsearch = () => { + const { share } = useKibana().services; + const { data } = useGetApiKeys(); + const elasticsearchUrl = useElasticsearchUrl(); + const locator = share?.url?.locators.get('MANAGEMENT_APP_LOCATOR'); + const manageKeysLink = locator?.useUrl({ sectionId: 'security', appId: 'api_keys' }); + const createApiKeyLink = locator?.useUrl({ sectionId: 'security', appId: 'api_keys/create' }); + + return ( + + + + + + + +

+ {i18n.translate('xpack.searchHomepage.connectToElasticsearch.title', { + defaultMessage: 'Connect to Elasticsearch', + })} +

+
+
+ + +

+ {i18n.translate('xpack.searchHomepage.connectToElasticsearch.description', { + defaultMessage: + 'Set up your connection to Elasticsearch to start searching and analyzing your data.', + })} +

+
+
+
+
+ + + + + + + + {i18n.translate( + 'xpack.searchHomepage.connectToElasticsearch.elasticSearchEndpointLabel', + { + defaultMessage: 'Elasticsearch endpoint', + } + )} + + + + + + + + {(copy) => ( + + )} + + + + + + + + + + + + + + + {i18n.translate( + 'xpack.searchHomepage.connectToElasticsearch.apiKeysLabel', + { + defaultMessage: 'API keys', + } + )} + + + + + + + + {i18n.translate( + 'xpack.searchHomepage.connectToElasticsearch.createApiKey', + { + defaultMessage: 'Create API key', + } + )} + + + + + Manage API keys + + + + 0 ? 'success' : 'warning'} + > + {data?.apiKeys?.length ?? 0} active + + + + + + + + + + + + + + +
+
+ + + +
+ ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch_side_panel.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch_side_panel.tsx new file mode 100644 index 000000000000..a1bb53c5ef1f --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/connect_to_elasticsearch_side_panel.tsx @@ -0,0 +1,110 @@ +/* + * 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, { useCallback } from 'react'; +import { EuiCard, EuiButtonEmpty, EuiFlexGroup, EuiLink, EuiPanel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '../hooks/use_kibana'; +import { docLinks } from '../../common/doc_links'; + +export const ConnectToElasticsearchSidePanel = () => { + const { application } = useKibana().services; + + const onFileUpload = useCallback(() => { + application.navigateToApp('ml', { path: 'filedatavisualizer' }); + }, [application]); + + return ( + + + + } + footer={ + + {i18n.translate('xpack.searchHomepage.connectToElasticsearch.uploadFileButton', { + defaultMessage: 'Upload a file', + })} + + } + /> + + {/* TODO: Enable The “Sample Data” section when the one-click sample data ingestion feature is complete. */} + {/* + } + description={ + + } + footer={ + + {i18n.translate('xpack.searchHomepage.connectToElasticsearch.sampleDatasetButton', { + defaultMessage: 'Add sample data', + })} + + } + /> */} + + } + description={ + + } + footer={ + + {i18n.translate( + 'xpack.searchHomepage.connectToElasticsearch.customerEngineerRequestForm', + { + defaultMessage: 'Submit a request', + } + )} + + } + /> + + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/console_link_button.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/console_link_button.tsx deleted file mode 100644 index b65d41a24d8d..000000000000 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/console_link_button.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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, { useCallback } from 'react'; -import { EuiButtonEmpty } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { ConsolePluginStart } from '@kbn/console-plugin/public'; - -import { useKibana } from '../hooks/use_kibana'; -import { useUsageTracker } from '../hooks/use_usage_tracker'; - -const canOpenConsole = (plugin?: ConsolePluginStart): boolean => { - if (!plugin) return false; - if (!plugin.isEmbeddedConsoleAvailable || !plugin.openEmbeddedConsole) return false; - return plugin.isEmbeddedConsoleAvailable(); -}; - -export const ConsoleLinkButton = () => { - const { - services: { console: consolePlugin }, - } = useKibana(); - const usageTracker = useUsageTracker(); - const openConsole = useCallback(() => { - usageTracker.click('get_started_in_console'); - if (!canOpenConsole(consolePlugin)) return; - - consolePlugin!.openEmbeddedConsole!(); - }, [consolePlugin, usageTracker]); - if (consolePlugin === undefined || consolePlugin.openEmbeddedConsole === undefined) return null; - - return ( - - - - ); -}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/dive_deeper_with_elasticsearch.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/dive_deeper_with_elasticsearch.tsx new file mode 100644 index 000000000000..0e7b24d40e15 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/dive_deeper_with_elasticsearch.tsx @@ -0,0 +1,88 @@ +/* + * 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, + EuiSpacer, + EuiTitle, + useCurrentEuiBreakpoint, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { docLinks } from '../../../common/doc_links'; +import { DocCallouts } from './doc_callouts'; + +export const DiveDeeperWithElasticsearch: React.FC = () => { + const currentBreakpoint = useCurrentEuiBreakpoint(); + return ( + <> + +

+ {i18n.translate('xpack.searchHomepage.diveDeeper.title', { + defaultMessage: 'Dive deeper with Elasticsearch', + })} +

+
+ + + + + + + + + + + + + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/doc_callouts.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/doc_callouts.tsx new file mode 100644 index 000000000000..b5eae89306c8 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/dive_deeper/doc_callouts.tsx @@ -0,0 +1,50 @@ +/* + * 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, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import React from 'react'; + +export interface DocCalloutsProps { + title: string; + description: string; + buttonHref: string; + buttonLabel: string; + dataTestSubj: string; +} + +export const DocCallouts: React.FC = ({ + title, + description, + buttonHref, + buttonLabel, + dataTestSubj, +}) => { + return ( + <> + +

{title}

+
+ + + +

{description}

+
+ + + + {buttonLabel} + + + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/endpoints_header_action.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/endpoints_header_action.tsx deleted file mode 100644 index 408b0d2763df..000000000000 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/endpoints_header_action.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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, { useState, useCallback } from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlyout, EuiHeaderLinks } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { - KibanaWiredConnectionDetailsProvider, - ConnectionDetailsFlyoutContent, -} from '@kbn/cloud/connection_details'; - -import { useUsageTracker } from '../hooks/use_usage_tracker'; - -export const EndpointsHeaderAction = () => { - const usageTracker = useUsageTracker(); - const [open, setOpen] = useState(false); - const onClickEndpointsButton = useCallback(() => { - usageTracker.click('endpoints_and_api_keys'); - setOpen(true); - }, [usageTracker]); - - return ( - <> - - - - - - - - {open && ( - setOpen(false)} size="s"> - - - - - )} - - ); -}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/feature_update_group.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/feature_update_group.tsx new file mode 100644 index 000000000000..09c7ba331eb5 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/feature_update_group.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 from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; + +interface FeatureUpdateGroupProps { + updates: string[]; +} + +export const FeatureUpdateGroup: React.FC = ({ updates }) => { + return ( + + {updates.map((update, index) => ( + + + + + + {update} + + + ))} + + ); +}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/footer/footer.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/footer/footer.tsx new file mode 100644 index 000000000000..cc6243c5883e --- /dev/null +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/footer/footer.tsx @@ -0,0 +1,33 @@ +/* + * 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 from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { docLinks } from '../../../common/doc_links'; + +export const Footer: React.FC = () => ( + <> + + + + {i18n.translate('xpack.searchHomepage.footer.elasticCommunity', { + defaultMessage: 'Connect with the Elastic Community', + })} + + + + + {i18n.translate('xpack.searchHomepage.footer.giveFeedback', { + defaultMessage: 'Give feedback', + })} + + + + + +); diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage.tsx index 76f34caad0a5..d3cf444cbb81 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage.tsx @@ -5,10 +5,10 @@ * 2.0. */ +import { EuiHorizontalRule, EuiPageTemplate } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { EuiPageTemplate } from '@elastic/eui'; - import { useKibana } from '../hooks/use_kibana'; +import { useSearchHomePageRedirect } from '../hooks/use_search_home_page_redirect'; import { SearchHomepageBody } from './search_homepage_body'; import { SearchHomepageHeader } from './search_homepage_header'; @@ -17,6 +17,8 @@ export const SearchHomepagePage = () => { services: { console: consolePlugin }, } = useKibana(); + useSearchHomePageRedirect(); + const embeddableConsole = useMemo( () => (consolePlugin?.EmbeddableConsole ? : null), [consolePlugin] @@ -24,7 +26,8 @@ export const SearchHomepagePage = () => { return ( - + + {embeddableConsole} diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage_body.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage_body.tsx index f27da2346871..8f75b1167cfa 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage_body.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage_body.tsx @@ -6,15 +6,36 @@ */ import React from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; -import { ConsoleLinkButton } from './console_link_button'; +import { ConnectToElasticsearch } from './connect_to_elasticsearch'; +import { AlternateSolutions } from './alternate_solutions/alternate_solutions'; +import { DiveDeeperWithElasticsearch } from './dive_deeper/dive_deeper_with_elasticsearch'; +import { Footer } from './footer/footer'; export const SearchHomepageBody = () => ( - - - + + + + + + + + + + + + + + + + + + + + +