diff --git a/packages/deeplinks/search/constants.ts b/packages/deeplinks/search/constants.ts index 52f7bb201388e..6d9f6492abda7 100644 --- a/packages/deeplinks/search/constants.ts +++ b/packages/deeplinks/search/constants.ts @@ -16,6 +16,7 @@ export const ENTERPRISE_SEARCH_APPSEARCH_APP_ID = 'appSearch'; export const ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID = 'workplaceSearch'; export const SERVERLESS_ES_APP_ID = 'serverlessElasticsearch'; export const SERVERLESS_ES_CONNECTORS_ID = 'serverlessConnectors'; +export const SERVERLESS_ES_WEB_CRAWLERS_ID = 'serverlessWebCrawlers'; export const SERVERLESS_ES_SEARCH_PLAYGROUND_ID = 'searchPlayground'; export const SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID = 'searchInferenceEndpoints'; export const SEARCH_HOMEPAGE = 'searchHomepage'; diff --git a/packages/deeplinks/search/deep_links.ts b/packages/deeplinks/search/deep_links.ts index b23a86b3cc51c..9dc9a8ed18203 100644 --- a/packages/deeplinks/search/deep_links.ts +++ b/packages/deeplinks/search/deep_links.ts @@ -10,6 +10,7 @@ import { SERVERLESS_ES_APP_ID, SERVERLESS_ES_CONNECTORS_ID, + SERVERLESS_ES_WEB_CRAWLERS_ID, ENTERPRISE_SEARCH_APP_ID, ENTERPRISE_SEARCH_CONTENT_APP_ID, ENTERPRISE_SEARCH_APPLICATIONS_APP_ID, @@ -38,6 +39,7 @@ export type EnterpriseSearchAppsearchApp = typeof ENTERPRISE_SEARCH_APPSEARCH_AP export type EnterpriseSearchWorkplaceSearchApp = typeof ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID; export type ServerlessSearchApp = typeof SERVERLESS_ES_APP_ID; export type ConnectorsId = typeof SERVERLESS_ES_CONNECTORS_ID; +export type ServerlessWebCrawlers = typeof SERVERLESS_ES_WEB_CRAWLERS_ID; export type SearchPlaygroundId = typeof SERVERLESS_ES_SEARCH_PLAYGROUND_ID; export type SearchInferenceEndpointsId = typeof SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID; export type SearchHomepage = typeof SEARCH_HOMEPAGE; @@ -68,6 +70,7 @@ export type DeepLinkId = | EnterpriseSearchWorkplaceSearchApp | ServerlessSearchApp | ConnectorsId + | ServerlessWebCrawlers | SearchPlaygroundId | SearchInferenceEndpointsId | SearchHomepage diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx index 5a03d0560dfbf..6be21dd8c63a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx @@ -11,7 +11,6 @@ import { useValues } from 'kea'; import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { CRAWLER } from '../../../../../common/constants'; import { HttpLogic } from '../../../shared/http'; import { GithubIcon } from '../../../shared/icons/github_icon'; import { KibanaLogic } from '../../../shared/kibana'; @@ -49,7 +48,7 @@ export const CrawlerEmptyState: React.FC = () => { color="primary" fill iconType={GithubIcon} - href={CRAWLER.github_repo} + href={'https://github.com/elastic/crawler'} > {i18n.translate( 'xpack.enterpriseSearch.crawlerEmptyState.openSourceCrawlerButtonLabel', diff --git a/x-pack/plugins/serverless_search/common/i18n_string.ts b/x-pack/plugins/serverless_search/common/i18n_string.ts index cf0dbad5277c8..32ec0cf8eb957 100644 --- a/x-pack/plugins/serverless_search/common/i18n_string.ts +++ b/x-pack/plugins/serverless_search/common/i18n_string.ts @@ -65,6 +65,13 @@ export const CONNECTOR_LABEL: string = i18n.translate('xpack.serverlessSearch.co defaultMessage: 'Connector', }); +export const WEB_CRAWLERS_LABEL: string = i18n.translate( + 'xpack.serverlessSearch.webCrawlersLabel', + { + defaultMessage: 'Web crawlers', + } +); + export const DELETE_CONNECTOR_LABEL = i18n.translate( 'xpack.serverlessSearch.connectors.deleteConnectorLabel', { diff --git a/x-pack/plugins/serverless_search/public/application/components/common/decorative_horizontal_stepper.tsx b/x-pack/plugins/serverless_search/public/application/components/common/decorative_horizontal_stepper.tsx new file mode 100644 index 0000000000000..99711dcb9c71e --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/common/decorative_horizontal_stepper.tsx @@ -0,0 +1,45 @@ +/* + * 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 { EuiStepsHorizontal, EuiStepsHorizontalProps } from '@elastic/eui'; +import { css } from '@emotion/react'; + +interface DecorativeHorizontalStepperProps { + stepCount?: number; +} + +export const DecorativeHorizontalStepper: React.FC = ({ + stepCount = 2, +}) => { + // Generate the steps dynamically based on the stepCount prop + const horizontalSteps: EuiStepsHorizontalProps['steps'] = Array.from( + { length: stepCount }, + (_, index) => ({ + title: '', + status: 'incomplete', + onClick: () => {}, + }) + ); + + return ( + /* This is a presentational component, not intended for user interaction: + pointer-events: none, prevents user interaction with the component. + inert prevents click, focus, and other interactive events, removing it from the tab order. + role="presentation" indicates that this component is purely decorative and not interactive for assistive technologies. + Together, these attributes help ensure the component is accesible. */ + + ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/connector_icon.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_icon.tsx new file mode 100644 index 0000000000000..7ae2961ef10b8 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_icon.tsx @@ -0,0 +1,19 @@ +/* + * 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 { EuiToolTip, EuiIcon } from '@elastic/eui'; + +export const ConnectorIcon: React.FC<{ name: string; serviceType: string; iconPath?: string }> = ({ + name, + serviceType, + iconPath, +}) => ( + + + +); diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/elastic_managed_connector_coming_soon.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/elastic_managed_connector_coming_soon.tsx new file mode 100644 index 0000000000000..3057c6806fd73 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/elastic_managed_connector_coming_soon.tsx @@ -0,0 +1,196 @@ +/* + * 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, + EuiPanel, + EuiIcon, + EuiTitle, + EuiText, + EuiBadge, + EuiButtonEmpty, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +// import { generatePath } from 'react-router-dom'; +import { SERVERLESS_ES_CONNECTORS_ID } from '@kbn/deeplinks-search/constants'; +import { useKibanaServices } from '../../hooks/use_kibana'; +import { useConnectorTypes } from '../../hooks/api/use_connector_types'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +import { BACK_LABEL } from '../../../../common/i18n_string'; +// import { BASE_CONNECTORS_PATH } from '../../constants'; +import { ConnectorIcon } from './connector_icon'; +import { DecorativeHorizontalStepper } from '../common/decorative_horizontal_stepper'; + +export const ElasticManagedConnectorComingSoon: React.FC = () => { + const connectorTypes = useConnectorTypes(); + + const connectorExamples = connectorTypes.filter((connector) => + ['Gmail', 'Sharepoint Online', 'Jira Cloud', 'Dropbox'].includes(connector.name) + ); + + const { + application: { navigateToApp }, + } = useKibanaServices(); + + const assetBasePath = useAssetBasePath(); + const connectorsIcon = assetBasePath + '/connectors.svg'; + return ( + + + + + + navigateToApp(SERVERLESS_ES_CONNECTORS_ID)} + > + {BACK_LABEL} + + + + + + + +

+ {i18n.translate('xpack.serverlessSearch.elasticManagedConnectorEmpty.title', { + defaultMessage: 'Elastic managed connectors', + })} +

+
+
+ + Coming soon + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedConnectorEmpty.description', + { + defaultMessage: + "We're actively developing Elastic managed connectors, that won't require any self-managed infrastructure. You'll be able to handle all configuration in the UI. This will simplify syncing your data into a serverless Elasticsearch project. This new workflow will have two steps:", + } + )} +

+
+
+ + + + + + + + + + + + {connectorExamples.map((connector, index) => ( + + {index === Math.floor(connectorExamples.length / 2) && ( + + + + )} + + + + + ))} + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedConnectorEmpty.guideOneDescription', + { + defaultMessage: + "Choose from over 30 third-party data sources you'd like to sync", + } + )} +

+
+
+
+
+ + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedConnectorEmpty.guideThreeDescription', + { + defaultMessage: + 'Enter access and connection details for your data source and run your first sync using the Kibana UI', + } + )} +

+
+
+
+
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx index 56c7a9aaf8155..0767f8cfaf276 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx @@ -14,30 +14,49 @@ import { EuiText, EuiLink, EuiButton, - EuiToolTip, + EuiBadge, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { docLinks } from '../../../../common/doc_links'; +import { useKibanaServices } from '../../hooks/use_kibana'; import { useConnectorTypes } from '../../hooks/api/use_connector_types'; import { useCreateConnector } from '../../hooks/api/use_create_connector'; import { useAssetBasePath } from '../../hooks/use_asset_base_path'; import { useConnectors } from '../../hooks/api/use_connectors'; +import { DecorativeHorizontalStepper } from '../common/decorative_horizontal_stepper'; +import { ConnectorIcon } from './connector_icon'; + +import { ELASTIC_MANAGED_CONNECTOR_PATH, BASE_CONNECTORS_PATH } from '../../constants'; export const EmptyConnectorsPrompt: React.FC = () => { const connectorTypes = useConnectorTypes(); + + const connectorExamples = connectorTypes.filter((connector) => + ['Gmail', 'Sharepoint Online', 'Jira Cloud', 'Dropbox'].includes(connector.name) + ); const { createConnector, isLoading } = useCreateConnector(); const { data } = useConnectors(); const assetBasePath = useAssetBasePath(); const connectorsPath = assetBasePath + '/connectors.svg'; + + const { + application: { navigateToUrl }, + } = useKibanaServices(); + return ( - + @@ -45,13 +64,13 @@ export const EmptyConnectorsPrompt: React.FC = () => {

{i18n.translate('xpack.serverlessSearch.connectorsEmpty.title', { - defaultMessage: 'Create a connector', + defaultMessage: 'Set up a new connector', })}

- +

{i18n.translate('xpack.serverlessSearch.connectorsEmpty.description', { defaultMessage: @@ -60,155 +79,215 @@ export const EmptyConnectorsPrompt: React.FC = () => {

- - - - - - - - - - -

- {i18n.translate( - 'xpack.serverlessSearch.connectorsEmpty.guideOneDescription', - { - defaultMessage: "Choose a data source you'd like to sync", - } - )} -

-
-
-
+ + + + + - - - - - - - -

- - {i18n.translate( - 'xpack.serverlessSearch.connectorsEmpty.sourceLabel', - { defaultMessage: 'source' } - )} - - ), - docker: ( - - {i18n.translate( - 'xpack.serverlessSearch.connectorsEmpty.dockerLabel', - { defaultMessage: 'Docker' } - )} - - ), - }} - /> -

-
-
-
-
- - - + + + + + + {connectorExamples.map((connector, index) => ( + + {index === Math.floor(connectorExamples.length / 2) && ( + + + + )} + + + + + ))} + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.connectorsEmpty.guideOneDescription', + { + defaultMessage: + "Choose from over 30 third-party data sources you'd like to sync", + } + )} +

+
+
+
+
+ + - - - - - - - + - + - - - -

- {i18n.translate( - 'xpack.serverlessSearch.connectorsEmpty.guideThreeDescription', - { - defaultMessage: - 'Enter access and connection details for your data source and run your first sync', - } - )} -

-
-
-
+ + +

+ + {i18n.translate( + 'xpack.serverlessSearch.connectorsEmpty.sourceLabel', + { defaultMessage: 'source' } + )} + + ), + docker: ( + + {i18n.translate( + 'xpack.serverlessSearch.connectorsEmpty.dockerLabel', + { defaultMessage: 'Docker' } + )} + + ), + }} + /> +

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.connectorsEmpty.guideThreeDescription', + { + defaultMessage: + 'Enter access and connection details for your data source and run your first sync', + } + )} +

+
+
+
+
+
+
+
+
+ + + createConnector()} + isLoading={isLoading} + > + {i18n.translate('xpack.serverlessSearch.connectorsEmpty.selfManagedButton', { + defaultMessage: 'Self-managed connector', + })} + + + + + + + navigateToUrl(`${BASE_CONNECTORS_PATH}/${ELASTIC_MANAGED_CONNECTOR_PATH}`) + } + > + {i18n.translate( + 'xpack.serverlessSearch.connectorsEmpty.elasticManagedButton', + { + defaultMessage: 'Elastic managed connector', + } + )} + + + + Coming soon - - - - createConnector()} - isLoading={isLoading} - > - {i18n.translate('xpack.serverlessSearch.connectorsEmpty.createConnector', { - defaultMessage: 'Create connector', - })} - - + + - - -

- {i18n.translate('xpack.serverlessSearch.connectorsEmpty.availableConnectors', { - defaultMessage: 'Available connectors', - })} -

-
-
- - - {connectorTypes.map((connectorType) => ( - - - - - - ))} - - ); }; diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors_elastic_managed.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors_elastic_managed.tsx new file mode 100644 index 0000000000000..e645ede3d67e8 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/connectors_elastic_managed.tsx @@ -0,0 +1,63 @@ +/* + * 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 { EuiLink, EuiPageTemplate, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useMemo } from 'react'; + +import { LEARN_MORE_LABEL } from '../../../common/i18n_string'; + +import { useKibanaServices } from '../hooks/use_kibana'; +import { ElasticManagedConnectorComingSoon } from './connectors/elastic_managed_connector_coming_soon'; + +import { docLinks } from '../../../common/doc_links'; + +export const ConnectorsElasticManaged = () => { + const { console: consolePlugin } = useKibanaServices(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + + return ( + + + +

+ + {LEARN_MORE_LABEL} + + ), + }} + /> +

+
+
+ + + + {embeddableConsole} +
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx index f059a8d531eac..775cec8db1551 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx @@ -7,30 +7,36 @@ import { EuiButton, + EuiCallOut, EuiFlexGroup, EuiFlexItem, - EuiIcon, EuiLink, EuiPageTemplate, + EuiSpacer, EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; +import { GithubLink } from '@kbn/search-api-panels'; import { docLinks } from '../../../common/doc_links'; import { LEARN_MORE_LABEL } from '../../../common/i18n_string'; -import { PLUGIN_ID } from '../../../common'; import { useConnectors } from '../hooks/api/use_connectors'; import { useCreateConnector } from '../hooks/api/use_create_connector'; import { useKibanaServices } from '../hooks/use_kibana'; import { EmptyConnectorsPrompt } from './connectors/empty_connectors_prompt'; import { ConnectorsTable } from './connectors/connectors_table'; import { ConnectorPrivilegesCallout } from './connectors/connector_config/connector_privileges_callout'; +import { useAssetBasePath } from '../hooks/use_asset_base_path'; + +import { BASE_CONNECTORS_PATH, ELASTIC_MANAGED_CONNECTOR_PATH } from '../constants'; + +const CALLOUT_KEY = 'search.connectors.ElasticManaged.ComingSoon.feedbackCallout'; export const ConnectorsOverview = () => { const { data, isLoading: connectorsLoading } = useConnectors(); - const { http, console: consolePlugin } = useKibanaServices(); + const { console: consolePlugin } = useKibanaServices(); const { createConnector, isLoading } = useCreateConnector(); const embeddableConsole = useMemo( () => (consolePlugin?.EmbeddableConsole ? : null), @@ -39,6 +45,18 @@ export const ConnectorsOverview = () => { const canManageConnectors = !data || data.canManageConnectors; + const { + application: { navigateToUrl }, + } = useKibanaServices(); + + const [showCallOut, setShowCallOut] = useState(sessionStorage.getItem(CALLOUT_KEY) !== 'hidden'); + + const onDismiss = () => { + setShowCallOut(false); + sessionStorage.setItem(CALLOUT_KEY, 'hidden'); + }; + const assetBasePath = useAssetBasePath(); + return ( { })} data-test-subj="serverlessSearchConnectorsTitle" restrictWidth - rightSideItems={[ - - - - - - - - - 0 + ? [ + + + + + + createConnector()} > - {i18n.translate('xpack.serverlessSearch.connectorsPythonLink', { - defaultMessage: 'elastic/connectors', + {i18n.translate('xpack.serverlessSearch.connectors.createConnector', { + defaultMessage: 'Create connector', })} - - - - - - - createConnector()} - > - {i18n.translate('xpack.serverlessSearch.connectors.createConnector', { - defaultMessage: 'Create connector', - })} - - - , - ]} + + + , + ] + : undefined + } >

@@ -103,7 +107,6 @@ export const ConnectorsOverview = () => { learnMoreLink: ( @@ -118,7 +121,39 @@ export const ConnectorsOverview = () => { {connectorsLoading || (data?.connectors || []).length > 0 ? ( - + <> + {showCallOut && ( + <> + +

+ {i18n.translate( + 'xpack.serverlessSearch.connectorsOverview.calloutDescription', + { + defaultMessage: + "We're actively developing Elastic managed connectors, that won't require any self-managed infrastructure. You'll be able to handle all configuration in the UI. This will simplify syncing your data into a serverless Elasticsearch project.", + } + )} +

+ + navigateToUrl(`${BASE_CONNECTORS_PATH}/${ELASTIC_MANAGED_CONNECTOR_PATH}`) + } + > + {LEARN_MORE_LABEL} + + + + + )} + + ) : ( )} diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors_router.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors_router.tsx index f8c224ed2c9c6..4085b812d1f3d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors_router.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors_router.tsx @@ -9,10 +9,15 @@ import { Route, Routes } from '@kbn/shared-ux-router'; import React from 'react'; import { EditConnector } from './connectors/edit_connector'; import { ConnectorsOverview } from './connectors_overview'; +import { ConnectorsElasticManaged } from './connectors_elastic_managed'; +import { ELASTIC_MANAGED_CONNECTOR_PATH } from '../constants'; export const ConnectorsRouter: React.FC = () => { return ( + + + diff --git a/x-pack/plugins/serverless_search/public/application/components/web_crawlers/elastic_managed_web_crawler_coming_soon.tsx b/x-pack/plugins/serverless_search/public/application/components/web_crawlers/elastic_managed_web_crawler_coming_soon.tsx new file mode 100644 index 0000000000000..ba146ed847990 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/web_crawlers/elastic_managed_web_crawler_coming_soon.tsx @@ -0,0 +1,171 @@ +/* + * 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, + EuiPanel, + EuiIcon, + EuiTitle, + EuiText, + EuiBadge, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useKibanaServices } from '../../hooks/use_kibana'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +import { BACK_LABEL } from '../../../../common/i18n_string'; +import { DecorativeHorizontalStepper } from '../common/decorative_horizontal_stepper'; + +export const ElasticManagedWebCrawlersCommingSoon: React.FC = () => { + const { + application: { navigateToUrl }, + } = useKibanaServices(); + + const assetBasePath = useAssetBasePath(); + const webCrawlerIcon = assetBasePath + '/web_crawlers.svg'; + + return ( + + + + + + navigateToUrl(`./`)} + > + {BACK_LABEL} + + + + + + + +

+ {i18n.translate('xpack.serverlessSearch.elasticManagedWebCrawlerEmpty.title', { + defaultMessage: 'Elastic managed web crawlers', + })} +

+
+
+ + Coming soon + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedWebCrawlerEmpty.description', + { + defaultMessage: + "We're actively developing Elastic managed web crawlers, that won't require any self-managed infrastructure. You'll be able to handle all configuration in the UI. This will simplify syncing your data into a serverless Elasticsearch project. This new workflow will have two steps:", + } + )} +

+
+
+ + + + + + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedWebCrawlerEmpty.guideOneDescription', + { + defaultMessage: 'Set one or more domain URLs you want to crawl', + } + )} +

+
+
+
+
+ + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.elasticManagedWebCrawlerEmpty.guideThreeDescription', + { + defaultMessage: + 'Configure all the web crawler process using Kibana', + } + )} +

+
+
+
+
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/web_crawlers/empty_web_crawlers_prompt.tsx b/x-pack/plugins/serverless_search/public/application/components/web_crawlers/empty_web_crawlers_prompt.tsx new file mode 100644 index 0000000000000..8170ed6da3134 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/web_crawlers/empty_web_crawlers_prompt.tsx @@ -0,0 +1,269 @@ +/* + * 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, + EuiPanel, + EuiIcon, + EuiTitle, + EuiText, + EuiLink, + EuiButton, + EuiBadge, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useKibanaServices } from '../../hooks/use_kibana'; +import { useAssetBasePath } from '../../hooks/use_asset_base_path'; + +import { ELASTIC_MANAGED_WEB_CRAWLERS_PATH, BASE_WEB_CRAWLERS_PATH } from '../../constants'; +import { DecorativeHorizontalStepper } from '../common/decorative_horizontal_stepper'; + +export const EmptyWebCrawlersPrompt: React.FC = () => { + const { + application: { navigateToUrl }, + } = useKibanaServices(); + + const assetBasePath = useAssetBasePath(); + const webCrawlersIcon = assetBasePath + '/web_crawlers.svg'; + const githubIcon = assetBasePath + '/github_white.svg'; + + return ( + + + + + + + + + +

+ {i18n.translate('xpack.serverlessSearch.webCrawlersEmpty.title', { + defaultMessage: 'Set up a web crawler', + })} +

+
+
+ + +

+ {i18n.translate('xpack.serverlessSearch.webCrawlersEmpty.description', { + defaultMessage: + "To set up and deploy a web crawler you'll be working between data source, your terminal, and the Kibana UI. The high level process looks like this:", + })} +

+
+
+ + + + + + + + + + + + + + + + + + + + + + +

+ + {i18n.translate( + 'xpack.serverlessSearch.webCrawlersEmpty.sourceLabel', + { defaultMessage: 'source' } + )} + + ), + docker: ( + + {i18n.translate( + 'xpack.serverlessSearch.webCrawlersEmpty.dockerLabel', + { defaultMessage: 'Docker' } + )} + + ), + }} + /> +

+
+
+
+
+ + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.webCrawlersEmpty.guideOneDescription', + { + defaultMessage: 'Set one or more domain URLs you want to crawl', + } + )} +

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.serverlessSearch.webCrawlersEmpty.guideThreeDescription', + { + defaultMessage: + 'Configure your web crawler and connect it to Elasticsearch', + } + )} +

+
+
+
+
+
+
+
+
+ + + + {i18n.translate('xpack.serverlessSearch.webCrawlersEmpty.selfManagedButton', { + defaultMessage: 'Self-managed web crawler', + })} + + + + + + + navigateToUrl( + `${BASE_WEB_CRAWLERS_PATH}/${ELASTIC_MANAGED_WEB_CRAWLERS_PATH}` + ) + } + > + {i18n.translate( + 'xpack.serverlessSearch.webCrawlersEmpty.elasticManagedButton', + { + defaultMessage: 'Elastic managed web crawler', + } + )} + + + + Coming soon + + + + +
+
+
+
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/web_crawlers_elastic_managed.tsx b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_elastic_managed.tsx new file mode 100644 index 0000000000000..8ac5a0c59dd14 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_elastic_managed.tsx @@ -0,0 +1,62 @@ +/* + * 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 { EuiLink, EuiPageTemplate, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useMemo } from 'react'; + +import { LEARN_MORE_LABEL } from '../../../common/i18n_string'; + +import { useKibanaServices } from '../hooks/use_kibana'; +import { ElasticManagedWebCrawlersCommingSoon } from './web_crawlers/elastic_managed_web_crawler_coming_soon'; + +export const WebCrawlersElasticManaged = () => { + const { console: consolePlugin } = useKibanaServices(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + + return ( + + + +

+ + {LEARN_MORE_LABEL} + + ), + }} + /> +

+
+
+ + + + {embeddableConsole} +
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/web_crawlers_overview.tsx b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_overview.tsx new file mode 100644 index 0000000000000..b7e3763539536 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_overview.tsx @@ -0,0 +1,62 @@ +/* + * 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 { EuiLink, EuiPageTemplate, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useMemo } from 'react'; + +import { LEARN_MORE_LABEL } from '../../../common/i18n_string'; + +import { useKibanaServices } from '../hooks/use_kibana'; +import { EmptyWebCrawlersPrompt } from './web_crawlers/empty_web_crawlers_prompt'; + +export const WebCrawlersOverview = () => { + const { console: consolePlugin } = useKibanaServices(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + + return ( + + + +

+ + {LEARN_MORE_LABEL} + + ), + }} + /> +

+
+
+ + + + {embeddableConsole} +
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/web_crawlers_router.tsx b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_router.tsx new file mode 100644 index 0000000000000..7e4ae7a5d2657 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/web_crawlers_router.tsx @@ -0,0 +1,24 @@ +/* + * 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 { Route, Routes } from '@kbn/shared-ux-router'; +import React from 'react'; +import { WebCrawlersOverview } from './web_crawlers_overview'; +import { WebCrawlersElasticManaged } from './web_crawlers_elastic_managed'; + +export const WebCrawlersRouter: React.FC = () => { + return ( + + + + + + + + + ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/constants.ts b/x-pack/plugins/serverless_search/public/application/constants.ts index 8e8c15638a860..e5dce2a328d05 100644 --- a/x-pack/plugins/serverless_search/public/application/constants.ts +++ b/x-pack/plugins/serverless_search/public/application/constants.ts @@ -12,5 +12,8 @@ export const INDEX_NAME_PLACEHOLDER = 'index_name'; // Paths export const BASE_CONNECTORS_PATH = 'connectors'; +export const BASE_WEB_CRAWLERS_PATH = 'web_crawlers'; export const EDIT_CONNECTOR_PATH = `${BASE_CONNECTORS_PATH}/:id`; +export const ELASTIC_MANAGED_CONNECTOR_PATH = '/elastic_managed'; +export const ELASTIC_MANAGED_WEB_CRAWLERS_PATH = '/elastic_managed'; export const FILE_UPLOAD_PATH = '/app/ml/filedatavisualizer'; diff --git a/x-pack/plugins/serverless_search/public/application/web_crawlers.tsx b/x-pack/plugins/serverless_search/public/application/web_crawlers.tsx new file mode 100644 index 0000000000000..e9a590c6dee57 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/web_crawlers.tsx @@ -0,0 +1,46 @@ +/* + * 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 { CoreStart } from '@kbn/core-lifecycle-browser'; + +import { I18nProvider } from '@kbn/i18n-react'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +import ReactDOM from 'react-dom'; +import React from 'react'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { Router } from '@kbn/shared-ux-router'; +import { ServerlessSearchContext } from './hooks/use_kibana'; + +export async function renderApp( + element: HTMLElement, + core: CoreStart, + services: ServerlessSearchContext, + queryClient: QueryClient +) { + const { WebCrawlersRouter } = await import('./components/web_crawlers_router'); + + ReactDOM.render( + + + + + + + + + + + + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); +} diff --git a/x-pack/plugins/serverless_search/public/assets/connectors.svg b/x-pack/plugins/serverless_search/public/assets/connectors.svg index 659e9e5494352..53624f84a8a00 100644 --- a/x-pack/plugins/serverless_search/public/assets/connectors.svg +++ b/x-pack/plugins/serverless_search/public/assets/connectors.svg @@ -1 +1,11 @@ - \ No newline at end of file + + + + + + + + + + + diff --git a/x-pack/plugins/serverless_search/public/assets/web_crawlers.svg b/x-pack/plugins/serverless_search/public/assets/web_crawlers.svg new file mode 100644 index 0000000000000..d6e2464c0f003 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/assets/web_crawlers.svg @@ -0,0 +1,4 @@ + + + + diff --git a/x-pack/plugins/serverless_search/public/navigation_tree.ts b/x-pack/plugins/serverless_search/public/navigation_tree.ts index ae8f41b8b17f8..524888d0d33e6 100644 --- a/x-pack/plugins/serverless_search/public/navigation_tree.ts +++ b/x-pack/plugins/serverless_search/public/navigation_tree.ts @@ -8,7 +8,7 @@ import type { AppDeepLinkId, NavigationTreeDefinition } from '@kbn/core-chrome-browser'; import type { ApplicationStart } from '@kbn/core-application-browser'; import { i18n } from '@kbn/i18n'; -import { CONNECTORS_LABEL } from '../common/i18n_string'; +import { CONNECTORS_LABEL, WEB_CRAWLERS_LABEL } from '../common/i18n_string'; export const navigationTree = ({ isAppRegistered }: ApplicationStart): NavigationTreeDefinition => { function isAvailable(appId: string, content: T): T[] { @@ -54,6 +54,10 @@ export const navigationTree = ({ isAppRegistered }: ApplicationStart): Navigatio title: CONNECTORS_LABEL, link: 'serverlessConnectors', }, + { + title: WEB_CRAWLERS_LABEL, + link: 'serverlessWebCrawlers', + }, ], }, { diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index d097cd1eb3ad4..3c24211d2a520 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -120,6 +120,27 @@ export class ServerlessSearchPlugin }, }); + const webCrawlersTitle = i18n.translate('xpack.serverlessSearch.app.webCrawlers.title', { + defaultMessage: 'Web Crawlers', + }); + + core.application.register({ + id: 'serverlessWebCrawlers', + title: webCrawlersTitle, + appRoute: '/app/web_crawlers', + euiIconType: 'logoElastic', + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + visibleIn: [], + async mount({ element, history }: AppMountParameters) { + const { renderApp } = await import('./application/web_crawlers'); + const [coreStart, services] = await core.getStartServices(); + coreStart.chrome.docTitle.change(webCrawlersTitle); + docLinks.setDocLinks(coreStart.docLinks.links); + + return await renderApp(element, coreStart, { history, ...services }, queryClient); + }, + }); + const { searchIndices } = setupDeps; core.application.register({ id: 'serverlessHomeRedirect', diff --git a/x-pack/plugins/serverless_search/tsconfig.json b/x-pack/plugins/serverless_search/tsconfig.json index 794f146299a0f..854a90fdb5fb5 100644 --- a/x-pack/plugins/serverless_search/tsconfig.json +++ b/x-pack/plugins/serverless_search/tsconfig.json @@ -54,6 +54,7 @@ "@kbn/core-http-server", "@kbn/logging", "@kbn/security-plugin-types-public", + "@kbn/deeplinks-search", "@kbn/core-application-browser", ] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 5a5e006df3f28..cb51a020eeaac 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -43115,8 +43115,6 @@ "xpack.serverlessSearch.connectors.typeLabel": "Type", "xpack.serverlessSearch.connectors.variablesTitle": "Variable pour votre {url}", "xpack.serverlessSearch.connectors.waitingForConnection": "En attente de connexion", - "xpack.serverlessSearch.connectorsEmpty.availableConnectors": "Connecteurs disponibles", - "xpack.serverlessSearch.connectorsEmpty.createConnector": "Créer un connecteur", "xpack.serverlessSearch.connectorsEmpty.description": "La configuration et le déploiement d'un connecteur se passe entre la source de données tierce, votre terminal et l'UI sans serveur d'Elasticsearch. Le processus à haut niveau ressemble à ça :", "xpack.serverlessSearch.connectorsEmpty.dockerLabel": "Docker", "xpack.serverlessSearch.connectorsEmpty.guideOneDescription": "Choisissez une source de données à synchroniser", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 640d5d403d886..b5a0c4a1481df 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -43081,8 +43081,6 @@ "xpack.serverlessSearch.connectors.typeLabel": "型", "xpack.serverlessSearch.connectors.variablesTitle": "{url}の変数", "xpack.serverlessSearch.connectors.waitingForConnection": "接続を待機中", - "xpack.serverlessSearch.connectorsEmpty.availableConnectors": "使用可能なコネクター", - "xpack.serverlessSearch.connectorsEmpty.createConnector": "コネクターを作成", "xpack.serverlessSearch.connectorsEmpty.description": "コネクターを設定およびデプロイするには、サードパーティのデータソース、端末、ElasticsearchサーバーレスUI の間で作業することになります。プロセスの概要は次のとおりです。", "xpack.serverlessSearch.connectorsEmpty.dockerLabel": "Docker", "xpack.serverlessSearch.connectorsEmpty.guideOneDescription": "同期したいデータソースを選択します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 991c23b73faa0..8c21f00ca8228 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -42429,8 +42429,6 @@ "xpack.serverlessSearch.connectors.typeLabel": "类型", "xpack.serverlessSearch.connectors.variablesTitle": "您的 {url} 的变量", "xpack.serverlessSearch.connectors.waitingForConnection": "等待连接", - "xpack.serverlessSearch.connectorsEmpty.availableConnectors": "可用连接器", - "xpack.serverlessSearch.connectorsEmpty.createConnector": "创建连接器", "xpack.serverlessSearch.connectorsEmpty.description": "要设置并部署连接器,您需要在第三方数据源、终端与 Elasticsearch 无服务器 UI 之间开展工作。高级流程类似于这样:", "xpack.serverlessSearch.connectorsEmpty.dockerLabel": "Docker", "xpack.serverlessSearch.connectorsEmpty.guideOneDescription": "选择要同步的数据源", diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_connectors_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_connectors_page.ts index 615e3397a45ce..78554ea05beda 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_connectors_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_connectors_page.ts @@ -14,7 +14,7 @@ export function SvlSearchConnectorsPageProvider({ getService }: FtrProviderConte return { connectorConfigurationPage: { async createConnector() { - await testSubjects.click('serverlessSearchConnectorsOverviewCreateConnectorButton'); + await testSubjects.click('serverlessSearchEmptyConnectorsPromptCreateConnectorButton'); await testSubjects.existOrFail('serverlessSearchEditConnectorButton'); await testSubjects.exists('serverlessSearchConnectorLinkElasticsearchRunWithDockerButton'); await testSubjects.exists('serverlessSearchConnectorLinkElasticsearchRunFromSourceButton'); @@ -90,9 +90,9 @@ export function SvlSearchConnectorsPageProvider({ getService }: FtrProviderConte }, async expectConnectorOverviewPageComponentsToExist() { await testSubjects.existOrFail('serverlessSearchConnectorsTitle'); - await testSubjects.existOrFail('serverlessSearchConnectorsOverviewElasticConnectorsLink'); + // await testSubjects.existOrFail('serverlessSearchConnectorsOverviewElasticConnectorsLink'); await testSubjects.exists('serverlessSearchEmptyConnectorsPromptCreateConnectorButton'); - await testSubjects.existOrFail('serverlessSearchConnectorsOverviewCreateConnectorButton'); + // await testSubjects.existOrFail('serverlessSearchConnectorsOverviewCreateConnectorButton'); }, async expectConnectorTableToExist() { await testSubjects.existOrFail('serverlessSearchConnectorTable'); diff --git a/x-pack/test_serverless/functional/test_suites/search/navigation.ts b/x-pack/test_serverless/functional/test_suites/search/navigation.ts index 97952d68f8fd1..24009866b2b15 100644 --- a/x-pack/test_serverless/functional/test_suites/search/navigation.ts +++ b/x-pack/test_serverless/functional/test_suites/search/navigation.ts @@ -241,6 +241,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await solutionNavigation.sidenav.expectLinkExists({ text: 'Data' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Index Management' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Connectors' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Web crawlers' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Build' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Dev Tools' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Playground' }); @@ -265,6 +266,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { 'data', 'management:index_management', 'serverlessConnectors', + 'serverlessWebCrawlers', 'build', 'dev_tools', 'searchPlayground',