[Home page V1] Create a home page in serverless env (#223172)

## Summary

This PR introduces a new Search Home page.

Currently, the homepage experience differs across Classic nav, Solution
nav, and Serverless. Our goal is to unify these into a consistent,
foundational experience that we can iterate on to create a more
personalized and customizable homepage for Elasticsearch users.

The new page includes:

- 	An option to connect to Elasticsearch
- 	File upload and sample dataset ingestion
- 	Entry points to explore Elastic’s AI Search capabilities
- 	Quick access to Observability and Security solutions
- Links to Search Labs, Python notebooks, and Elasticsearch
documentation



https://github.com/user-attachments/assets/7b1b5330-59b4-43b7-aa5b-000fcd2654e2


### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Lisa Cawley <lcawley@elastic.co>
Co-authored-by: Rodney Norris <rodney@tattdcodemonkey.com>
This commit is contained in:
Saikat Sarkar 2025-06-24 10:54:04 -06:00 committed by GitHub
parent a8c7893458
commit b713dc94eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 2775 additions and 285 deletions

View file

@ -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

View file

@ -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`,

View file

@ -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;

View file

@ -33704,8 +33704,6 @@
"xpack.searchAssistant.navControl.initFailureErrorTitle": "Impossible dinitialiser l'assistant dAI",
"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",

View file

@ -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": "インデックス管理",

View file

@ -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": "索引管理",

View file

@ -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();

View file

@ -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';

View file

@ -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;
}

View file

@ -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(
<KibanaRenderContextProvider {...core}>
<KibanaContextProvider services={{ ...core, ...services }}>
<UsageTrackerContextProvider usageCollection={services.usageCollection}>
<I18nProvider>
<Router history={services.history}>
<HomepageRouter />
</Router>
</I18nProvider>
<QueryClientProvider client={queryClient}>
<I18nProvider>
<Router history={services.history}>
<HomepageRouter />
</Router>
</I18nProvider>
</QueryClientProvider>
</UsageTrackerContextProvider>
</KibanaContextProvider>
</KibanaRenderContextProvider>,

View file

@ -0,0 +1,20 @@
<svg width="119" height="103" viewBox="0 0 119 103" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_825_5618" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="119" height="103">
<path d="M119 0H0V102.289H119V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_825_5618)">
<path d="M98.9847 0.560669H17.8524C14.7723 0.560669 12.2754 3.0576 12.2754 6.1377V81.1723C12.2754 84.2524 14.7723 86.7493 17.8524 86.7493H98.9847C102.065 86.7493 104.562 84.2524 104.562 81.1723V6.1377C104.562 3.0576 102.065 0.560669 98.9847 0.560669Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M101.668 0.50061H20.5355C17.4554 0.50061 14.9585 2.99753 14.9585 6.07764V81.1122C14.9585 84.1924 17.4554 86.6893 20.5355 86.6893H101.668C104.748 86.6893 107.245 84.1924 107.245 81.1122V6.07764C107.245 2.99753 104.748 0.50061 101.668 0.50061Z" fill="#0B64DD"/>
<path d="M101.668 0.50061H20.5355C17.4554 0.50061 14.9585 2.99753 14.9585 6.07764V81.1122C14.9585 84.1924 17.4554 86.6893 20.5355 86.6893H101.668C104.748 86.6893 107.245 84.1924 107.245 81.1122V6.07764C107.245 2.99753 104.748 0.50061 101.668 0.50061Z" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M15.0288 15.9801H107.065" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M35.4145 6.93872H24.7211C23.6427 6.93872 22.7686 7.81287 22.7686 8.89118V9.18155C22.7686 10.2599 23.6427 11.134 24.7211 11.134H35.4145C36.4928 11.134 37.367 10.2599 37.367 9.18155V8.89118C37.367 7.81287 36.4928 6.93872 35.4145 6.93872Z" fill="white"/>
<path d="M111.13 33.0817H7.86978C3.79983 33.0817 0.500488 36.3811 0.500488 40.451V56.2709C0.500488 60.3409 3.79983 63.6402 7.86978 63.6402H111.13C115.2 63.6402 118.499 60.3409 118.499 56.2709V40.451C118.499 36.3811 115.2 33.0817 111.13 33.0817Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M111.13 29.8575H7.86978C3.79983 29.8575 0.500488 33.1569 0.500488 37.2268V53.0468C0.500488 57.1167 3.79983 60.4161 7.86978 60.4161H111.13C115.2 60.4161 118.499 57.1167 118.499 53.0468V37.2268C118.499 33.1569 115.2 29.8575 111.13 29.8575Z" fill="white" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M53.3571 39.3296C61.3272 36.3959 68.7265 39.5298 72.7416 45.1169C60.7264 35.3245 41.9828 46.8791 43.8351 61.898C44.1355 64.3511 44.9966 66.5939 46.2682 68.5364C43.4947 66.1834 41.5422 62.7491 41.1017 58.2034C40.3107 49.993 45.7175 42.1331 53.3571 39.3196V39.3296Z" fill="#101C3F" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M35.4544 59.7053C36.9763 71.9607 48.7912 79.9408 60.7163 78.0384C80.3811 74.9144 90.5639 47.0393 71.4598 35.6349C54.929 25.7625 33.1716 40.9917 35.4544 59.7053ZM70.5487 42.1832C82.9843 53.0068 72.2108 72.942 57.0617 72.6716C49.0716 72.5314 42.1328 66.664 41.1516 58.6439C39.2793 43.4147 58.5335 31.72 70.5487 42.1732V42.1832Z" fill="#48EFCF" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M60.7163 78.0384C76.0056 75.6053 85.5476 58.2334 80.0807 45.2571C84.7366 51.6451 86.0282 60.4662 81.122 68.9669C72.371 84.1361 48.8714 85.4577 40.7612 71.1497C45.6373 76.4664 53.1468 79.2399 60.7163 78.0384Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M91.5329 100.928L91.9924 100.573C93.7688 99.1998 94.0959 96.6468 92.7229 94.8704L77.4522 75.1124C76.0792 73.336 73.5262 73.0089 71.7498 74.3819L71.2903 74.737C69.514 76.11 69.1869 78.663 70.5599 80.4394L85.8306 100.197C87.2035 101.974 89.7566 102.301 91.5329 100.928Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M92.7434 99.9964L93.2029 99.6412C94.9793 98.2683 95.3063 95.7153 93.9334 93.9389L78.6626 74.1809C77.2897 72.4045 74.7367 72.0774 72.9603 73.4504L72.5008 73.8055C70.7244 75.1784 70.3974 77.7315 71.7703 79.5079L87.041 99.2659C88.414 101.042 90.967 101.369 92.7434 99.9964Z" fill="#48EFCF" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M12.2754 35.0242V55.49" stroke="#101C3F" stroke-miterlimit="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,274 @@
<svg width="100%" height="auto" viewBox="0 0 676 380" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_736_26044)">
<mask id="mask0_736_26044" maskUnits="userSpaceOnUse" x="-1" y="0" width="677" height="380">
<path d="M675.204 0H-0.351837V380H675.204V0Z" fill="white" />
</mask>
<g mask="url(#mask0_736_26044)">
<path
d="M381.443 289.493L469.539 291.986C474.799 292.268 478.849 287.628 478.012 282.281L468.515 218.147C467.822 213.736 464.001 210.445 459.67 210.531L371.841 212.396C366.791 212.502 363.125 217.105 364.001 222.24L373.23 282.015C373.938 286.134 377.382 289.275 381.443 289.493Z"
fill="#153385" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M450.75 256.969L443.882 208.904L379.504 210.27C374.455 210.376 370.789 214.979 364.293 219.875L372.608 273.686L433.893 274.733C448.073 273.797 451.602 262.944 450.75 256.964V256.969Z"
fill="#0E2A78" />
<path
d="M105.27 218.929L210.171 217.499C216.387 217.52 221.177 211.854 220.187 205.651L208.218 130.372C207.398 125.248 202.875 121.623 197.75 121.979L93.8653 129.298C87.8887 129.718 83.5567 135.315 84.6 141.273L95.5577 210.664C96.3933 215.442 100.475 218.913 105.275 218.929H105.27Z"
fill="#ABB4C4" stroke="#153385" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M107.612 204.657L98.0695 146.929C97.6491 144.393 99.5223 142.06 102.088 141.917C104.28 141.794 106.212 143.341 106.579 145.504L116.239 203.233C116.68 205.864 114.653 208.256 111.986 208.256H111.87C109.762 208.256 107.958 206.73 107.612 204.652V204.657Z"
fill="#101C3F" />
<path
d="M102.577 174.642L99.331 155.029C98.9105 152.494 100.784 150.161 103.349 150.017C105.542 149.895 107.474 151.441 107.841 153.605L111.204 173.218C111.646 175.849 109.618 178.241 106.952 178.241H106.835C104.727 178.241 102.923 176.715 102.577 174.637V174.642Z"
fill="#0B64DD" />
<path
d="M123.929 203.44L114.387 145.711C113.966 143.176 115.839 140.843 118.405 140.699C120.598 140.577 122.529 142.124 122.897 144.287L132.556 202.015C132.998 204.647 130.97 207.038 128.303 207.038H128.187C126.079 207.038 124.275 205.513 123.929 203.435V203.44Z"
fill="#101C3F" />
<path
d="M123.056 198.136L119.81 178.522C119.389 175.987 121.263 173.654 123.828 173.51C126.02 173.388 127.952 174.934 128.32 177.098L131.683 196.711C132.125 199.342 130.097 201.734 127.431 201.734H127.314C125.206 201.734 123.402 200.208 123.056 198.13V198.136Z"
fill="#0B64DD" />
<path
d="M148.005 203.498L201.688 202.6C204.902 202.611 207.382 199.682 206.866 196.472L201.241 157.703C200.815 155.056 198.479 153.179 195.828 153.366L142.103 157.15C139.011 157.368 136.771 160.259 137.314 163.342L142.982 199.225C143.413 201.697 145.526 203.493 148.005 203.498Z"
stroke="#153385" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path d="M150.932 187.165L179.703 186.081" stroke="#153385" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M171.747 192.619L177.947 192.48" stroke="#153385" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M151.837 193.059L168.053 192.698" stroke="#153385" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M370.352 189.222L543.555 178.57C549.459 178.209 553.818 172.893 553.016 167.015L533.988 27.2885C533.224 21.6703 528.035 17.7583 522.43 18.5716L349.67 43.6487C344.092 44.4565 340.234 49.6551 341.059 55.2358L359.642 180.531C360.419 185.767 365.071 189.541 370.345 189.216L370.352 189.222Z"
fill="#153385" />
<path
d="M366.193 188.733L537.528 178.193C543.372 177.831 547.682 172.585 546.89 166.792L528.098 28.9363C527.341 23.3925 522.208 19.5337 516.666 20.3363L345.769 45.1106C340.25 45.9132 336.429 51.0368 337.248 56.5437L355.61 180.165C356.377 185.331 360.972 189.052 366.193 188.733Z"
fill="#ABB4C4" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path d="M505.699 41.1825L346.088 61.7788" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<path d="M508.834 64.4684L349.228 85.0647" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<path d="M511.973 87.749L352.369 108.351" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<path d="M515.111 111.035L355.054 130.739" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<path d="M518.246 134.321L358.196 150.437" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<path d="M521.388 157.602L361.781 172.83" stroke="#1C1E23" strokeWidth="3.51852" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.02 13.65" />
<g>
<path
d="M419.999 86.6541L346.891 94.5472L359.642 180.537C360.222 184.438 362.949 187.521 366.485 188.712L443.608 183.965L430.92 95.1267C430.164 89.8274 425.333 86.0802 420.006 86.6541H419.999Z"
fill="#C5CEDE" />
</g>
<path
d="M206.845 266.404L421.409 267.797C435.586 266.861 439.227 255.678 438.372 249.698L416.871 84.9107C416.114 79.6114 411.283 75.8642 405.956 76.4384L182.215 100.596C176.552 101.207 172.556 106.437 173.46 112.056L197.005 258.054C197.782 262.875 201.954 266.42 206.845 266.41V266.404Z"
fill="#101C3F" />
<path
d="M204.391 266.797L421.325 267.281C427.37 267.271 432.011 261.918 431.152 255.938L406.874 86.2023C406.118 80.9031 401.287 77.1558 395.96 77.7301L179.767 100.984C174.104 101.595 170.107 106.825 171.012 112.444L194.557 258.442C195.333 263.263 199.506 266.808 204.397 266.797H204.391Z"
fill="white" stroke="#153385" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M294.7 73.1215L423.352 57.6492C428.805 56.9955 432.595 51.9935 431.831 46.4554L418.162 -52.3439C417.363 -58.1217 411.741 -63.8569 401.97 -62.4002L277.85 -38.9176C272.608 -38.0088 269.101 -33.0816 269.883 -27.7186L283.342 64.2932C284.151 69.8317 289.234 73.7809 294.7 73.1272V73.1215Z"
fill="#101C3F" />
<path
d="M289.766 72.7176L418.419 57.2453C423.872 56.5912 427.662 51.5896 426.895 46.0511L413.229 -52.7479C412.43 -58.5256 407.001 -62.5652 401.361 -61.5871L272.922 -39.3219C267.68 -38.4127 264.173 -33.4856 264.955 -28.1226L278.414 63.8889C279.223 69.4274 284.305 73.3766 289.771 72.7229L289.766 72.7176Z"
fill="#153385" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M250.889 229.543L190.187 231.393L194.551 258.442C195.328 263.263 199.5 266.808 204.391 266.797L260.74 266.643L255.903 233.769C255.541 231.297 253.39 229.484 250.889 229.538V229.543Z"
fill="#C5CEDE" />
<path
d="M176.797 299.64L251.438 302.59C256.472 302.165 258.202 298.162 257.755 295.143L248.202 230.191C247.84 227.72 245.69 225.908 243.188 225.961L167.292 227.645C164.301 227.709 162.05 230.388 162.502 233.343L171.97 295.436C172.332 297.827 174.376 299.603 176.792 299.64H176.797Z"
fill="#101C3F" />
<path
d="M174.088 300.581L250.07 301.756C253.124 301.803 255.493 299.103 255.051 296.084L245.498 231.132C245.136 228.661 242.986 226.848 240.485 226.902L164.589 228.586C161.598 228.65 159.347 231.329 159.799 234.284L169.267 296.376C169.628 298.768 171.672 300.544 174.088 300.581Z"
fill="#0B64DD" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M177.457 250.767C181.151 251.936 185.158 255.083 186.797 259.239C186.952 259.633 187.468 259.648 187.521 259.26C188.053 255.598 190.443 251.771 194.109 250.676C194.455 250.575 194.386 250.017 194.008 249.9C190.315 248.71 186.276 245.499 184.679 241.29C184.53 240.896 184.014 240.881 183.955 241.263C183.338 245.335 180.709 248.923 177.345 249.985C177.005 250.092 177.079 250.644 177.452 250.761L177.457 250.767Z"
fill="white" />
<path d="M197.782 256.635L231.193 256.13" stroke="white" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M183.306 268.286L232.518 267.743" stroke="white" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M184.141 280.245L233.715 280.176" stroke="white" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M279.01 121.187L274.316 91.2039L179.756 100.984C174.093 101.595 170.097 106.825 171.608 112.444L176.685 144.59L264.385 139.429C273.699 138.855 280.484 130.388 279.01 121.187Z"
fill="#C5CEDE" />
<path
d="M162.912 137.664L262.725 131.499C268.665 131.132 272.991 125.731 272.055 119.864L262.257 58.6643C261.406 53.3492 256.52 49.6442 251.166 50.2557L151.566 61.6142C145.818 62.268 141.805 67.615 142.779 73.3077L152.364 129.314C153.231 134.38 157.782 137.978 162.918 137.664H162.912Z"
fill="#101C3F" />
<path
d="M159.33 138.903L259.143 132.737C265.083 132.37 269.409 126.97 268.473 121.102L258.675 59.9028C257.824 54.5874 252.938 50.8827 247.584 51.4939L147.984 62.8527C142.236 63.5065 138.224 68.8535 139.198 74.5462L148.782 130.553C149.65 135.618 154.2 139.216 159.336 138.903H159.33Z"
fill="#48EFCF" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M240.9 66.7488C242.581 76.0876 250.878 82.4234 259.431 80.9031C260.293 80.749 261.129 80.5203 261.927 80.2229L258.675 59.9028C257.824 54.5874 252.938 50.8827 247.584 51.4939L244.955 51.7916C241.554 55.5602 239.883 61.0509 240.905 66.7435L240.9 66.7488Z"
fill="#101C3F" />
<g>
<path
d="M496.604 149.841L390.506 157.968C386.509 158.277 383.575 161.949 384.176 165.898L387.449 187.42L507.258 180.048L503.718 155.518C503.218 152.037 500.126 149.565 496.607 149.836L496.604 149.841Z"
fill="#C5CEDE" />
</g>
<path
d="M387.083 243.044L387.632 245.898C388.1 248.338 390.26 250.086 392.744 250.033L429.562 249.241L428.587 242.081L387.076 243.044H387.083Z"
fill="#C5CEDE" />
<path
d="M392.287 241.641L496.635 240.003C503.071 239.679 505.724 234.274 505.14 230.202L493.261 147.875C492.761 144.393 489.669 141.922 486.15 142.193L380.053 150.32C376.056 150.628 373.125 154.301 373.726 158.25L385.538 235.996C386.045 239.323 388.919 241.725 392.287 241.646V241.641Z"
fill="#101C3F" />
<path
d="M388.195 242.741L494.496 240.147C498.62 240.046 501.772 236.32 501.185 232.249L489.169 148.97C488.669 145.488 485.577 143.017 482.058 143.288L375.961 151.415C371.964 151.723 369.029 155.396 369.631 159.345L381.446 237.091C381.953 240.418 384.827 242.82 388.195 242.741Z"
fill="#F04E98" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path d="M173.008 132.036L158.729 132.971" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M213.109 92.3946C211.618 92.5381 210.192 91.4857 209.932 90.0452L208.149 80.2173C207.334 75.7368 202.891 72.5209 198.234 73.042C193.583 73.5575 190.458 77.5651 191.267 81.9875L193.051 91.6877C193.311 93.112 192.311 94.3772 190.821 94.5208C189.33 94.6587 187.904 93.6225 187.643 92.2084L185.86 82.5508C184.525 75.3329 189.65 68.7631 197.287 67.8863C204.918 67.0039 212.215 72.2711 213.551 79.6487L215.333 89.5136C215.594 90.9593 214.594 92.251 213.103 92.3946H213.109Z"
fill="white" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M222.805 124.791L192.869 126.896C189.751 127.108 186.781 124.871 186.238 121.905L181.635 96.9018C181.092 93.9571 183.178 91.3264 186.291 91.0287L216.227 88.0893C219.346 87.7758 222.315 89.9869 222.858 93.0272L227.462 118.832C228.005 121.899 225.919 124.568 222.805 124.791Z"
fill="#48EFCF" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M208.766 103.737C208.266 100.973 205.387 98.9907 202.518 99.5702C200.293 100.017 198.803 101.973 199.006 104.173C199.197 106.209 200.688 107.888 202.588 108.547C202.811 108.627 202.971 108.824 202.992 109.042L203.668 116.536C203.694 116.85 203.998 117.1 204.322 117.073L208.133 116.781C208.457 116.754 208.665 116.467 208.58 116.159L206.557 108.744C206.499 108.526 206.579 108.308 206.77 108.197C208.266 107.304 209.107 105.597 208.766 103.732V103.737Z"
fill="#101C3F" />
<path
d="M254.626 79.5766C263.754 77.8458 269.686 68.7206 267.875 59.1945C266.064 49.6688 257.196 43.3496 248.069 45.0803C238.941 46.8111 233.009 55.9364 234.82 65.4624C236.631 74.9881 245.498 81.3074 254.626 79.5766Z"
fill="#101C3F" />
<path
d="M252.505 79.4242C261.037 77.8061 266.519 68.9383 264.747 59.6171C262.975 50.2962 254.621 44.0515 246.088 45.6693C237.555 47.2875 232.074 56.1552 233.846 65.4765C235.618 74.7974 243.972 81.042 252.505 79.4242Z"
fill="white" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path d="M241.948 65.5261L247.063 69.5337L254.79 56.2087" stroke="#101C3F" strokeWidth="2.11111"
strokeLinecap="round" strokeLinejoin="round" />
<path
d="M132.971 355.986L132.742 359.849C132.742 359.849 134.003 365.001 134.541 366.922C135.078 368.839 133.769 368.832 132.678 367.154C131.587 365.476 127.425 359.871 127.425 359.871L129.235 357.985L130.054 355.307L132.971 355.986Z"
fill="#0A52B3" />
<path
d="M140.012 294.102C140.012 294.102 141.268 309.744 139.788 325.664L132.396 325.743L129.586 295.425L140.012 294.102Z"
fill="#0A52B3" />
<path
d="M139.767 325.488L133.53 358.442L127.952 357.232L132.455 325.52C132.167 321.406 139.464 320.55 139.762 325.493L139.767 325.488Z"
fill="#0A52B3" />
<path
d="M137.941 300.985L140.581 304.748C140.581 304.748 140.735 313.47 139.98 325.079L138.096 334.264C138.474 330.899 138.809 327.635 138.548 325.908C138.548 325.908 137.814 324.207 137.702 323.76C137.431 322.676 137.92 321.098 138.298 318.615C138.676 316.133 137.941 300.985 137.941 300.985Z"
fill="#FF957D" />
<path
d="M144.551 291.194C144.551 291.194 156.249 309.542 158.468 318.163L151.592 320.922L133.934 295.569L144.551 291.194Z"
fill="#0A52B3" />
<path
d="M139.693 345.394L138.117 348.928C138.117 348.928 137.484 354.195 137.308 356.18C137.132 358.161 135.909 357.7 135.483 355.743C135.057 353.787 133.136 347.078 133.136 347.078L135.494 345.946L137.207 343.724L139.693 345.388V345.394Z"
fill="#0A52B3" />
<path
d="M158.511 318.86L140.645 347.386L135.291 343.873L150.326 319.264C152.454 314.868 158.186 314.82 158.511 318.86Z"
fill="#0A52B3" />
<path
d="M158.202 317.228C158.202 317.228 157.383 315.384 156.664 315.177C155.946 314.969 154.919 315.485 154.408 314.969C153.897 314.459 155.435 314.459 155.333 313.635C155.232 312.816 145.605 296.605 144.993 295.781C144.376 294.963 142.024 295.526 138.628 295.68L146.079 292.789C146.079 292.789 155.85 309.33 158.207 317.223L158.202 317.228Z"
fill="#FF957D" />
<path
d="M129.15 241.641C129.15 241.641 127.968 243.123 126.004 244.309C124.461 245.239 119.661 247.663 119.884 251.941C120.023 254.673 121.353 256.879 123.95 258.591C125.531 259.633 126.318 260.435 126.318 260.435L126.568 264.049L131.625 263.725L130.672 259.26C130.672 259.26 134.748 257.778 134.152 256.082C133.668 254.705 132.965 253.759 132.965 253.759C132.965 253.759 134.019 252.627 134.046 252.223C134.072 251.819 132.497 251.389 131.241 249.773L130.566 248.423C130.566 248.423 131.859 247.36 131.938 245.111C131.938 245.111 131.635 245.377 131.215 245.68C131.215 245.68 131.369 244.234 130.794 243.32C129.746 241.646 129.155 241.646 129.155 241.646L129.15 241.641Z"
fill="#0A52B3" />
<path
d="M130.672 259.25C130.672 259.25 129.368 259.723 128.666 259.85C128.666 259.85 129.517 261.068 130.72 263.252L131.614 263.284L130.677 259.25H130.672Z"
fill="#FF957D" />
<path
d="M115.419 304.775C114.445 301.819 114.967 291.747 115.254 287.415C115.499 283.774 117.883 270.561 119.538 269.455L124.227 278.964L120.47 287.49C119.538 290.7 118.612 304.206 118.612 304.206L115.424 304.775H115.419Z"
fill="#0A52B3" />
<path
d="M134.381 265.261L132.572 263.061C132.572 263.061 128.586 262.646 125.637 263.199L124.296 267.111L134.381 265.256V265.261Z"
fill="#0A52B3" />
<path
d="M119.533 269.46C119.533 269.46 123.716 266.425 127.351 265.601C130.986 264.778 136.824 265.187 138.91 265.931C138.91 265.931 142.263 282.195 143.12 284.433C143.977 286.671 146.068 292.799 146.068 292.799C146.068 292.799 141.518 297.726 128.532 296.78C128.532 296.78 126.255 286.007 125.637 284.061C125.02 282.116 119.251 272.825 119.533 269.46Z"
fill="#0A52B3" />
<path
d="M118.33 303.244C118.33 303.244 120.087 306.358 120.704 307.15C121.321 307.942 122.551 309.138 122.018 309.542C121.486 309.946 119.916 308.038 119.368 308.288C118.591 308.639 119.927 311.238 120.401 312.28C120.8 313.157 120.214 313.593 119.539 312.705C118.863 311.817 117.894 310.031 117.894 310.031C117.867 310.738 118.66 313.406 118.134 313.561C117.702 313.688 116.973 311.413 116.973 311.413C116.973 311.413 116.106 309.819 115.861 308.756C115.611 307.693 115.329 304.184 115.329 304.184C115.478 302.787 117.75 301.846 118.336 303.244H118.33Z"
fill="#0A52B3" />
<path
d="M152.364 300.592C149.841 298.763 144.285 290.339 142.061 286.613C139.836 282.886 134.934 271.687 134.934 271.687C132.949 267.271 139.144 262.567 142.284 269.003L146.329 283.631C147.452 286.777 154.615 298.274 154.615 298.274L152.359 300.597L152.364 300.592Z"
fill="#0A52B3" />
<path
d="M154.397 298.104C154.397 298.104 157.516 299.853 158.458 300.209C159.395 300.565 161.071 300.953 160.826 301.564C160.581 302.181 158.245 301.367 157.904 301.867C157.425 302.569 159.916 304.1 160.868 304.743C161.667 305.285 161.396 305.96 160.352 305.556C159.266 305.131 157.553 304.126 157.553 304.126C157.899 304.743 159.964 306.614 159.596 307.018C159.293 307.352 157.489 305.785 157.489 305.785C157.489 305.785 155.919 304.875 155.153 304.094C154.386 303.313 152.316 300.469 152.316 300.469C151.715 299.199 153.168 297.211 154.392 298.104H154.397Z"
fill="#0A52B3" />
<path
d="M132.966 253.749C132.966 253.749 134.12 252.611 134.046 252.212C133.971 251.814 133.742 251.787 133.056 251.351C132.348 250.9 131.651 250.049 131.241 249.762L130.565 248.412L130.4 248.582C130.4 248.582 129.575 250.347 130.278 251.298C130.752 251.941 131.64 251.979 131.912 252.409C131.912 252.409 132.268 253.993 132.848 254.923C133.146 255.402 134.008 255.731 134.008 255.731C134.008 255.731 133.519 254.418 132.971 253.754L132.966 253.749Z"
fill="#FF957D" />
<path
d="M131.614 263.284L132.822 265.059C132.822 265.059 131.561 265.362 129.144 265.671C129.144 265.671 133.258 265.782 135.621 266.176C137.984 266.569 139.73 267.722 140.517 269.21C141.305 270.699 142.007 274.366 142.444 276.072C142.88 277.779 141.002 279.447 141.31 280.101C141.619 280.755 142.859 278.895 143.125 279.166C143.934 280 145.419 284.55 146.34 286.214C147.26 287.872 152.518 294.697 152.518 294.697L146.334 283.631C146.334 283.631 142.572 269.928 142.29 269.003C142.007 268.078 140.784 266.027 138.421 265.676C136.058 265.325 134.269 265.112 134.269 265.112L132.838 263.369L131.608 263.284H131.614Z"
fill="#FF957D" />
<path
d="M249.66 183.402C269.808 188.207 291.49 203.02 300.053 223.733C300.867 225.7 303.683 225.61 304.034 223.611C307.36 204.769 320.888 184.497 341.149 177.783C343.07 177.146 342.74 174.345 340.681 173.845C320.521 168.923 298.691 153.791 290.372 132.817C289.595 130.861 286.748 130.935 286.392 132.918C282.56 153.892 267.744 172.995 249.149 179.474C247.265 180.133 247.627 182.923 249.66 183.407V183.402Z"
fill="#101C3F" />
<path
d="M323.284 177.821C303.124 172.899 281.293 157.766 272.975 136.793C272.512 135.634 271.331 135.193 270.362 135.437V135.405L261.884 138.669C261.884 138.669 265.03 168.944 231.751 183.455C229.921 184.252 230.229 186.904 232.262 187.388C247.632 191.056 272.241 213.433 272.241 213.433L277.211 232.179L285.364 229.054C285.987 228.863 286.503 228.379 286.642 227.598C289.968 208.755 303.496 188.483 323.757 181.77C325.678 181.132 325.348 178.331 323.289 177.831L323.284 177.821Z"
fill="url(#paint0_linear_736_26044)" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M224.152 190.482C244.301 195.287 265.982 210.1 274.545 230.813C275.359 232.78 278.175 232.689 278.526 230.691C281.852 211.849 295.381 191.576 315.641 184.863C317.563 184.225 317.232 181.424 315.173 180.925C295.013 176.003 273.183 160.871 264.865 139.897C264.088 137.941 261.24 138.015 260.884 139.998C257.052 160.972 242.236 180.074 223.641 186.554C221.757 187.213 222.119 190.003 224.152 190.487V190.482Z"
fill="white" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M316.599 184.741C316.599 184.741 320.404 182.785 323.752 181.765C323.752 181.765 326.424 180.749 324.119 178.108L323.284 177.826L314.364 180.616C314.364 180.616 316.195 181.02 316.668 182.243C317.142 183.46 316.599 184.746 316.599 184.746V184.741Z"
fill="#101C3F" />
<path
d="M453.326 169.221L419.527 171.389C413.968 171.746 409.686 176.752 409.957 182.572C410.228 188.393 414.949 192.82 420.505 192.464L421.42 192.406C421.969 192.368 422.437 192.81 422.461 193.384L422.792 200.453C422.831 201.272 423.816 201.59 424.298 200.947L430.983 192.145C431.163 191.911 431.43 191.762 431.712 191.747L454.304 190.296C459.86 189.939 464.145 184.932 463.874 179.112C463.6 173.292 458.882 168.865 453.326 169.221Z"
fill="#FF957D" />
<path
d="M442.25 196.945L476.214 195.43C481.798 195.18 486.541 199.677 486.801 205.476C487.065 211.275 482.755 216.175 477.171 216.425L476.256 216.468C475.704 216.494 475.278 216.978 475.306 217.552L475.623 224.595C475.662 225.408 474.708 225.833 474.159 225.248L466.679 217.249C466.475 217.036 466.2 216.919 465.912 216.93L443.214 217.94C437.63 218.19 432.89 213.693 432.626 207.894C432.363 202.095 436.673 197.195 442.257 196.945H442.25Z"
fill="white" />
<path
d="M432.51 181.132C432.616 182.307 431.916 183.263 430.934 183.274C429.963 183.28 429.083 182.334 428.978 181.159C428.872 179.984 429.572 179.027 430.554 179.017C431.525 179.011 432.405 179.957 432.51 181.132Z"
fill="white" />
<path
d="M438.914 180.904C439.02 182.078 438.316 183.035 437.338 183.046C436.363 183.051 435.487 182.105 435.378 180.93C435.272 179.755 435.976 178.799 436.954 178.788C437.929 178.783 438.805 179.729 438.914 180.904Z"
fill="white" />
<path
d="M445.314 180.67C445.423 181.844 444.72 182.801 443.741 182.812C442.767 182.817 441.887 181.871 441.782 180.696C441.676 179.522 442.376 178.565 443.358 178.554C444.333 178.549 445.209 179.495 445.314 180.67Z"
fill="white" />
<path
d="M453.952 207.214C454.058 208.389 453.358 209.345 452.376 209.356C451.405 209.361 450.525 208.415 450.42 207.24C450.314 206.066 451.014 205.109 451.996 205.098C452.967 205.093 453.847 206.039 453.952 207.214Z"
fill="#F04E98" />
<path
d="M460.356 206.985C460.461 208.16 459.758 209.117 458.78 209.127C457.805 209.133 456.929 208.186 456.82 207.012C456.714 205.837 457.418 204.881 458.396 204.87C459.371 204.865 460.25 205.811 460.356 206.985Z"
fill="#F04E98" />
<path
d="M466.763 206.751C466.869 207.926 466.168 208.883 465.187 208.893C464.212 208.899 463.336 207.953 463.231 206.778C463.121 205.603 463.825 204.647 464.803 204.636C465.778 204.631 466.658 205.577 466.763 206.751Z"
fill="#F04E98" />
<path d="M476.562 233.519L389.44 235.996" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M492.037 233.014L484.331 233.306" stroke="#101C3F" strokeWidth="0.703704" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M353.984 48.5865C352.693 48.7727 351.479 47.8691 351.277 46.5616L338.962 -32.0877C338.76 -33.3899 339.638 -34.5965 340.931 -34.7825C342.224 -34.9685 343.438 -34.0649 343.64 -32.7574L355.954 45.8917C356.159 47.1939 355.279 48.4008 353.984 48.5865Z"
fill="#0A52B3" />
<path
d="M364.374 35.1975L363.667 35.2985C362.569 35.4579 361.538 34.6873 361.366 33.5817L352.58 -22.5151C352.404 -23.6206 353.157 -24.6465 354.251 -24.8059L354.959 -24.9069C356.056 -25.0664 357.087 -24.2957 357.26 -23.1901L366.046 32.9067C366.221 34.0122 365.472 35.0381 364.374 35.1975Z"
fill="#0A52B3" />
<path
d="M374.384 23.9931C373.093 24.1792 371.879 23.2756 371.675 21.968L365.993 -14.3296C365.789 -15.6318 366.668 -16.8383 367.96 -17.0244C369.254 -17.2104 370.468 -16.3068 370.669 -14.9993L376.355 21.2983C376.555 22.6005 375.679 23.8071 374.384 23.9931Z"
fill="#0A52B3" />
<path
d="M384.775 12.8843C383.48 13.0703 382.266 12.1667 382.065 10.8592L379.324 -6.63847C379.12 -7.94071 380 -9.14725 381.291 -9.33327C382.586 -9.51933 383.8 -8.61574 384.001 -7.30819L386.742 10.1895C386.946 11.4917 386.066 12.6983 384.775 12.8843Z"
fill="#0A52B3" />
<path
d="M330.543 -21.3934L331.251 -21.4944C332.347 -21.6539 333.379 -20.8832 333.55 -19.7776L342.336 36.3192C342.512 37.4248 341.761 38.4504 340.665 38.6101L339.957 38.7111C338.861 38.8705 337.828 38.0996 337.658 36.9941L328.871 -19.1026C328.696 -20.2082 329.446 -21.234 330.543 -21.3934Z"
fill="#0A52B3" />
<path
d="M320.532 -10.1944C321.825 -10.3804 323.039 -9.47683 323.241 -8.16928L328.925 28.1283C329.127 29.4306 328.249 30.6371 326.956 30.8232C325.663 31.0092 324.449 30.1056 324.247 28.798L318.563 -7.49957C318.361 -8.80177 319.239 -10.0083 320.532 -10.1944Z"
fill="#0A52B3" />
<path
d="M310.144 0.914402C311.437 0.728371 312.65 1.63195 312.853 2.9395L315.593 20.4372C315.795 21.7394 314.917 22.946 313.624 23.132C312.331 23.318 311.118 22.4144 310.915 21.1069L308.174 3.60922C307.972 2.30699 308.85 1.10044 310.144 0.914402Z"
fill="#0A52B3" />
<path
d="M303.236 34.2409C301.942 34.4269 300.729 33.5233 300.527 32.2158L294.843 -4.08181C294.641 -5.38405 295.519 -6.59062 296.812 -6.77664C298.105 -6.96266 299.319 -6.05907 299.521 -4.75156L305.205 31.546C305.407 32.8483 304.529 34.0549 303.236 34.2409Z"
fill="#0A52B3" />
<path
d="M391.681 -20.442C392.973 -20.6281 394.187 -19.7245 394.391 -18.417L400.073 17.8806C400.277 19.1829 399.398 20.3894 398.106 20.5755C396.811 20.7615 395.598 19.8579 395.397 18.5504L389.711 -17.7472C389.511 -19.0495 390.387 -20.256 391.681 -20.442Z"
fill="#0A52B3" />
</g>
</g>
<defs>
<linearGradient id="paint0_linear_736_26044" x1="272.752" y1="148.709" x2="284.744" y2="229.755"
gradientUnits="userSpaceOnUse">
<stop stop-color="#48EFCF" />
<stop offset="0.1" stop-color="#3DD5D1" />
<stop offset="0.3" stop-color="#2BADD5" />
<stop offset="0.49" stop-color="#1D8DD8" />
<stop offset="0.67" stop-color="#1376DB" />
<stop offset="0.85" stop-color="#0D68DC" />
<stop offset="1" stop-color="#0B64DD" />
</linearGradient>
<clipPath id="clip0_736_26044">
<rect width="675.556" height="380" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -0,0 +1,269 @@
<svg width="100%" height="auto" viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_735_14633)">
<path
d="M1085.1 822.77L1335.48 829.855C1350.43 830.656 1361.94 817.468 1359.56 802.271L1332.57 619.997C1330.6 607.459 1319.74 598.108 1307.43 598.35L1057.81 603.652C1043.46 603.954 1033.04 617.036 1035.53 631.629L1061.76 801.516C1063.77 813.223 1073.56 822.151 1085.1 822.77Z"
fill="#F5F7FA" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M1282.08 730.335L1262.56 593.727L1079.59 597.61C1065.24 597.912 1054.82 610.994 1036.36 624.907L1059.99 777.844L1234.17 780.82C1274.47 778.161 1284.5 747.314 1282.08 730.319V730.335Z"
fill="#C5CEDE" />
<path
d="M300.189 622.218L598.328 618.154C615.995 618.215 629.608 602.111 626.794 584.482L592.777 370.53C590.448 355.968 577.591 345.665 563.025 346.677L267.775 367.479C250.789 368.672 238.477 384.579 241.442 401.514L272.585 598.728C274.96 612.308 286.561 622.173 300.204 622.218H300.189Z"
fill="#F5F7FA" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M306.844 581.657L279.724 417.587C278.529 410.381 283.853 403.749 291.144 403.341C297.376 402.994 302.866 407.39 303.91 413.538L331.363 577.609C332.618 585.087 326.855 591.884 319.277 591.884H318.945C312.955 591.884 307.827 587.549 306.844 581.642V581.657Z"
fill="#C5CEDE" />
<path
d="M292.536 496.352L283.309 440.609C282.114 433.403 287.438 426.771 294.729 426.364C300.96 426.016 306.451 430.412 307.495 436.56L317.054 492.303C318.309 499.781 312.547 506.579 304.969 506.579H304.636C298.646 506.579 293.519 502.243 292.536 496.336V496.352Z"
fill="#153385" />
<path
d="M353.219 578.198L326.099 414.127C324.904 406.922 330.228 400.29 337.519 399.882C343.751 399.535 349.241 403.931 350.285 410.079L377.738 574.149C378.993 581.627 373.23 588.425 365.652 588.425H365.32C359.33 588.425 354.202 584.089 353.219 578.183V578.198Z"
fill="#C5CEDE" />
<path
d="M350.739 563.122L341.512 507.379C340.317 500.173 345.641 493.542 352.932 493.134C359.163 492.786 364.654 497.182 365.698 503.331L375.257 559.073C376.512 566.551 370.75 573.349 363.172 573.349H362.839C356.849 573.349 351.722 569.013 350.739 563.107V563.122Z"
fill="#0B64DD" />
<path
d="M421.647 578.364L574.218 575.811C583.354 575.841 590.402 567.518 588.935 558.393L572.947 448.207C571.737 440.684 565.097 435.352 557.565 435.881L404.873 446.636C396.085 447.256 389.717 455.474 391.26 464.235L407.368 566.219C408.594 573.243 414.599 578.349 421.647 578.364Z"
stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M429.966 531.942L511.734 528.86" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M489.122 547.441L506.743 547.048" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M432.537 548.695L478.625 547.668" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M1053.58 537.788L1545.84 507.515C1562.62 506.488 1575.01 491.382 1572.73 474.674L1518.65 77.5568C1516.48 61.5894 1501.73 50.4711 1485.8 52.7823L994.798 124.054C978.946 126.35 967.98 141.125 970.325 156.986L1023.14 513.089C1025.35 527.969 1038.57 538.695 1053.56 537.773L1053.58 537.788Z"
fill="#101C3F" />
<path
d="M1041.76 536.399L1528.71 506.443C1545.32 505.415 1557.57 490.505 1555.32 474.039L1501.91 82.24C1499.76 66.484 1485.17 55.5167 1469.42 57.7978L983.711 128.209C968.026 130.49 957.166 145.052 959.495 160.702L1011.68 512.047C1013.86 526.731 1026.92 537.305 1041.76 536.399Z"
fill="#C5CEDE" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M1438.25 117.045L984.618 175.582" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<path d="M1447.16 183.226L993.543 241.763" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<path d="M1456.08 249.392L1002.47 307.944" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<path d="M1465 315.573L1010.1 371.573" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<path d="M1473.91 381.754L1019.03 427.557" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<path d="M1482.84 447.92L1029.22 491.2" stroke="#ABB4C4" strokeWidth="10" strokeLinecap="round"
strokeLinejoin="round" strokeDasharray="0.05 38.8" />
<g>
<path
d="M1194.68 246.28L986.902 268.713L1023.14 513.105C1024.79 524.193 1032.54 532.954 1042.59 536.338L1261.78 522.848L1225.72 270.36C1223.57 255.299 1209.84 244.649 1194.7 246.28H1194.68Z"
fill="#C5CEDE" />
</g>
<path
d="M588.875 757.148L1198.69 761.106C1238.98 758.448 1249.33 726.664 1246.89 709.669L1185.79 241.325C1183.64 226.264 1169.91 215.614 1154.77 217.246L518.874 285.904C502.78 287.642 491.421 302.506 493.992 318.474L560.907 733.416C563.116 747.118 574.974 757.194 588.875 757.164V757.148Z"
fill="#101C3F" />
<path
d="M581.902 758.266L1198.45 759.641C1215.63 759.611 1228.82 744.399 1226.38 727.404L1157.38 244.996C1155.23 229.935 1141.5 219.285 1126.36 220.917L511.916 287.007C495.822 288.744 484.463 303.609 487.035 319.576L553.95 734.519C556.158 748.221 568.016 758.297 581.917 758.266H581.902Z"
fill="white" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M838.567 207.819L1204.21 163.845C1219.71 161.987 1230.48 147.771 1228.31 132.031L1189.46 -148.767C1187.19 -165.188 1171.21 -181.488 1143.44 -177.348L790.679 -110.608C775.781 -108.025 765.813 -94.0216 768.036 -78.7792L806.289 182.728C808.588 198.468 823.033 209.693 838.567 207.834V207.819Z"
fill="#101C3F" />
<path
d="M824.545 206.671L1190.19 162.696C1205.69 160.838 1216.46 146.623 1214.28 130.882L1175.44 -149.915C1173.17 -166.336 1157.74 -177.817 1141.71 -175.037L776.673 -111.757C761.774 -109.173 751.807 -95.1696 754.03 -79.9273L792.282 181.579C794.581 197.32 809.026 208.544 824.56 206.686L824.545 206.671Z"
fill="#F5F7FA" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M714.053 652.386L541.532 657.643L553.935 734.519C556.143 748.221 568.001 758.297 581.902 758.266L742.051 757.828L728.302 664.395C727.273 657.371 721.162 652.219 714.053 652.37V652.386Z"
fill="#C5CEDE" />
<path
d="M503.476 851.609L715.611 859.993C729.92 858.784 734.836 847.409 733.565 838.829L706.415 654.228C705.386 647.204 699.276 642.053 692.167 642.204L476.462 646.993C467.961 647.174 461.563 654.787 462.849 663.187L489.757 839.659C490.786 846.457 496.594 851.503 503.461 851.609H503.476Z"
fill="#101C3F" />
<path
d="M495.777 854.282L711.724 857.621C720.406 857.757 727.137 850.083 725.882 841.502L698.731 656.902C697.703 649.878 691.592 644.726 684.483 644.878L468.778 649.666C460.277 649.848 453.879 657.461 455.165 665.86L482.073 842.333C483.102 849.131 488.91 854.177 495.777 854.282Z"
fill="#0B64DD" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M505.352 712.705C515.849 716.029 527.238 724.972 531.897 736.785C532.336 737.903 533.803 737.948 533.954 736.845C535.467 726.437 542.258 715.561 552.679 712.449C553.663 712.162 553.466 710.575 552.392 710.243C541.895 706.859 530.414 697.735 525.877 685.771C525.453 684.653 523.986 684.607 523.82 685.695C522.065 697.267 514.593 707.464 505.034 710.485C504.066 710.787 504.278 712.358 505.336 712.69L505.352 712.705Z"
fill="white" />
<path d="M563.116 729.383L658.074 727.948" stroke="white" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M521.974 762.496L661.84 760.955" stroke="white" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M524.349 796.486L665.243 796.289" stroke="white" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M793.976 344.427L780.636 259.211L511.886 287.007C495.792 288.744 484.433 303.609 488.729 319.577L503.158 410.94L752.412 396.272C778.881 394.64 798.166 370.576 793.976 344.427Z"
fill="#C5CEDE" />
<path
d="M464.014 391.256L747.693 373.733C764.573 372.691 776.87 357.343 774.208 340.665L746.361 166.73C743.941 151.624 730.056 141.094 714.84 142.832L431.766 175.114C415.43 176.972 404.026 192.169 406.794 208.348L434.035 367.524C436.5 381.921 449.432 392.148 464.029 391.256H464.014Z"
fill="#101C3F" />
<path
d="M453.834 394.776L737.513 377.253C754.393 376.21 766.69 360.862 764.028 344.185L736.182 170.25C733.762 155.143 719.877 144.614 704.66 146.351L421.586 178.634C405.251 180.492 393.846 195.689 396.614 211.868L423.855 371.044C426.321 385.44 439.253 395.667 453.849 394.776H453.834Z"
fill="#48EFCF" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M685.663 189.707C690.442 216.249 714.023 234.256 738.33 229.935C740.78 229.497 743.155 228.847 745.424 228.002L736.182 170.25C733.762 155.143 719.877 144.614 704.66 146.351L697.188 147.197C687.523 157.908 682.774 173.513 685.678 189.692L685.663 189.707Z"
fill="#101C3F" />
<g>
<path
d="M1412.4 425.865L1110.86 448.963C1099.5 449.839 1091.16 460.277 1092.87 471.502L1102.17 532.667L1442.68 511.715L1432.62 441.999C1431.2 432.104 1422.41 425.08 1412.41 425.85L1412.4 425.865Z"
fill="#C5CEDE" />
</g>
<path
d="M1101.13 690.756L1102.69 698.868C1104.02 705.802 1110.16 710.772 1117.22 710.621L1221.86 708.37L1219.09 688.021L1101.11 690.756H1101.13Z"
fill="#C5CEDE" />
<path
d="M1115.92 686.768L1412.49 682.115C1430.78 681.193 1438.32 665.83 1436.66 654.259L1402.9 420.276C1401.48 410.381 1392.69 403.357 1382.69 404.127L1081.15 427.225C1069.79 428.101 1061.46 438.539 1063.17 449.763L1096.74 670.725C1098.18 680.181 1106.35 687.009 1115.92 686.783V686.768Z"
fill="#101C3F" />
<path
d="M1104.29 689.895L1406.41 682.523C1418.13 682.236 1427.09 671.646 1425.42 660.075L1391.27 423.387C1389.85 413.493 1381.06 406.468 1371.06 407.239L1069.52 430.336C1058.16 431.213 1049.82 441.651 1051.53 452.875L1085.11 673.836C1086.55 683.293 1094.72 690.121 1104.29 689.895Z"
fill="#F04E98" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M492.707 375.259L452.125 377.917" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M606.677 262.595C602.442 263.003 598.388 260.012 597.647 255.918L592.58 227.986C590.266 215.252 577.636 206.112 564.401 207.593C551.182 209.058 542.303 220.448 544.602 233.017L549.669 260.586C550.41 264.634 547.567 268.23 543.332 268.638C539.096 269.03 535.043 266.085 534.302 262.066L529.235 234.618C525.438 214.104 540.004 195.432 561.709 192.939C583.399 190.432 604.136 205.402 607.933 226.37L613 254.407C613.741 258.516 610.897 262.187 606.662 262.595H606.677Z"
fill="white" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M634.236 354.669L549.155 360.651C540.291 361.255 531.851 354.895 530.309 346.466L517.225 275.405C515.682 267.036 521.611 259.559 530.46 258.713L615.541 250.359C624.404 249.468 632.844 255.752 634.387 264.393L647.471 337.734C649.014 346.451 643.084 354.034 634.236 354.669Z"
fill="#48EFCF" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M594.335 294.832C592.913 286.977 584.73 281.342 576.578 282.989C570.255 284.258 566.02 289.817 566.595 296.071C567.139 301.857 571.374 306.63 576.774 308.503C577.409 308.73 577.863 309.289 577.924 309.908L579.845 331.208C579.92 332.1 580.782 332.81 581.705 332.734L592.535 331.903C593.458 331.828 594.047 331.012 593.805 330.136L588.058 309.062C587.891 308.443 588.118 307.824 588.663 307.506C592.913 304.969 595.303 300.119 594.335 294.817V294.832Z"
fill="#101C3F" />
<path
d="M724.674 226.165C750.617 221.246 767.475 195.311 762.328 168.237C757.182 141.164 731.979 123.204 706.037 128.123C680.095 133.042 663.236 158.977 668.383 186.051C673.53 213.124 698.732 231.084 724.674 226.165Z"
fill="#101C3F" />
<path
d="M718.645 225.731C742.896 221.133 758.474 195.93 753.438 169.438C748.402 142.947 724.66 125.199 700.408 129.797C676.156 134.396 660.579 159.599 665.615 186.091C670.651 212.582 694.393 230.33 718.645 225.731Z"
fill="white" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M688.642 186.232L703.178 197.622L725.14 159.751" stroke="#101C3F" strokeWidth="6"
strokeLinecap="round" strokeLinejoin="round" />
<path
d="M378.917 1011.75L378.267 1022.73C378.267 1022.73 381.852 1037.37 383.379 1042.83C384.907 1048.28 381.186 1048.26 378.085 1043.49C374.985 1038.72 363.157 1022.79 363.157 1022.79L368.299 1017.43L370.628 1009.82L378.917 1011.75Z"
fill="#101C3F" />
<path
d="M398.928 835.868C398.928 835.868 402.498 880.326 398.293 925.57L377.284 925.796L369.298 839.629L398.928 835.868Z"
fill="#101C3F" />
<path
d="M398.233 925.071L380.505 1018.73L364.654 1015.29L377.45 925.162C376.633 913.469 397.371 911.037 398.218 925.086L398.233 925.071Z"
fill="#101C3F" />
<path
d="M393.045 855.43L400.547 866.126C400.547 866.126 400.985 890.915 398.838 923.908L393.483 950.012C394.557 940.449 395.51 931.174 394.769 926.264C394.769 926.264 392.682 921.43 392.364 920.161C391.593 917.08 392.984 912.593 394.058 905.538C395.132 898.484 393.045 855.43 393.045 855.43Z"
fill="#FF957D" />
<path
d="M411.831 827.605C411.831 827.605 445.076 879.752 451.384 904.254L431.842 912.095L381.655 840.037L411.831 827.605Z"
fill="#101C3F" />
<path
d="M398.021 981.644L393.544 991.69C393.544 991.69 391.744 1006.66 391.245 1012.3C390.745 1017.93 387.267 1016.62 386.057 1011.06C384.847 1005.5 379.386 986.433 379.386 986.433L386.087 983.216L390.957 976.901L398.021 981.629V981.644Z"
fill="#101C3F" />
<path
d="M451.505 906.234L400.728 987.31L385.512 977.324L428.242 907.382C434.292 894.889 450.582 894.753 451.505 906.234Z"
fill="#101C3F" />
<path
d="M450.627 901.596C450.627 901.596 448.298 896.354 446.256 895.764C444.214 895.175 441.295 896.641 439.843 895.175C438.391 893.725 442.762 893.725 442.475 891.384C442.187 889.057 414.825 842.983 413.086 840.641C411.331 838.315 404.646 839.916 394.996 840.354L416.172 832.136C416.172 832.136 443.942 879.148 450.642 901.58L450.627 901.596Z"
fill="#FF957D" />
<path
d="M368.057 686.768C368.057 686.768 364.699 690.982 359.118 694.351C354.732 696.995 341.088 703.883 341.724 716.044C342.117 723.809 345.898 730.078 353.28 734.942C357.772 737.903 360.011 740.184 360.011 740.184L360.721 750.456L375.091 749.535L372.383 736.845C372.383 736.845 383.969 732.631 382.275 727.812C380.899 723.899 378.902 721.21 378.902 721.21C378.902 721.21 381.897 717.993 381.973 716.844C382.048 715.696 377.571 714.473 374.002 709.88L372.081 706.043C372.081 706.043 375.756 703.022 375.983 696.632C375.983 696.632 375.121 697.387 373.926 698.248C373.926 698.248 374.365 694.14 372.731 691.541C369.751 686.783 368.072 686.783 368.072 686.783L368.057 686.768Z"
fill="#101C3F" />
<path
d="M372.383 736.815C372.383 736.815 368.677 738.16 366.681 738.522C366.681 738.522 369.101 741.982 372.519 748.19L375.06 748.281L372.398 736.815H372.383Z"
fill="#FF957D" />
<path
d="M329.033 866.201C326.265 857.802 327.748 829.176 328.565 816.864C329.26 806.516 336.037 768.962 340.741 765.82L354.066 792.845L343.388 817.076C340.741 826.2 338.109 864.585 338.109 864.585L329.049 866.201H329.033Z"
fill="#101C3F" />
<path
d="M382.926 753.9L377.783 747.646C377.783 747.646 366.454 746.468 358.074 748.039L354.263 759.157L382.926 753.885V753.9Z"
fill="#101C3F" />
<path
d="M340.726 765.835C340.726 765.835 352.614 757.209 362.945 754.867C373.276 752.526 389.868 753.689 395.797 755.804C395.797 755.804 405.327 802.03 407.762 808.389C410.197 814.749 416.141 832.167 416.141 832.167C416.141 832.167 403.209 846.17 366.303 843.481C366.303 843.481 359.829 812.861 358.074 807.332C356.32 801.803 339.924 775.397 340.726 765.835Z"
fill="#101C3F" />
<path
d="M337.307 861.851C337.307 861.851 342.299 870.703 344.053 872.954C345.808 875.205 349.302 878.604 347.789 879.752C346.277 880.9 341.815 875.477 340.257 876.187C338.048 877.184 341.845 884.571 343.191 887.532C344.325 890.024 342.662 891.263 340.741 888.74C338.82 886.217 336.067 881.142 336.067 881.142C335.991 883.151 338.245 890.734 336.748 891.172C335.522 891.535 333.45 885.069 333.45 885.069C333.45 885.069 330.985 880.537 330.289 877.516C329.578 874.495 328.776 864.525 328.776 864.525C329.2 860.552 335.659 857.878 337.322 861.851H337.307Z"
fill="#101C3F" />
<path
d="M434.035 854.313C426.865 849.116 411.074 825.173 404.752 814.583C398.429 803.993 384.499 772.164 384.499 772.164C378.857 759.611 396.463 746.242 405.387 764.535L416.882 806.108C420.074 815.051 440.433 847.726 440.433 847.726L434.02 854.328L434.035 854.313Z"
fill="#101C3F" />
<path
d="M439.813 847.243C439.813 847.243 448.676 852.213 451.353 853.225C454.016 854.237 458.78 855.34 458.084 857.077C457.389 858.83 450.748 856.518 449.78 857.938C448.419 859.932 455.498 864.283 458.205 866.111C460.474 867.652 459.703 869.57 456.738 868.422C453.652 867.214 448.782 864.358 448.782 864.358C449.765 866.111 455.634 871.428 454.59 872.576C453.728 873.528 448.601 869.072 448.601 869.072C448.601 869.072 444.139 866.488 441.96 864.268C439.782 862.047 433.899 853.965 433.899 853.965C432.189 850.355 436.319 844.705 439.798 847.243H439.813Z"
fill="#101C3F" />
<path
d="M378.902 721.18C378.902 721.18 382.184 717.947 381.973 716.814C381.761 715.681 381.11 715.606 379.159 714.367C377.148 713.083 375.166 710.666 374.002 709.85L372.081 706.013L371.612 706.496C371.612 706.496 369.267 711.512 371.264 714.216C372.61 716.044 375.136 716.149 375.907 717.373C375.907 717.373 376.921 721.875 378.569 724.518C379.416 725.878 381.867 726.815 381.867 726.815C381.867 726.815 380.475 723.083 378.917 721.195L378.902 721.18Z"
fill="#FF957D" />
<path
d="M375.06 748.281L378.494 753.326C378.494 753.326 374.909 754.187 368.042 755.064C368.042 755.064 379.734 755.381 386.45 756.499C393.166 757.617 398.127 760.895 400.365 765.124C402.604 769.354 404.6 779.778 405.841 784.627C407.081 789.476 401.742 794.219 402.619 796.077C403.496 797.936 407.021 792.648 407.777 793.419C410.076 795.79 414.296 808.722 416.913 813.45C419.529 818.163 434.473 837.56 434.473 837.56L416.898 806.108C416.898 806.108 406.204 767.164 405.402 764.535C404.601 761.907 401.122 756.076 394.406 755.079C387.69 754.082 382.608 753.477 382.608 753.477L378.539 748.523L375.045 748.281H375.06Z"
fill="#FF957D" />
<path
d="M710.559 521.247C767.825 534.903 829.446 577.005 853.783 635.874C856.097 641.464 864.098 641.207 865.097 635.527C874.55 581.975 912.999 524.359 970.582 505.279C976.042 503.467 975.104 495.506 969.251 494.086C911.955 480.097 849.911 437.089 826.27 377.479C824.061 371.92 815.969 372.132 814.955 377.766C804.065 437.376 761.956 491.669 709.107 510.083C703.753 511.956 704.781 519.887 710.559 521.262V521.247Z"
fill="#101C3F" />
<path
d="M919.806 505.385C862.51 491.397 800.465 448.389 776.824 388.779C775.508 385.486 772.15 384.232 769.397 384.927V384.836L745.303 394.111C745.303 394.111 754.242 480.157 659.662 521.398C654.459 523.664 655.336 531.202 661.114 532.577C704.796 543 774.737 606.598 774.737 606.598L788.864 659.878L812.036 650.996C813.806 650.452 815.273 649.077 815.666 646.857C825.12 593.304 863.569 535.688 921.152 516.609C926.612 514.796 925.674 506.835 919.821 505.415L919.806 505.385Z"
fill="url(#paint0_linear_735_14633)" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M638.063 541.369C695.328 555.025 756.949 597.126 781.286 655.996C783.6 661.585 791.602 661.328 792.6 655.648C802.054 602.096 840.503 544.48 898.085 525.401C903.546 523.588 902.608 515.627 896.754 514.207C839.459 500.219 777.414 457.211 753.773 397.601C751.565 392.042 743.472 392.253 742.459 397.888C731.569 457.498 689.459 511.79 636.611 530.205C631.256 532.078 632.285 540.009 638.063 541.384V541.369Z"
fill="white" stroke="#101C3F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M900.808 525.054C900.808 525.054 911.623 519.495 921.137 516.594C921.137 516.594 928.73 513.709 922.18 506.201L919.806 505.4L894.455 513.331C894.455 513.331 899.658 514.479 901.005 517.954C902.351 521.413 900.808 525.069 900.808 525.069V525.054Z"
fill="#101C3F" />
<path
d="M1289.4 480.943L1193.34 487.106C1177.54 488.119 1165.37 502.349 1166.14 518.89C1166.91 535.432 1180.33 548.015 1196.12 547.003L1198.72 546.837C1200.28 546.731 1201.61 547.985 1201.68 549.617L1202.62 569.708C1202.73 572.035 1205.53 572.941 1206.9 571.113L1225.9 546.097C1226.41 545.432 1227.17 545.009 1227.97 544.964L1292.18 540.84C1307.97 539.828 1320.15 525.598 1319.38 509.056C1318.6 492.515 1305.19 479.931 1289.4 480.943Z"
fill="#FF957D" />
<path
d="M1257.92 559.738L1354.45 555.433C1370.32 554.723 1383.8 567.503 1384.54 583.984C1385.29 600.465 1373.04 614.393 1357.17 615.103L1354.57 615.224C1353 615.299 1351.79 616.674 1351.87 618.306L1352.77 638.322C1352.88 640.633 1350.17 641.841 1348.61 640.18L1327.35 617.444C1326.77 616.84 1325.99 616.508 1325.17 616.538L1260.66 619.408C1244.79 620.118 1231.32 607.338 1230.57 590.857C1229.82 574.376 1242.07 560.448 1257.94 559.738H1257.92Z"
fill="white" />
<path
d="M1230.24 514.797C1230.54 518.135 1228.55 520.854 1225.76 520.884C1223 520.9 1220.5 518.211 1220.2 514.872C1219.9 511.534 1221.89 508.814 1224.68 508.784C1227.44 508.769 1229.94 511.458 1230.24 514.797Z"
fill="white" />
<path
d="M1248.44 514.147C1248.74 517.485 1246.74 520.205 1243.96 520.235C1241.19 520.25 1238.7 517.561 1238.39 514.223C1238.09 510.884 1240.09 508.165 1242.87 508.135C1245.64 508.12 1248.14 510.808 1248.44 514.147Z"
fill="white" />
<path
d="M1266.63 513.482C1266.94 516.821 1264.94 519.54 1262.16 519.57C1259.39 519.585 1256.89 516.896 1256.59 513.558C1256.29 510.219 1258.28 507.5 1261.07 507.47C1263.84 507.455 1266.33 510.144 1266.63 513.482Z"
fill="white" />
<path
d="M1291.18 588.924C1291.48 592.262 1289.49 594.981 1286.7 595.011C1283.94 595.027 1281.44 592.338 1281.14 588.999C1280.84 585.661 1282.83 582.941 1285.62 582.911C1288.38 582.896 1290.88 585.585 1291.18 588.924Z"
fill="#F04E98" />
<path
d="M1309.38 588.274C1309.68 591.613 1307.68 594.332 1304.9 594.362C1302.13 594.377 1299.64 591.688 1299.33 588.35C1299.03 585.011 1301.03 582.292 1303.81 582.262C1306.58 582.247 1309.08 584.936 1309.38 588.274Z"
fill="#F04E98" />
<path
d="M1327.59 587.609C1327.89 590.948 1325.9 593.667 1323.11 593.697C1320.34 593.712 1317.85 591.023 1317.55 587.685C1317.24 584.346 1319.24 581.627 1322.02 581.597C1324.79 581.582 1327.29 584.271 1327.59 587.609Z"
fill="#F04E98" />
<path d="M1355.44 663.685L1107.83 670.725" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M1399.42 662.25L1377.52 663.081" stroke="#101C3F" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round" />
<path
d="M1007.06 138.088C1003.39 138.617 999.94 136.049 999.366 132.333L964.365 -91.1967C963.791 -94.8978 966.286 -98.3269 969.962 -98.8556C973.637 -99.3844 977.086 -96.8163 977.661 -93.1001L1012.66 130.429C1013.24 134.13 1010.74 137.559 1007.06 138.088Z"
fill="#C5CEDE" />
<path
d="M1036.59 100.035L1034.58 100.322C1031.46 100.775 1028.53 98.5849 1028.04 95.4428L1003.07 -63.9901C1002.57 -67.1322 1004.7 -70.0478 1007.82 -70.501L1009.83 -70.788C1012.95 -71.2412 1015.88 -69.0508 1016.37 -65.9086L1041.34 93.5243C1041.84 96.6665 1039.71 99.582 1036.59 100.035Z"
fill="#C5CEDE" />
<path
d="M1065.04 68.191C1061.37 68.7197 1057.92 66.1516 1057.34 62.4354L1041.19 -40.7261C1040.61 -44.4272 1043.11 -47.8564 1046.78 -48.3851C1050.46 -48.9138 1053.91 -46.3457 1054.48 -42.6296L1070.64 60.532C1071.21 64.2331 1068.72 67.6623 1065.04 68.191Z"
fill="#C5CEDE" />
<path
d="M1094.57 36.6184C1090.89 37.1472 1087.44 34.5791 1086.87 30.8629L1079.08 -18.8674C1078.5 -22.5684 1081 -25.9976 1084.67 -26.5263C1088.35 -27.055 1091.8 -24.4869 1092.37 -20.7708L1100.16 28.9595C1100.74 32.6606 1098.24 36.0897 1094.57 36.6184Z"
fill="#C5CEDE" />
<path
d="M940.437 -60.8024L942.448 -61.0895C945.564 -61.5427 948.499 -59.3522 948.983 -56.2101L973.955 103.223C974.454 106.365 972.321 109.28 969.206 109.733L967.194 110.021C964.078 110.474 961.144 108.283 960.66 105.141L935.687 -54.2916C935.188 -57.4337 937.321 -60.3493 940.437 -60.8024Z"
fill="#C5CEDE" />
<path
d="M911.986 -28.9735C915.661 -29.5022 919.11 -26.9341 919.685 -23.2179L935.839 79.9436C936.413 83.6447 933.918 87.0739 930.242 87.6026C926.567 88.1313 923.118 85.5632 922.543 81.847L906.389 -21.3145C905.815 -25.0156 908.31 -28.4448 911.986 -28.9735Z"
fill="#C5CEDE" />
<path
d="M882.461 2.59875C886.136 2.07003 889.585 4.63811 890.16 8.35428L897.949 58.0845C898.524 61.7856 896.028 65.2147 892.353 65.7435C888.677 66.2722 885.229 63.7041 884.654 59.9879L876.864 10.2577C876.29 6.55663 878.785 3.12747 882.461 2.59875Z"
fill="#C5CEDE" />
<path
d="M862.828 97.3161C859.152 97.8448 855.704 95.2768 855.129 91.5606L838.975 -11.601C838.4 -15.3021 840.896 -18.7312 844.571 -19.2599C848.247 -19.7887 851.695 -17.2206 852.27 -13.5044L868.424 89.6572C868.999 93.3582 866.503 96.7874 862.828 97.3161Z"
fill="#C5CEDE" />
<path
d="M1114.2 -58.0985C1117.87 -58.6272 1121.32 -56.0591 1121.9 -52.343L1138.05 50.8186C1138.63 54.5197 1136.13 57.9488 1132.46 58.4776C1128.78 59.0063 1125.33 56.4382 1124.76 52.722L1108.6 -50.4396C1108.03 -54.1406 1110.52 -57.5698 1114.2 -58.0985Z"
fill="#C5CEDE" />
</g>
<defs>
<linearGradient id="paint0_linear_735_14633" x1="776.189" y1="422.647" x2="810.273" y2="652.987"
gradientUnits="userSpaceOnUse">
<stop stop-color="#48EFCF" />
<stop offset="0.1" stop-color="#3DD5D1" />
<stop offset="0.3" stop-color="#2BADD5" />
<stop offset="0.49" stop-color="#1D8DD8" />
<stop offset="0.67" stop-color="#1376DB" />
<stop offset="0.85" stop-color="#0D68DC" />
<stop offset="1" stop-color="#0B64DD" />
</linearGradient>
<clipPath id="clip0_735_14633">
<rect width="1920" height="1080" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,54 @@
<svg width="117" height="107" viewBox="0 0 117 107" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_770_1602" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="117" height="107">
<path d="M116.999 0H0V106.112H116.999V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_770_1602)">
<path d="M100.759 7.06494H11.5479C9.74073 7.06494 8.2757 8.52997 8.2757 10.3372V34.0735C8.2757 35.8806 9.74073 37.3457 11.5479 37.3457H100.759C102.566 37.3457 104.031 35.8806 104.031 34.0735V10.3372C104.031 8.52997 102.566 7.06494 100.759 7.06494Z" fill="#101C3F"/>
<mask id="mask1_770_1602" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="8" y="7" width="97" height="31">
<path d="M100.759 7.06494H11.5479C9.74073 7.06494 8.2757 8.52997 8.2757 10.3372V34.0735C8.2757 35.8806 9.74073 37.3457 11.5479 37.3457H100.759C102.566 37.3457 104.031 35.8806 104.031 34.0735V10.3372C104.031 8.52997 102.566 7.06494 100.759 7.06494Z" fill="white"/>
</mask>
<g mask="url(#mask1_770_1602)">
<path d="M42.469 0.650513H1.07074V42.0488H42.469V0.650513Z" fill="#101C3F"/>
</g>
<path d="M100.759 7.06494H11.5479C9.74073 7.06494 8.2757 8.52997 8.2757 10.3372V34.0735C8.2757 35.8807 9.74073 37.3457 11.5479 37.3457H100.759C102.566 37.3457 104.031 35.8807 104.031 34.0735V10.3372C104.031 8.52997 102.566 7.06494 100.759 7.06494Z" stroke="#101C3F" stroke-width="1.19" stroke-miterlimit="10"/>
<path d="M100.759 47.9329H11.5479C9.74073 47.9329 8.2757 49.3979 8.2757 51.2051V74.9414C8.2757 76.7486 9.74073 78.2136 11.5479 78.2136H100.759C102.566 78.2136 104.031 76.7486 104.031 74.9414V51.2051C104.031 49.3979 102.566 47.9329 100.759 47.9329Z" fill="#101C3F"/>
<mask id="mask2_770_1602" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="8" y="47" width="97" height="32">
<path d="M100.759 47.9329H11.5479C9.74073 47.9329 8.2757 49.3979 8.2757 51.2051V74.9414C8.2757 76.7486 9.74073 78.2136 11.5479 78.2136H100.759C102.566 78.2136 104.031 76.7486 104.031 74.9414V51.2051C104.031 49.3979 102.566 47.9329 100.759 47.9329Z" fill="white"/>
</mask>
<g mask="url(#mask2_770_1602)">
<path d="M42.469 41.5184H1.07074V82.9167H42.469V41.5184Z" fill="#101C3F"/>
</g>
<path d="M100.759 47.9329H11.5479C9.74073 47.9329 8.2757 49.3979 8.2757 51.2051V74.9414C8.2757 76.7486 9.74073 78.2136 11.5479 78.2136H100.759C102.566 78.2136 104.031 76.7486 104.031 74.9414V51.2051C104.031 49.3979 102.566 47.9329 100.759 47.9329Z" stroke="#101C3F" stroke-width="1.19" stroke-miterlimit="10"/>
<path d="M99.6882 47.2222H10.4772C8.66999 47.2222 7.20496 48.6873 7.20496 50.4945V74.2307C7.20496 76.0379 8.66999 77.5029 10.4772 77.5029H99.6882C101.495 77.5029 102.96 76.0379 102.96 74.2307V50.4945C102.96 48.6873 101.495 47.2222 99.6882 47.2222Z" fill="white"/>
<mask id="mask3_770_1602" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="7" y="47" width="96" height="31">
<path d="M99.6882 47.2222H10.4772C8.66999 47.2222 7.20496 48.6873 7.20496 50.4945V74.2307C7.20496 76.0379 8.66999 77.5029 10.4772 77.5029H99.6882C101.495 77.5029 102.96 76.0379 102.96 74.2307V50.4945C102.96 48.6873 101.495 47.2222 99.6882 47.2222Z" fill="white"/>
</mask>
<g mask="url(#mask3_770_1602)">
<path d="M41.3983 40.8077H0V82.206H41.3983V40.8077Z" fill="#0B64DD"/>
</g>
<path d="M99.6882 47.2222H10.4772C8.66999 47.2222 7.20496 48.6873 7.20496 50.4945V74.2307C7.20496 76.0379 8.66999 77.5029 10.4772 77.5029H99.6882C101.495 77.5029 102.96 76.0379 102.96 74.2307V50.4945C102.96 48.6873 101.495 47.2222 99.6882 47.2222Z" stroke="#101C3F" stroke-width="1.19" stroke-miterlimit="10"/>
<path d="M48.4832 54.3972H90.5219" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M48.4832 62.3625H84.5378" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M48.4832 70.6182H72.0893" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.6882 6.41443H10.4772C8.66999 6.41443 7.20496 7.87946 7.20496 9.68667V33.4229C7.20496 35.2301 8.66999 36.6951 10.4772 36.6951H99.6882C101.495 36.6951 102.96 35.2301 102.96 33.4229V9.68667C102.96 7.87946 101.495 6.41443 99.6882 6.41443Z" fill="white"/>
<mask id="mask4_770_1602" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="7" y="6" width="96" height="31">
<path d="M99.6882 6.41443H10.4772C8.66999 6.41443 7.20496 7.87946 7.20496 9.68667V33.4229C7.20496 35.2301 8.66999 36.6951 10.4772 36.6951H99.6882C101.495 36.6951 102.96 35.2301 102.96 33.4229V9.68667C102.96 7.87946 101.495 6.41443 99.6882 6.41443Z" fill="white"/>
</mask>
<g mask="url(#mask4_770_1602)">
<path d="M41.3983 0H0V41.3983H41.3983V0Z" fill="#0B64DD"/>
</g>
<path d="M99.6882 6.41443H10.4772C8.66999 6.41443 7.20496 7.87946 7.20496 9.68667V33.4229C7.20496 35.2301 8.66999 36.6951 10.4772 36.6951H99.6882C101.495 36.6951 102.96 35.2301 102.96 33.4229V9.68667C102.96 7.87946 101.495 6.41443 99.6882 6.41443Z" stroke="#101C3F" stroke-width="1.19" stroke-miterlimit="10"/>
<path d="M88.7407 81.9161C100.759 81.9161 110.496 72.1594 110.496 60.1312C110.496 48.103 100.759 38.3463 88.7407 38.3463C76.7225 38.3463 66.9858 48.103 66.9858 60.1312C66.9858 72.1594 76.7225 81.9161 88.7407 81.9161Z" fill="#48EFCF" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M77.4129 61.2318L86.7693 68.5769L102.06 47.9928" stroke="#101C3F" stroke-width="5.41" stroke-miterlimit="10"/>
<path d="M99.7482 43.0094C91.3625 37.7258 82.1562 39.497 76.2121 44.9007C92.3131 36.635 110.896 54.3972 105.132 71.0986C104.191 73.8204 102.66 76.1821 100.739 78.0934C104.481 76.082 107.544 72.6395 109.155 67.5562C112.057 58.3699 107.794 48.0929 99.7582 43.0194L99.7482 43.0094Z" fill="#101C3F" stroke="#101C3F" stroke-width="0.71" stroke-miterlimit="10"/>
<path d="M79.9747 34.3935C55.3979 42.7693 60.2412 77.0727 81.926 85.4284C95.085 90.4919 110.506 84.2576 115.229 70.6383C122.384 49.8241 101.249 27.1486 79.9747 34.4035V34.3935ZM108.985 68.0365C105.902 76.9526 96.566 81.9661 87.3998 80.1748C70.0379 76.7925 62.5828 51.4152 79.4243 42.0788C95.6854 33.0626 114.829 51.105 108.985 68.0265V68.0365Z" fill="#0B64DD" stroke="#101C3F" stroke-width="0.71" stroke-miterlimit="10"/>
<path d="M81.926 85.4185C65.0645 78.9241 58.3899 56.7689 67.7863 43.2797C60.9216 49.4339 57.2992 59.2006 60.8316 70.098C67.1259 89.5413 93.634 96.7763 106.383 82.4165C99.528 87.2998 90.2717 88.6407 81.926 85.4285V85.4185Z" fill="#153385" stroke="#101C3F" stroke-width="0.71" stroke-miterlimit="10"/>
<path d="M70.6837 79.1209L70.1921 78.5406C68.5131 76.5586 65.5453 76.3131 63.5634 77.9921L41.2607 96.8861C39.2788 98.5651 39.0332 101.533 40.7123 103.515L41.2038 104.095C42.8829 106.077 45.8506 106.322 47.8325 104.643L70.1352 85.7496C72.1171 84.0705 72.3627 81.1028 70.6837 79.1209Z" fill="#153385" stroke="#101C3F" stroke-width="0.71" stroke-miterlimit="10"/>
<path d="M69.5373 77.7634L69.0457 77.1831C67.3667 75.2012 64.3989 74.9556 62.417 76.6346L40.1143 95.5286C38.1324 97.2076 37.8868 100.175 39.5658 102.157L40.0574 102.738C41.7364 104.72 44.7042 104.965 46.6861 103.286L68.9888 84.3921C70.9707 82.7131 71.2163 79.7453 69.5373 77.7634Z" fill="#0B64DD" stroke="#101C3F" stroke-width="0.71" stroke-miterlimit="10"/>
<path d="M28.9598 13.5994H19.013L14.0396 22.2052L19.013 30.8211H28.9598L33.9232 22.2052L28.9598 13.5994Z" fill="#48EFCF"/>
<path d="M48.4832 13.5994H90.5219" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M48.4832 21.5548H84.5378" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M48.4832 29.8204H72.0893" stroke="#101C3F" stroke-width="0.9" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M23.9864 71.749C28.778 71.749 32.6623 67.8647 32.6623 63.0731C32.6623 58.2815 28.778 54.3972 23.9864 54.3972C19.1948 54.3972 15.3105 58.2815 15.3105 63.0731C15.3105 67.8647 19.1948 71.749 23.9864 71.749Z" fill="#48EFCF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -0,0 +1,15 @@
<svg width="100" height="116" viewBox="0 0 100 116" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_825_4410" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="116">
<path d="M99.8473 0H0V116H99.8473V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_825_4410)">
<path d="M69.5018 11.7326C56.3328 6.88087 44.118 12.054 37.4783 21.2954C57.3373 5.11295 88.2961 24.2186 85.2424 49.0298C84.7401 53.0779 83.3238 56.7946 81.2143 60.009C85.7949 56.1216 89.0293 50.4461 89.7526 42.9324C91.0685 29.3616 82.1284 16.3834 69.5018 11.7326Z" fill="#101C3F" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M39.5776 5.63528C8.02615 24.4898 24.8415 70.5462 57.3372 75.7094C77.0455 78.8434 96.563 65.6644 99.0743 45.4136C102.841 14.4849 66.9 -10.6779 39.5776 5.64533V5.63528ZM89.6721 43.6557C88.0448 56.9051 76.5834 66.5985 63.3742 66.8296C38.342 67.2615 20.5322 34.3339 41.0944 16.4437C60.9534 -0.84376 92.776 18.4929 89.6721 43.6557Z" fill="#0B64DD" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M57.337 75.6994C32.0838 71.6914 16.303 42.9727 25.3335 21.5266C17.6491 32.0738 15.5095 46.6592 23.6158 60.7122C38.0706 85.7746 76.9047 87.9644 90.3048 64.3284C82.2487 73.1078 69.833 77.6984 57.3269 75.7094L57.337 75.6994Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M40.9493 71.1058L38.0165 68.8391C35.6813 67.0343 32.3252 67.4642 30.5203 69.7994L3.60862 104.619C1.80377 106.954 2.23371 110.31 4.56891 112.115L7.50168 114.382C9.83688 116.187 13.1931 115.757 14.998 113.422L41.9096 78.602C43.7144 76.2668 43.2845 72.9107 40.9493 71.1058Z" fill="#153385" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M38.9593 69.5674L36.0265 67.3007C33.6913 65.4959 30.3352 65.9258 28.5303 68.261L1.61863 103.081C-0.186217 105.416 0.24372 108.772 2.57892 110.577L5.51168 112.844C7.84689 114.648 11.2031 114.218 13.008 111.883L39.9196 77.0637C41.7244 74.7285 41.2945 71.3723 38.9593 69.5674Z" fill="#0B64DD" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M36.4536 50.7374L52.8069 33.3696L67.111 48.4974L85.5336 25.3335" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M52.8067 37.4479C54.421 37.4479 55.7298 36.1392 55.7298 34.5248C55.7298 32.9104 54.421 31.6017 52.8067 31.6017C51.1923 31.6017 49.8835 32.9104 49.8835 34.5248C49.8835 36.1392 51.1923 37.4479 52.8067 37.4479Z" fill="#48EFCF" stroke="#101C3F" stroke-miterlimit="10"/>
<path d="M67.613 51.1896C69.2274 51.1896 70.5361 49.8809 70.5361 48.2665C70.5361 46.6521 69.2274 45.3434 67.613 45.3434C65.9986 45.3434 64.6899 46.6521 64.6899 48.2665C64.6899 49.8809 65.9986 51.1896 67.613 51.1896Z" fill="#48EFCF" stroke="#101C3F" stroke-miterlimit="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -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<string, React.FC> = {
semantic: SemanticSearch,
vector: VectorSearch,
keyword: KeywordSearch,
};
export const AISearchCapabilities: React.FC = () => {
const [selectedCapability, setSelectedCapability] = useState<string>(
SEARCH_CAPABILITIES[0].value
);
const currentBreakpoint = useCurrentEuiBreakpoint();
const SelectedComponent = capabilityComponents[selectedCapability];
return (
<EuiFlexGroup gutterSize="xs" direction="column">
<EuiFlexItem>
<EuiTitle size="xxs">
<h4>
{i18n.translate('xpack.searchHomepage.aiSearchCapabilities.title', {
defaultMessage: 'Explore Elastics AI search capabilities',
})}
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup
gutterSize="xl"
direction={currentBreakpoint === 's' || currentBreakpoint === 'm' ? 'column' : 'row'}
>
<EuiFlexItem grow={1}>
<EuiListGroup>
{SEARCH_CAPABILITIES.map((capability) => (
<EuiListGroupItem
key={capability.value}
color="primary"
label={capability.label}
onClick={() => setSelectedCapability(capability.value)}
isActive={selectedCapability === capability.value}
data-test-subj={`aiSearchCapabilities-item-${capability.value}`}
/>
))}
</EuiListGroup>
</EuiFlexItem>
<EuiFlexItem grow={3}>
<SelectedComponent />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -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 (
<EuiPanel color="transparent" paddingSize="s">
<EuiFlexGroup gutterSize="l" direction={currentBreakpoint === 'xl' ? 'row' : 'column'}>
<EuiFlexItem grow={false}>
<span>
<EuiImage src={capability.image} alt={capability.imageAlt} size="s" />
</span>
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h4>{capability.heading}</h4>
</EuiTitle>
<EuiSpacer size="xs" />
<EuiText size="m" color="subdued">
<p>{capability.subheading}</p>
</EuiText>
<EuiSpacer size="s" />
<EuiFlexGroup direction="column" gutterSize="xs">
{capability.featureBullets.map((item: string, index: number) => (
<EuiFlexItem grow={false} key={index}>
<WorkflowFeatureBullet feature={item} />
</EuiFlexItem>
))}
</EuiFlexGroup>
<EuiSpacer />
<span>
<EuiButton
iconType="plusInCircle"
href={createIndexUrl}
data-test-subj={capability.dataTestSubj}
>
{capability.buttonLabel}
</EuiButton>
</span>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
};

View file

@ -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 Elasticsearchs full-text search capabilities',
}),
subheading: i18n.translate(
'xpack.searchHomepage.aiSearchCapabilities.keywordSearch.description',
{
defaultMessage:
'Use a semantic_text field and Elastics 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 <AISearchWorkflow capability={keywordSearchText} />;
};

View file

@ -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 Elastics 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 <AISearchWorkflow capability={semanticSearchText} />;
};

View file

@ -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 <AISearchWorkflow capability={vectorSearchText} />;
};

View file

@ -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 }) => (
<EuiFlexGroup gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type="checkInCircleFilled" color="subdued" size="s" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
<p>{feature}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -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 (
<>
<EuiTitle size="xxs">
<h4>
{i18n.translate('xpack.searchHomepage.threatDetectionSolutions.title', {
defaultMessage: 'Looking for logs or threat detection solutions?',
})}
</h4>
</EuiTitle>
<EuiSpacer />
<EuiFlexGroup gutterSize="xl" direction={currentBreakpoint === 'xl' ? 'row' : 'column'}>
<EuiFlexItem>
<Observability />
</EuiFlexItem>
{/* TO DO: Enable the following once we have text content ready
<EuiFlexItem>
<Security />
</EuiFlexItem> */}
</EuiFlexGroup>
</>
);
};

View file

@ -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 (
<EuiFlexGroup gutterSize="m" data-test-subj="observabilitySection">
<EuiFlexItem grow={false}>
<EuiAvatar
size="xl"
color="plain"
name="Observability"
iconType="logoObservability"
style={{ border: euiTheme.border.thin }}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h4>
{i18n.translate('xpack.searchHomepage.observability.title', {
defaultMessage: 'Observability',
})}
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s" color="subdued">
<span>
{i18n.translate('xpack.searchHomepage.observability.description', {
defaultMessage:
'Consolidate your logs, metrics, application traces, and system availability with purpose-built UIs.',
})}
</span>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate('xpack.searchHomepage.observability.logsTitle', {
defaultMessage: 'Collect and analyze logs',
})}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink
href={docLinks.analyzeLogs}
data-test-subj="exploreLogstashAndBeatsLink"
>
{i18n.translate('xpack.searchHomepage.observability.exploreLogstashBeats', {
defaultMessage: 'Explore Logstash and Beats',
})}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate(
'xpack.searchHomepage.observability.performanceMonitoringTitle',
{
defaultMessage: 'Powerful performance monitoring',
}
)}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink href={o11yTrialLink} data-test-subj="createObservabilityProjectLink">
{i18n.translate('xpack.searchHomepage.observability.observabilitySpaceLink', {
defaultMessage: 'Create an Observability project',
})}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -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 (
<EuiFlexGroup gutterSize="m">
<EuiFlexItem grow={false}>
<EuiAvatar
size="xl"
color="plain"
name="Security"
iconType="logoSecurity"
style={{ border: euiTheme.border.thin }}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<span>
{i18n.translate('xpack.searchHomepage.security.title', {
defaultMessage: 'Security',
})}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s" color="subdued">
<span>
{i18n.translate('xpack.searchHomepage.security.description', {
defaultMessage:
'Prevent, collect, detect, and respond to threats for unified protection across your infrastructure.',
})}
</span>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate('xpack.searchHomepage.security.detectThreatsTitle', {
defaultMessage: 'Detect threats in your data',
})}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj="setupSiemLink">
{i18n.translate('xpack.searchHomepage.security.setupSiem', {
defaultMessage: 'Setup your SIEM',
})}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate('xpack.searchHomepage.security.secureCloudAssetsTitle', {
defaultMessage: 'Secure your cloud assets',
})}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj="cloudSecurityPostureManagementLink">
{i18n.translate(
'xpack.searchHomepage.security.cloudSecurityPostureManagementLink',
{
defaultMessage: 'Cloud Security Posture Management',
}
)}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -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 (
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={3}>
<EuiFlexGroup direction="column" gutterSize="l">
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
<EuiTitle size="m">
<h2>
{i18n.translate('xpack.searchHomepage.connectToElasticsearch.title', {
defaultMessage: 'Connect to Elasticsearch',
})}
</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<EuiText grow={false} color="subdued">
<p>
{i18n.translate('xpack.searchHomepage.connectToElasticsearch.description', {
defaultMessage:
'Set up your connection to Elasticsearch to start searching and analyzing your data.',
})}
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="flexStart" gutterSize="l">
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate(
'xpack.searchHomepage.connectToElasticsearch.elasticSearchEndpointLabel',
{
defaultMessage: 'Elasticsearch endpoint',
}
)}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiCopy textToCopy={elasticsearchUrl}>
{(copy) => (
<EuiButtonIcon
aria-label={i18n.translate(
'xpack.searchHomepage.connectToElasticsearch.copyElasticsearchUrlAriaLabel',
{ defaultMessage: 'Copy Elasticsearch URL' }
)}
onClick={copy}
iconType="copyClipboard"
size="m"
data-test-subj="copyEndpointButton"
/>
)}
</EuiCopy>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFieldText
value={elasticsearchUrl}
readOnly
data-test-subj="endpointValueField"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<span>
{i18n.translate(
'xpack.searchHomepage.connectToElasticsearch.apiKeysLabel',
{
defaultMessage: 'API keys',
}
)}
</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" alignItems="baseline">
<EuiFlexItem grow={false}>
<EuiButton
href={createApiKeyLink}
iconType="key"
data-test-subj="createApiKeyButton"
>
{i18n.translate(
'xpack.searchHomepage.connectToElasticsearch.createApiKey',
{
defaultMessage: 'Create API key',
}
)}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="gear"
href={manageKeysLink}
data-test-subj="manageApiKeysButton"
>
Manage API keys
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBadge
data-test-subj="activeApiKeysBadge"
color={(data?.apiKeys?.length ?? 0) > 0 ? 'success' : 'warning'}
>
{data?.apiKeys?.length ?? 0} active
</EuiBadge>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AISearchCapabilities />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<ConnectToElasticsearchSidePanel />
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -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 (
<EuiPanel color="subdued" grow={false}>
<EuiFlexGroup gutterSize="m" direction="column">
<EuiCard
display="plain"
hasBorder
textAlign="left"
titleSize="xs"
title={i18n.translate('xpack.searchHomepage.connectToElasticsearch.uploadFileTitle', {
defaultMessage: 'Upload a file',
})}
description={
<FormattedMessage
id="xpack.searchHomepage.connectToElasticsearch.uploadFileDescription"
defaultMessage="Analyze and import data from a file."
/>
}
footer={
<EuiButtonEmpty
onClick={onFileUpload}
iconType="importAction"
data-test-subj="uploadFileButton"
>
{i18n.translate('xpack.searchHomepage.connectToElasticsearch.uploadFileButton', {
defaultMessage: 'Upload a file',
})}
</EuiButtonEmpty>
}
/>
{/* TODO: Enable The “Sample Data” section when the one-click sample data ingestion feature is complete. */}
{/* <EuiCard
display="plain"
hasBorder
textAlign="left"
titleSize="xs"
title={
<FormattedMessage
id="xpack.searchHomepage.connectToElasticsearch.sampleDatasetTitle"
defaultMessage="Add sample data"
/>
}
description={
<FormattedMessage
id="xpack.searchHomepage.connectToElasticsearch.uploadFileDescription"
defaultMessage="Add data sets with sample visualizations, dashboards, and more."
/>
}
footer={
<EuiButtonEmpty href="#" iconType="importAction" data-test-subj="sampleDatasetButton">
{i18n.translate('xpack.searchHomepage.connectToElasticsearch.sampleDatasetButton', {
defaultMessage: 'Add sample data',
})}
</EuiButtonEmpty>
}
/> */}
<EuiCard
display="transparent"
textAlign="left"
titleSize="xs"
title={
<FormattedMessage
id="xpack.searchHomepage.connectToElasticsearch.needAdviceTitle"
defaultMessage="Need help with Serverless? Engage a customer engineer."
/>
}
description={
<FormattedMessage
id="xpack.searchHomepage.connectToElasticsearch.getExpertAdviceDescription"
defaultMessage="Get in touch with us for help getting started with best practices on data ingest, performance, and/or cost efficiency in your Serverless projects!"
/>
}
footer={
<EuiLink
href={docLinks.customerEngineerRequestForm}
data-test-subj="customerEngineerRequestFormLink"
>
{i18n.translate(
'xpack.searchHomepage.connectToElasticsearch.customerEngineerRequestForm',
{
defaultMessage: 'Submit a request',
}
)}
</EuiLink>
}
/>
</EuiFlexGroup>
</EuiPanel>
);
};

View file

@ -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 (
<EuiButtonEmpty
iconType="console"
color="primary"
onClick={openConsole}
data-test-subj="searchHomepageEmbeddedConsoleButton"
data-telemetry-id="searchHomepageEmbeddedConsoleButton"
>
<FormattedMessage
id="xpack.searchHomepage.consoleLink.buttonText"
defaultMessage="Quickly get started in Console"
/>
</EuiButtonEmpty>
);
};

View file

@ -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 (
<>
<EuiTitle size="xxs">
<h4>
{i18n.translate('xpack.searchHomepage.diveDeeper.title', {
defaultMessage: 'Dive deeper with Elasticsearch',
})}
</h4>
</EuiTitle>
<EuiSpacer size="l" />
<EuiFlexGroup direction={currentBreakpoint === 'xl' ? 'row' : 'column'}>
<EuiFlexItem data-test-subj="searchLabsSection">
<DocCallouts
title={i18n.translate('xpack.searchHomepage.searchLabs.title', {
defaultMessage: 'Elasticsearch Labs',
})}
description={i18n.translate('xpack.searchHomepage.searchLabs.description', {
defaultMessage: 'Explore the latest innovations for advanced search experiences.',
})}
buttonHref={docLinks.visitSearchLabs}
buttonLabel={i18n.translate('xpack.searchHomepage.searchLabs.buttonText', {
defaultMessage: 'Visit Elasticsearch Labs',
})}
dataTestSubj="searchLabsButton"
/>
</EuiFlexItem>
<EuiFlexItem data-test-subj="pythonNotebooksSection">
<DocCallouts
title={i18n.translate('xpack.searchHomepage.pythonNotebooks.title', {
defaultMessage: 'Python notebooks',
})}
description={i18n.translate('xpack.searchHomepage.pythonNotebooks.description', {
defaultMessage: 'Test features in a virtual environment with executable notebooks.',
})}
buttonHref={docLinks.notebooksExamples}
buttonLabel={i18n.translate('xpack.searchHomepage.pythonNotebooks.buttonText', {
defaultMessage: 'Open notebooks',
})}
dataTestSubj="openNotebooksButton"
/>
</EuiFlexItem>
<EuiFlexItem data-test-subj="elasticsearchDocumentationSection">
<DocCallouts
title={i18n.translate('xpack.searchHomepage.elasticsearchDocs.title', {
defaultMessage: 'Elasticsearch documentation',
})}
description={i18n.translate(
'xpack.searchHomepage.elasticsearchDocumentation.description',
{
defaultMessage:
'Learn about Elasticsearch APIs, query languages, and common use cases.',
}
)}
buttonHref={docLinks.elasticsearchDocs}
buttonLabel={i18n.translate(
'xpack.searchHomepage.elasticsearchDocumentation.buttonText',
{
defaultMessage: 'View documentation',
}
)}
dataTestSubj="viewDocumentationButton"
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
};

View file

@ -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<DocCalloutsProps> = ({
title,
description,
buttonHref,
buttonLabel,
dataTestSubj,
}) => {
return (
<>
<EuiTitle size="s">
<h4>{title}</h4>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s" color="subdued">
<p>{description}</p>
</EuiText>
<EuiSpacer size="m" />
<span>
<EuiButton
iconType="popout"
href={buttonHref}
iconSide="right"
data-test-subj={dataTestSubj}
color="text"
>
{buttonLabel}
</EuiButton>
</span>
</>
);
};

View file

@ -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<boolean>(false);
const onClickEndpointsButton = useCallback(() => {
usageTracker.click('endpoints_and_api_keys');
setOpen(true);
}, [usageTracker]);
return (
<>
<EuiHeaderLinks>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiButtonEmpty
iconType="endpoint"
size="s"
onClick={onClickEndpointsButton}
data-test-subj="searchHomepageEndpointsHeaderActionEndpointsApiKeysButton"
data-telemetry-id="searchHomepageEndpointsHeaderActionEndpointsApiKeysButton"
>
<FormattedMessage
id="xpack.searchHomepage.header.endpointsButtonLabel"
defaultMessage="Endpoints & API keys"
/>
</EuiButtonEmpty>
</EuiFlexGroup>
</EuiHeaderLinks>
{open && (
<EuiFlyout onClose={() => setOpen(false)} size="s">
<KibanaWiredConnectionDetailsProvider>
<ConnectionDetailsFlyoutContent />
</KibanaWiredConnectionDetailsProvider>
</EuiFlyout>
)}
</>
);
};

View file

@ -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<FeatureUpdateGroupProps> = ({ updates }) => {
return (
<EuiFlexGroup gutterSize="xl">
{updates.map((update, index) => (
<EuiFlexItem grow={false} key={index}>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiIcon type="checkInCircleFilled" color="primary" />
</EuiFlexItem>
<EuiFlexItem grow={false}>{update}</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
))}
</EuiFlexGroup>
);
};

View file

@ -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 = () => (
<>
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="m">
<EuiFlexItem>
<EuiLink href={docLinks.elasticCommunity} data-test-subj="elasticCommunityLink">
{i18n.translate('xpack.searchHomepage.footer.elasticCommunity', {
defaultMessage: 'Connect with the Elastic Community',
})}
</EuiLink>
</EuiFlexItem>
<EuiFlexItem>
<EuiLink href={docLinks.kibanaFeedback} data-test-subj="giveFeedbackLink">
{i18n.translate('xpack.searchHomepage.footer.giveFeedback', {
defaultMessage: 'Give feedback',
})}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="l" />
</>
);

View file

@ -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 ? <consolePlugin.EmbeddableConsole /> : null),
[consolePlugin]
@ -24,7 +26,8 @@ export const SearchHomepagePage = () => {
return (
<EuiPageTemplate offset={0} restrictWidth={false} data-test-subj="search-homepage" grow={false}>
<SearchHomepageHeader showEndpointsAPIKeys />
<SearchHomepageHeader />
<EuiHorizontalRule margin="none" />
<SearchHomepageBody />
{embeddableConsole}
</EuiPageTemplate>

View file

@ -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 = () => (
<KibanaPageTemplate.Section alignment="top" restrictWidth={false} grow>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<ConsoleLinkButton />
<EuiFlexGroup gutterSize="l" direction="column">
<EuiFlexItem>
<ConnectToElasticsearch />
</EuiFlexItem>
<EuiFlexItem>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem>
<AlternateSolutions />
</EuiFlexItem>
<EuiFlexItem>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem>
<DiveDeeperWithElasticsearch />
</EuiFlexItem>
<EuiFlexItem>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem>
<Footer />
</EuiFlexItem>
</EuiFlexGroup>
</KibanaPageTemplate.Section>

View file

@ -5,23 +5,74 @@
* 2.0.
*/
import React from 'react';
import { EuiPageTemplate, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiPageTemplate,
EuiFlexGroup,
EuiFlexItem,
useEuiTheme,
EuiPanel,
EuiText,
EuiTitle,
EuiSpacer,
EuiImage,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useAssetBasePath } from '../hooks/use_asset_base_path';
import { EndpointsHeaderAction } from './endpoints_header_action';
export const SearchHomepageHeader: React.FC = () => {
const { euiTheme, colorMode } = useEuiTheme();
const assetBasePath = useAssetBasePath();
export interface SearchHomepageHeaderProps {
showEndpointsAPIKeys: boolean;
}
return (
<EuiPageTemplate.Section
data-test-subj="search-homepage-header"
paddingSize="none"
color="subdued"
>
<EuiFlexGroup
gutterSize="m"
alignItems="stretch"
style={{
paddingLeft: euiTheme.size.xxxl,
paddingRight: euiTheme.size.xxxl,
}}
>
<EuiFlexItem style={{ alignSelf: 'center' }}>
<EuiPanel color="transparent" paddingSize="xl">
<EuiTitle size="l">
<h1>
{i18n.translate('xpack.searchHomepage.pageTitle', {
defaultMessage: 'Your vector database just got faster',
})}
</h1>
</EuiTitle>
<EuiSpacer size="m" />
<EuiText grow={false}>
<p>
{i18n.translate('xpack.searchHomepage.description', {
defaultMessage:
'Elasticsearch and Lucene now offer “Better binary quantization”, delivering ~95% memory reduction while maintaining high ranking quality.',
})}
</p>
</EuiText>
<EuiSpacer size="xl" />
{/* To DO: Enable the following once we have text content ready
<FeatureUpdateGroup updates={['Feature update', 'Feature update', 'Feature update']} /> */}
</EuiPanel>
</EuiFlexItem>
export const SearchHomepageHeader = ({ showEndpointsAPIKeys }: SearchHomepageHeaderProps) => (
<EuiPageTemplate.Header
pageTitle={
<EuiTitle data-test-subj="search-homepage-header-title" size="l">
<FormattedMessage id="xpack.searchHomepage.pageTitle" defaultMessage="Welcome to Search" />
</EuiTitle>
}
data-test-subj="search-homepage-header"
rightSideItems={[...(showEndpointsAPIKeys ? [<EndpointsHeaderAction />] : [])]}
/>
);
<EuiFlexItem>
<EuiImage
size="xl"
src={
colorMode === 'LIGHT'
? `${assetBasePath}/search_homepage_light.svg`
: `${assetBasePath}/search_homepage_dark.svg`
}
alt=""
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageTemplate.Section>
);
};

View file

@ -18,7 +18,7 @@ export const App: React.FC = () => {
} = useKibana();
return (
<UsageTrackerContextProvider usageCollection={usageCollection}>
<SearchHomepageHeader showEndpointsAPIKeys={false} />
<SearchHomepageHeader />
<SearchHomepageBody />
</UsageTrackerContextProvider>
);

View file

@ -0,0 +1,18 @@
/*
* 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 enum QueryKeys {
FetchIndex = 'fetchIndex',
FetchMapping = 'fetchMapping',
FetchOnboardingToken = 'fetchOnboardingToken',
FetchSearchIndicesStatus = 'fetchSearchIndicesStatus',
FetchUserStartPrivileges = 'fetchUserStartPrivileges',
SearchDocuments = 'searchDocuments',
ApiKey = 'apiKey',
}
export const ELASTICSEARCH_URL_PLACEHOLDER = 'https://your_deployment_url';

View file

@ -0,0 +1,20 @@
/*
* 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 { useQuery } from '@tanstack/react-query';
import type { ApiKeysResponse } from '../../../common/types';
import { useKibana } from '../use_kibana';
import { GET_API_KEYS_ROUTE } from '../../../common/routes';
import { QueryKeys } from '../../constants';
export const useGetApiKeys = () => {
const { http } = useKibana().services;
return useQuery({
queryKey: [QueryKeys.ApiKey],
queryFn: () => http.fetch<ApiKeysResponse>(GET_API_KEYS_ROUTE),
});
};

View file

@ -0,0 +1,32 @@
/*
* 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 { useQuery } from '@tanstack/react-query';
import type { UseQueryResult } from '@tanstack/react-query';
import { GET_STATUS_ROUTE } from '../../../common/routes';
import type { IndicesStatusResponse } from '../../../common/types';
import { QueryKeys } from '../../constants';
import { useKibana } from '../use_kibana';
const DEFAULT_INDICES_POLLING_INTERVAL = 15 * 1000;
export const useIndicesStatusQuery = (
pollingInterval = DEFAULT_INDICES_POLLING_INTERVAL
): UseQueryResult<IndicesStatusResponse> => {
const { http } = useKibana().services;
return useQuery({
refetchInterval: pollingInterval,
refetchIntervalInBackground: true,
refetchOnWindowFocus: 'always',
retry: true,
queryKey: [QueryKeys.FetchSearchIndicesStatus],
queryFn: () => http.get<IndicesStatusResponse>(GET_STATUS_ROUTE),
});
};

View file

@ -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 { useQuery } from '@tanstack/react-query';
import type { UserStartPrivilegesResponse } from '../../../common/types';
import { QueryKeys } from '../../constants';
import { useKibana } from '../use_kibana';
import { GET_USER_PRIVILEGES_ROUTE } from '../../../common/routes';
export const useUserPrivilegesQuery = (indexName: string) => {
const { http } = useKibana().services;
return useQuery({
queryKey: [QueryKeys.FetchUserStartPrivileges],
queryFn: () =>
http.get<UserStartPrivilegesResponse>(
GET_USER_PRIVILEGES_ROUTE.replace('{indexName}', indexName)
),
});
};

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import { IUiSettingsClient } from '@kbn/core/public';
import { HOMEPAGE_FEATURE_FLAG_ID } from '../common';
import { PLUGIN_ID } from '../../common';
import { useKibana } from './use_kibana';
export function isHomepageEnabled(uiSettings: IUiSettingsClient): boolean {
return uiSettings.get<boolean>(HOMEPAGE_FEATURE_FLAG_ID, false);
}
export const useAssetBasePath = () => {
const { http } = useKibana().services;
return http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets`);
};

View file

@ -0,0 +1,27 @@
/*
* 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 { useEffect, useState } from 'react';
import { useKibana } from './use_kibana';
import { ELASTICSEARCH_URL_PLACEHOLDER } from '../constants';
export const useElasticsearchUrl = (): string => {
const {
services: { cloud },
} = useKibana();
const [elasticsearchUrl, setElasticsearchUrl] = useState<string>(ELASTICSEARCH_URL_PLACEHOLDER);
useEffect(() => {
cloud?.fetchElasticsearchConfig().then((config) => {
setElasticsearchUrl(config?.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER);
});
}, [cloud]);
return elasticsearchUrl;
};

View file

@ -0,0 +1,48 @@
/*
* 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 { useEffect, useMemo, useState } from 'react';
import { useIndicesStatusQuery } from './api/use_indices_status_query';
import { useUserPrivilegesQuery } from './api/use_user_permissions';
import { generateRandomIndexName } from '../utils/indices';
import { useKibana } from './use_kibana';
export const useSearchHomePageRedirect = () => {
const { application, http } = useKibana().services;
const indexName = useMemo(() => generateRandomIndexName(), []);
const { data: userPrivileges } = useUserPrivilegesQuery(indexName);
const { data: indicesStatus } = useIndicesStatusQuery();
const [hasDoneRedirect, setHasDoneRedirect] = useState(() => false);
return useEffect(() => {
if (hasDoneRedirect) {
return;
}
if (!userPrivileges) {
return;
}
if (userPrivileges?.privileges?.canManageIndex === false) {
setHasDoneRedirect(true);
return;
}
if (!indicesStatus) {
return;
}
if (indicesStatus.indexNames.length === 0) {
application.navigateToApp('elasticsearchStart');
setHasDoneRedirect(true);
return;
}
setHasDoneRedirect(true);
}, [application, http, indicesStatus, setHasDoneRedirect, hasDoneRedirect, userPrivileges]);
};

View file

@ -5,12 +5,10 @@
* 2.0.
*/
import { PluginInitializerContext } from '@kbn/core/public';
import { SearchHomepagePlugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
return new SearchHomepagePlugin(initializerContext);
export function plugin() {
return new SearchHomepagePlugin();
}
export type {

View file

@ -5,25 +5,19 @@
* 2.0.
*/
import {
CoreSetup,
Plugin,
CoreStart,
AppMountParameters,
PluginInitializerContext,
} from '@kbn/core/public';
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { PLUGIN_ID } from '../common';
import { docLinks } from '../common/doc_links';
import { SearchHomepage } from './embeddable';
import { isHomepageEnabled } from './feature_flags';
import { initQueryClient } from './services/query_client';
import {
SearchHomepageConfigType,
SearchHomepageAppInfo,
SearchHomepageAppPluginStartDependencies,
SearchHomepagePluginSetup,
SearchHomepagePluginStart,
SearchHomepageAppPluginStartDependencies,
SearchHomepageAppInfo,
SearchHomepageServicesContext,
SearchHomepageServicesContextDeps,
} from './types';
const appInfo: SearchHomepageAppInfo = {
@ -35,34 +29,26 @@ const appInfo: SearchHomepageAppInfo = {
export class SearchHomepagePlugin
implements Plugin<SearchHomepagePluginSetup, SearchHomepagePluginStart, {}, {}>
{
private readonly config: SearchHomepageConfigType;
constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.get<SearchHomepageConfigType>();
}
public setup(
core: CoreSetup<SearchHomepageAppPluginStartDependencies, SearchHomepagePluginStart>
) {
const queryClient = initQueryClient(core.notifications.toasts);
const result: SearchHomepagePluginSetup = {
app: appInfo,
isHomepageFeatureEnabled() {
return isHomepageEnabled(core.uiSettings);
},
};
if (!this.config.ui?.enabled) return result;
if (!isHomepageEnabled(core.uiSettings)) return result;
core.application.register({
...result.app,
async mount({ element, history }: AppMountParameters) {
const { renderApp } = await import('./application');
const [coreStart, depsStart] = await core.getStartServices();
const startDeps: SearchHomepageServicesContext = {
docLinks.setDocLinks(coreStart.docLinks.links);
const startDeps: SearchHomepageServicesContextDeps = {
...depsStart,
history,
};
return renderApp(coreStart, startDeps, element);
return renderApp(coreStart, startDeps, element, queryClient);
},
});
@ -72,9 +58,6 @@ export class SearchHomepagePlugin
public start(core: CoreStart) {
return {
app: appInfo,
isHomepageFeatureEnabled() {
return isHomepageEnabled(core.uiSettings);
},
SearchHomepage,
};
}

View file

@ -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 type { IToasts } from '@kbn/core/public';
import { QueryClient, QueryCache } from '@tanstack/react-query';
import { getErrorCode, getErrorMessage, isKibanaServerError } from '../utils/get_error_message';
export function initQueryClient(toasts: IToasts): QueryClient {
return new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
// 404s are often functionally okay and shouldn't show toasts by default
if (getErrorCode(error) === 404) {
return;
}
if (isKibanaServerError(error) && !error.skipToast) {
toasts.addError(error, {
title: error.name,
toastMessage: getErrorMessage(error),
toastLifeTimeMs: 1000,
});
}
},
}),
});
}

View file

@ -7,17 +7,12 @@
import type { ComponentProps, FC } from 'react';
import type { ConsolePluginStart } from '@kbn/console-plugin/public';
import type { AppMountParameters } from '@kbn/core/public';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { CloudStart } from '@kbn/cloud-plugin/public';
import type { App } from './components/stack_app';
export interface SearchHomepageConfigType {
ui: {
enabled: boolean;
};
}
export interface SearchHomepageAppInfo {
appRoute: string;
id: string;
@ -31,11 +26,6 @@ export interface SearchHomepagePluginSetup {
* and deep links.
*/
app: SearchHomepageAppInfo;
/**
* Checks if the Search Homepage feature flag is currently enabled.
* @returns true if Search Homepage feature is enabled
*/
isHomepageFeatureEnabled: () => boolean;
}
export interface SearchHomepagePluginStart {
@ -45,11 +35,6 @@ export interface SearchHomepagePluginStart {
* and deep links.
*/
app: SearchHomepageAppInfo;
/**
* Checks if the Search Homepage feature flag is currently enabled.
* @returns true if Search Homepage feature is enabled
*/
isHomepageFeatureEnabled: () => boolean;
/**
* SearchHomepage shared component, used to render the search homepage in
* the Stack search plugin
@ -61,12 +46,19 @@ export interface SearchHomepageAppPluginStartDependencies {
console?: ConsolePluginStart;
share: SharePluginStart;
usageCollection?: UsageCollectionStart;
cloud?: CloudStart;
}
export interface SearchHomepageServicesContext extends SearchHomepageAppPluginStartDependencies {
export interface SearchHomepageServicesContextDeps {
history: AppMountParameters['history'];
usageCollection?: UsageCollectionStart;
}
export type SearchHomepageServicesContext = CoreStart &
SearchHomepageAppPluginStartDependencies & {
history: AppMountParameters['history'];
};
export interface AppUsageTracker {
click: (eventName: string | string[]) => void;
count: (eventName: string | string[]) => void;

View file

@ -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 { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
export function getErrorMessage(error: unknown): string {
if (typeof error === 'string') {
return error;
}
if (isKibanaServerError(error)) {
return error.body.message;
}
if (typeof error === 'object' && (error as { name: string }).name) {
return (error as { name: string }).name;
}
return '';
}
export function getErrorCode(error: unknown): number | undefined {
if (isKibanaServerError(error)) {
return error.body.statusCode;
}
return undefined;
}
export function isKibanaServerError(
input: unknown
): input is Error & { body: KibanaServerError; name: string; skipToast?: boolean } {
if (
typeof input === 'object' &&
(input as { body: KibanaServerError }).body &&
typeof (input as { body: KibanaServerError }).body.message === 'string'
) {
return true;
}
return false;
}

View file

@ -0,0 +1,23 @@
/*
* 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 function generateRandomIndexName(
prefix: string = 'search-',
randomSuffixLength: number = 4
) {
const suffixCharacters = 'abcdefghijklmnopqrstuvwxyz0123456789';
const charsLength = suffixCharacters.length;
let result = prefix;
let counter = 0;
do {
result += suffixCharacters.charAt(Math.random() * charsLength);
counter++;
} while (counter < randomSuffixLength);
return result;
}

View file

@ -12,16 +12,10 @@ export * from './types';
const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: false }),
ui: schema.object({
enabled: schema.boolean({ defaultValue: false }),
}),
});
export type SearchHomepageConfig = TypeOf<typeof configSchema>;
export const config: PluginConfigDescriptor<SearchHomepageConfig> = {
exposeToBrowser: {
ui: true,
},
schema: configSchema,
};

View file

@ -0,0 +1,72 @@
/*
* 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 type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { Logger } from '@kbn/logging';
import type { IndicesStatusResponse, UserStartPrivilegesResponse } from '../../common/types';
import { isClosed, isHidden } from '../utils/index_utils';
export async function fetchIndicesStatus(
client: ElasticsearchClient,
logger: Logger
): Promise<IndicesStatusResponse> {
const indexMatches = await client.indices.get({
expand_wildcards: ['open'],
// for better performance only compute settings of indices but not mappings
features: ['settings'],
index: '*',
});
const indexNames = Object.keys(indexMatches).filter(
(indexName) =>
indexMatches[indexName] &&
!isHidden(indexMatches[indexName]) &&
!isClosed(indexMatches[indexName])
);
return {
indexNames,
};
}
export async function fetchUserStartPrivileges(
client: ElasticsearchClient,
logger: Logger,
indexName: string
): Promise<UserStartPrivilegesResponse> {
try {
const securityCheck = await client.security.hasPrivileges({
cluster: ['manage_api_key'],
index: [
{
names: [indexName],
privileges: ['manage', 'delete'],
},
],
});
return {
privileges: {
canManageIndex: securityCheck?.index?.[indexName]?.manage ?? false,
canDeleteDocuments: securityCheck?.index?.[indexName]?.delete ?? false,
canCreateApiKeys: securityCheck?.cluster?.manage_api_key ?? false,
},
};
} catch (e) {
logger.error(`Error checking user privileges for searchIndices elasticsearch start`);
logger.error(e);
return {
privileges: {
canManageIndex: false,
canDeleteDocuments: false,
canCreateApiKeys: false,
},
};
}
}

View file

@ -4,10 +4,24 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server';
import { SearchHomepagePluginSetup, SearchHomepagePluginStart } from './types';
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
Logger,
IRouter,
} from '@kbn/core/server';
import type { SecurityPluginStart } from '@kbn/security-plugin/server';
import { SearchHomepagePluginStart, SearchHomepagePluginSetup } from './types';
import { defineRoutes } from './routes';
export interface RouteDependencies {
http: CoreSetup<SearchHomepagePluginSetup>['http'];
logger: Logger;
router: IRouter;
getSecurity: () => Promise<SecurityPluginStart>;
}
export class SearchHomepagePlugin
implements Plugin<SearchHomepagePluginSetup, SearchHomepagePluginStart, {}, {}>
{
@ -19,6 +33,10 @@ export class SearchHomepagePlugin
public setup(core: CoreSetup<{}, SearchHomepagePluginStart>) {
this.logger.debug('searchHomepage: Setup');
const router = core.http.createRouter();
// Register server side APIs
defineRoutes(router, this.logger);
return {};
}

View file

@ -0,0 +1,51 @@
/*
* 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 type { IRouter } from '@kbn/core/server';
import type { Logger } from '@kbn/logging';
import { errorHandler } from '../utils/error_handler';
export const registerApiKeyRoutes = (router: IRouter, logger: Logger) => {
router.get(
{
path: '/internal/search_homepage/api_keys',
validate: {},
security: {
authz: {
enabled: false,
reason: 'This route delegates authorization to the es client',
},
},
},
errorHandler(logger)(async (context, request, response) => {
const core = await context.core;
const { client } = core.elasticsearch;
const user = core.security.authc.getCurrentUser();
if (user) {
const privileges = await client.asCurrentUser.security.hasPrivileges({
cluster: ['manage_own_api_key'],
});
const canManageOwnApiKey = privileges?.cluster.manage_own_api_key;
try {
const apiKeys = await client.asCurrentUser.security.getApiKey({
username: user.username,
});
const validKeys = apiKeys.api_keys.filter(({ invalidated }) => !invalidated);
return response.ok({ body: { apiKeys: validKeys, canManageOwnApiKey } });
} catch {
return response.ok({ body: { apiKeys: [], canManageOwnApiKey } });
}
}
return response.customError({
statusCode: 502,
body: 'Could not retrieve current user, security plugin is not ready',
});
})
);
};

View file

@ -0,0 +1,17 @@
/*
* 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 type { IRouter } from '@kbn/core/server';
import type { Logger } from '@kbn/logging';
import { registerStatusRoutes } from './status';
import { registerApiKeyRoutes } from './api_key_routes';
export function defineRoutes(router: IRouter, logger: Logger) {
registerApiKeyRoutes(router, logger);
registerStatusRoutes(router, logger);
}

View file

@ -0,0 +1,73 @@
/*
* 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 type { IRouter } from '@kbn/core/server';
import type { Logger } from '@kbn/logging';
import { schema } from '@kbn/config-schema';
import { GET_STATUS_ROUTE, GET_USER_PRIVILEGES_ROUTE } from '../../common/routes';
import { fetchIndicesStatus, fetchUserStartPrivileges } from '../lib/status';
export function registerStatusRoutes(router: IRouter, logger: Logger) {
router.get(
{
path: GET_STATUS_ROUTE,
security: {
authz: {
enabled: false,
reason: 'This route delegates authorization to the scoped ES client',
},
},
validate: {},
options: {
access: 'internal',
},
},
async (context, _request, response) => {
const core = await context.core;
const client = core.elasticsearch.client.asCurrentUser;
const body = await fetchIndicesStatus(client, logger);
return response.ok({
body,
headers: { 'content-type': 'application/json' },
});
}
);
router.get(
{
path: GET_USER_PRIVILEGES_ROUTE,
security: {
authz: {
enabled: false,
reason: 'This route delegates authorization to the scoped ES client',
},
},
validate: {
params: schema.object({
indexName: schema.string(),
}),
},
options: {
access: 'internal',
},
},
async (context, request, response) => {
const core = await context.core;
const client = core.elasticsearch.client.asCurrentUser;
const { indexName } = request.params;
const body = await fetchUserStartPrivileges(client, logger, indexName);
return response.ok({
body,
headers: { 'content-type': 'application/json' },
});
}
);
}

View file

@ -0,0 +1,28 @@
/*
* 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 { RequestHandlerWrapper } from '@kbn/core-http-server';
import { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
import type { Logger } from '@kbn/logging';
function isKibanaServerError(error: any): error is KibanaServerError {
return error.statusCode && error.message;
}
export const errorHandler: (logger: Logger) => RequestHandlerWrapper = (logger) => (handler) => {
return async (context, request, response) => {
try {
return await handler(context, request, response);
} catch (e) {
logger.error(e);
if (isKibanaServerError(e)) {
return response.customError({ statusCode: e.statusCode, body: e.message });
}
throw e;
}
};
};

View file

@ -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 type { IndicesIndexState } from '@elastic/elasticsearch/lib/api/types';
export function isHidden(index: IndicesIndexState): boolean {
return index.settings?.index?.hidden === true || index.settings?.index?.hidden === 'true';
}
export function isClosed(index: IndicesIndexState): boolean {
return (
index.settings?.index?.verified_before_close === true ||
index.settings?.index?.verified_before_close === 'true'
);
}

View file

@ -14,16 +14,23 @@
"@kbn/react-kibana-context-render",
"@kbn/kibana-react-plugin",
"@kbn/i18n-react",
"@kbn/doc-links",
"@kbn/shared-ux-router",
"@kbn/shared-ux-page-kibana-template",
"@kbn/shared-ux-utility",
"@kbn/security-plugin",
"@kbn/security-plugin-types-common",
"@kbn/i18n",
"@kbn/console-plugin",
"@kbn/share-plugin",
"@kbn/usage-collection-plugin",
"@kbn/config-schema",
"@kbn/cloud",
"@kbn/analytics",
"@kbn/cloud-plugin",
"@kbn/kibana-utils-plugin",
"@kbn/core-http-server",
"@kbn/logging",
"@kbn/core-elasticsearch-server"
],
"exclude": [
"target/**/*",

View file

@ -28,6 +28,7 @@
"searchprofiler",
"security",
"serverless",
"searchHomepage",
"share",
"visualizations"
],

View file

@ -17,3 +17,4 @@ 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';
export const SEARCH_HOMEPAGE_PATH = '/app/elasticsearch/home';

View file

@ -26,6 +26,14 @@ export const navigationTree = ({ isAppRegistered }: ApplicationStart): Navigatio
isCollapsible: false,
breadcrumbStatus: 'hidden',
children: [
{
id: 'home',
title: i18n.translate('xpack.serverlessSearch.nav.home', {
defaultMessage: 'Home',
}),
link: 'searchHomepage',
spaceBefore: 'm',
},
{
id: 'analyze',
title: i18n.translate('xpack.serverlessSearch.nav.analyze', {

View file

@ -29,6 +29,7 @@ import {
import { createIndexDocumentsContent } from './application/components/index_documents/documents_tab';
import { getErrorCode, getErrorMessage, isKibanaServerError } from './utils/get_error_message';
import { navigationTree } from './navigation_tree';
import { SEARCH_HOMEPAGE_PATH } from './application/constants';
export class ServerlessSearchPlugin
implements
@ -99,6 +100,21 @@ export class ServerlessSearchPlugin
},
});
core.application.register({
id: 'serverlessHomeRedirect',
title: homeTitle,
appRoute: '/app/elasticsearch',
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
visibleIn: [],
async mount({}: AppMountParameters) {
const [coreStart] = await core.getStartServices();
coreStart.chrome.docTitle.change(homeTitle);
coreStart.application.navigateToApp('searchHomepage');
return () => {};
},
});
const connectorsTitle = i18n.translate('xpack.serverlessSearch.app.connectors.title', {
defaultMessage: 'Connectors',
});
@ -141,22 +157,6 @@ export class ServerlessSearchPlugin
},
});
const { searchIndices } = setupDeps;
core.application.register({
id: 'serverlessHomeRedirect',
title: homeTitle,
appRoute: '/app/elasticsearch',
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
visibleIn: [],
async mount({}: AppMountParameters) {
const [coreStart] = await core.getStartServices();
coreStart.chrome.docTitle.change(homeTitle);
coreStart.application.navigateToApp(searchIndices.startAppId);
return () => {};
},
});
return {};
}
@ -165,7 +165,7 @@ export class ServerlessSearchPlugin
services: ServerlessSearchPluginStartDependencies
): ServerlessSearchPluginStart {
const { serverless, management, indexManagement, security } = services;
serverless.setProjectHome(services.searchIndices.startRoute);
serverless.setProjectHome(SEARCH_HOMEPAGE_PATH);
const aiAssistantIsEnabled = core.application.capabilities.observabilityAIAssistant?.show;
const navigationTree$ = of(navigationTree(core.application));

View file

@ -18,6 +18,10 @@ import type {
SearchIndicesPluginSetup,
SearchIndicesPluginStart,
} from '@kbn/search-indices/public';
import type {
SearchHomepagePluginSetup,
SearchHomepagePluginStart,
} from '@kbn/search-homepage/public';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServerlessSearchPluginSetup {}
@ -31,6 +35,7 @@ export interface ServerlessSearchPluginSetupDependencies {
serverless: ServerlessPluginSetup;
discover: DiscoverSetup;
searchIndices: SearchIndicesPluginSetup;
searchHomepage?: SearchHomepagePluginSetup;
}
export interface ServerlessSearchPluginStartDependencies {
@ -43,4 +48,5 @@ export interface ServerlessSearchPluginStartDependencies {
share: SharePluginStart;
indexManagement?: IndexManagementPluginStart;
searchIndices: SearchIndicesPluginStart;
searchHomepage?: SearchHomepagePluginStart;
}

View file

@ -51,11 +51,12 @@
"@kbn/search-inference-endpoints",
"@kbn/security-plugin-types-common",
"@kbn/search-indices",
"@kbn/search-homepage",
"@kbn/core-http-server",
"@kbn/logging",
"@kbn/security-plugin-types-public",
"@kbn/deeplinks-search",
"@kbn/core-application-browser",
"@kbn/search-shared-ui",
"@kbn/search-shared-ui"
]
}

View file

@ -16,6 +16,9 @@ export function SvlSearchHomePageProvider({ getService }: FtrProviderContext) {
async expectToBeOnHomepage() {
expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/home');
},
async expectToBeOnStartpage() {
expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/start');
},
async expectToNotBeOnHomepage() {
expect(await browser.getCurrentUrl()).not.contain('/app/elasticsearch/home');
},
@ -47,11 +50,42 @@ export function SvlSearchHomePageProvider({ getService }: FtrProviderContext) {
await testSubjects.existOrFail('connectionDetailsEsUrl');
await testSubjects.existOrFail('connectionDetailsCloudIdSwitch');
},
async expectAPIKeyTabIsAvailable() {
await testSubjects.existOrFail('connectionDetailsTabBtn-apiKeys');
await testSubjects.click('connectionDetailsTabBtn-apiKeys');
await testSubjects.existOrFail('connectionDetailsApiKeyNameInput');
await testSubjects.existOrFail('connectionDetailsApiKeySubmitBtn');
async clickManageApiKeysLink() {
await testSubjects.existOrFail('manageApiKeysButton');
await testSubjects.click('manageApiKeysButton');
},
async expectToBeOnManageApiKeysPage() {
expect(await browser.getCurrentUrl()).contain('/app/management/security/api_keys');
},
async expectToBeOnUploadDataPage() {
expect(await browser.getCurrentUrl()).contain('ml/filedatavisualizer');
},
async expectToBeOnCustomerEngineerPage() {
expect(await browser.getCurrentUrl()).contain('contact/ce-help');
},
async expectToBeOnCreateIndexPage() {
expect(await browser.getCurrentUrl()).contain('app/elasticsearch/indices/create');
},
async expectToBeOnObservabilityPage() {
expect(await browser.getCurrentUrl()).contain('manage-data/ingest');
},
async expectToBeOnSpacesCreatePage() {
expect(await browser.getCurrentUrl()).contain('observability/start');
},
async expectToBeOnSearchLabsPage() {
expect(await browser.getCurrentUrl()).contain('search-labs');
},
async expectToBeOnNotebooksExamplesPage() {
expect(await browser.getCurrentUrl()).contain('search-labs/tutorials/examples');
},
async expectToBeOnGetStartedDocumentationPage() {
expect(await browser.getCurrentUrl()).contain('docs/solutions/search/get-started');
},
async expectToBeOnCommunityPage() {
expect(await browser.getCurrentUrl()).contain('community/');
},
async expectToBeOnGiveFeedbackPage() {
expect(await browser.getCurrentUrl()).contain('kibana/feedback');
},
async createApiKeyInFlyout(keyName: string) {
await testSubjects.existOrFail('connectionDetailsApiKeyNameInput');

View file

@ -50,5 +50,8 @@ export default createTestConfig({
searchInferenceEndpoints: {
pathname: '/app/elasticsearch/relevance/inference_endpoints',
},
searchHomepage: {
pathname: '/app/elasticsearch/home',
},
},
});

View file

@ -29,5 +29,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./ml'));
loadTestFile(require.resolve('./custom_role_access'));
loadTestFile(require.resolve('./inference_management'));
loadTestFile(require.resolve('./search_homepage'));
});
}

View file

@ -241,6 +241,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
);
await solutionNavigation.sidenav.expectOnlyDefinedLinks([
'search_project_nav',
'home',
'analyze',
'discover',
'dashboards',

View file

@ -7,7 +7,6 @@
import { FtrProviderContext } from '../../ftr_provider_context';
import { RoleCredentials } from '../../../shared/services';
import { testHasEmbeddedConsole } from './embedded_console';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
@ -16,77 +15,189 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'svlCommonNavigation',
'svlSearchHomePage',
'embeddedConsole',
'common',
]);
const svlUserManager = getService('svlUserManager');
const uiSettings = getService('uiSettings');
let roleAuthc: RoleCredentials;
const es = getService('es');
const esDeleteAllIndices = getService('esDeleteAllIndices');
const deleteAllTestIndices = async () => {
await esDeleteAllIndices(['test-*']);
};
const testSubjects = getService('testSubjects');
const HOMEPAGE_FF_UI_SETTING = 'searchHomepage:homepageEnabled';
describe('Search Homepage', function () {
this.tags('skipMKI');
before(async () => {
roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin');
// Enable Homepage Feature Flag
await uiSettings.setUiSetting(roleAuthc, HOMEPAGE_FF_UI_SETTING, true);
await pageObjects.svlCommonPage.loginAsViewer();
});
after(async () => {
if (!roleAuthc) return;
// Disable Homepage Feature Flag
await uiSettings.deleteUISetting(roleAuthc, HOMEPAGE_FF_UI_SETTING);
await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc);
});
it('has search homepage with Home sidenav', async () => {
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
await pageObjects.svlSearchHomePage.expectHomepageHeader();
// Navigate to another page
await pageObjects.svlCommonNavigation.sidenav.clickLink({
deepLinkId: 'serverlessConnectors',
describe('as admin', function () {
before(async () => {
await pageObjects.svlCommonPage.loginAsAdmin();
});
await pageObjects.svlSearchHomePage.expectToNotBeOnHomepage();
// Click Home in Side nav
await pageObjects.svlCommonNavigation.sidenav.clickLink({
deepLinkId: 'searchHomepage',
after(async () => {
await deleteAllTestIndices();
});
it('goes to the start page if there exists no index', async () => {
await pageObjects.svlSearchHomePage.expectToBeOnStartpage();
});
it('goes to the home page if there exists at least one index', async () => {
await es.indices.create({ index: 'test-my-index-001' });
await pageObjects.common.navigateToApp('searchHomepage');
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
});
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
});
it('has embedded dev console', async () => {
await testHasEmbeddedConsole(pageObjects);
describe('as developer', function () {
before(async () => {
await pageObjects.svlCommonPage.loginAsDeveloper();
});
after(async () => {
await deleteAllTestIndices();
});
it('goes to the start page if there exists no index', async () => {
await pageObjects.svlSearchHomePage.expectToBeOnStartpage();
});
it('goes to the home page if there exists at least one index', async () => {
await es.indices.create({ index: 'test-my-index-001' });
await pageObjects.common.navigateToApp('searchHomepage');
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
});
});
it('has console quickstart link on page', async () => {
await pageObjects.svlSearchHomePage.expectConsoleLinkExists();
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
await pageObjects.svlSearchHomePage.clickConsoleLink();
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen();
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
});
describe('as viewer', function () {
before(async () => {
roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin');
it('has endpoints link and flyout', async () => {
await pageObjects.svlSearchHomePage.expectEndpointsLinkExists();
await pageObjects.svlSearchHomePage.clickEndpointsLink();
await pageObjects.svlSearchHomePage.expectConnectionDetailsFlyoutToBeOpen();
await pageObjects.svlSearchHomePage.expectEndpointsTabIsAvailable();
await pageObjects.svlSearchHomePage.closeConnectionDetailsFlyout();
});
await pageObjects.svlCommonPage.loginAsViewer();
});
it('can create an API key', async () => {
await pageObjects.svlSearchHomePage.expectEndpointsLinkExists();
await pageObjects.svlSearchHomePage.clickEndpointsLink();
await pageObjects.svlSearchHomePage.expectConnectionDetailsFlyoutToBeOpen();
await pageObjects.svlSearchHomePage.expectAPIKeyTabIsAvailable();
await pageObjects.svlSearchHomePage.createApiKeyInFlyout('ftr-test-key');
await pageObjects.svlSearchHomePage.closeConnectionDetailsFlyout();
});
beforeEach(async () => {
await pageObjects.common.navigateToApp('searchHomepage');
});
it('shows the AI assistant', async () => {
await pageObjects.svlSearchHomePage.expectAIAssistantToExist();
after(async () => {
if (!roleAuthc) return;
await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc);
});
it('has search homepage with Home sidenav', async () => {
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
await pageObjects.svlSearchHomePage.expectHomepageHeader();
// Navigate to another page
await pageObjects.svlCommonNavigation.sidenav.clickLink({
deepLinkId: 'serverlessConnectors',
});
await pageObjects.svlSearchHomePage.expectToNotBeOnHomepage();
// Click Home in Side nav
await pageObjects.svlCommonNavigation.sidenav.clickLink({
deepLinkId: 'searchHomepage',
});
await pageObjects.svlSearchHomePage.expectToBeOnHomepage();
});
it('has embedded dev console', async () => {
await testHasEmbeddedConsole(pageObjects);
});
describe('Elasticsearch endpoint and API Keys', function () {
it('renders Elasticsearch endpoint with copy functionality', async () => {
await testSubjects.existOrFail('copyEndpointButton');
await testSubjects.existOrFail('endpointValueField');
});
it('renders API keys buttons and active badge correctly', async () => {
await testSubjects.existOrFail('createApiKeyButton');
await testSubjects.existOrFail('manageApiKeysButton');
await testSubjects.existOrFail('activeApiKeysBadge');
});
it('opens API keys management page on clicking Manage API Keys', async () => {
await pageObjects.svlSearchHomePage.clickManageApiKeysLink();
await pageObjects.svlSearchHomePage.expectToBeOnManageApiKeysPage();
});
});
describe('Connect To Elasticsearch Side Panel', function () {
it('renders the "Upload a file" card with copy link', async () => {
await testSubjects.existOrFail('uploadFileButton');
await testSubjects.click('uploadFileButton');
await pageObjects.svlSearchHomePage.expectToBeOnUploadDataPage();
});
it('renders the Customer Engineer Request Form link', async () => {
await testSubjects.existOrFail('customerEngineerRequestFormLink');
await testSubjects.click('customerEngineerRequestFormLink');
await pageObjects.svlSearchHomePage.expectToBeOnCustomerEngineerPage();
});
});
describe('AI search capabilities', function () {
it('renders Semantic Search content', async () => {
await testSubjects.existOrFail('aiSearchCapabilities-item-semantic');
await testSubjects.existOrFail('createSemanticOptimizedIndexButton');
await testSubjects.click('createSemanticOptimizedIndexButton');
await pageObjects.svlSearchHomePage.expectToBeOnCreateIndexPage();
});
it('renders Keyword Search content', async () => {
await testSubjects.scrollIntoView('aiSearchCapabilities-item-keyword');
await testSubjects.existOrFail('aiSearchCapabilities-item-keyword');
await testSubjects.click('aiSearchCapabilities-item-keyword');
await testSubjects.existOrFail('createKeywordIndexButton');
await testSubjects.click('createKeywordIndexButton');
await pageObjects.svlSearchHomePage.expectToBeOnCreateIndexPage();
});
});
describe('Alternate Solutions', function () {
it('renders Observability content', async () => {
await testSubjects.existOrFail('observabilitySection');
await testSubjects.existOrFail('exploreLogstashAndBeatsLink');
await testSubjects.click('exploreLogstashAndBeatsLink');
await pageObjects.svlSearchHomePage.expectToBeOnObservabilityPage();
});
});
describe('Dive deeper with Elasticsearch', function () {
it('renders Search labs content', async () => {
await testSubjects.existOrFail('searchLabsSection');
await testSubjects.existOrFail('searchLabsButton');
await testSubjects.click('searchLabsButton');
await pageObjects.svlSearchHomePage.expectToBeOnSearchLabsPage();
});
it('renders Open Notebooks content', async () => {
await testSubjects.existOrFail('pythonNotebooksSection');
await testSubjects.existOrFail('openNotebooksButton');
await testSubjects.click('openNotebooksButton');
await pageObjects.svlSearchHomePage.expectToBeOnNotebooksExamplesPage();
});
it('renders Elasticsearch Documentation content', async () => {
await testSubjects.existOrFail('elasticsearchDocumentationSection');
await testSubjects.existOrFail('viewDocumentationButton');
await testSubjects.click('viewDocumentationButton');
await pageObjects.svlSearchHomePage.expectToBeOnGetStartedDocumentationPage();
});
});
describe('Footer content', function () {
it('displays the community link', async () => {
await testSubjects.existOrFail('elasticCommunityLink');
await testSubjects.click('elasticCommunityLink');
await pageObjects.svlSearchHomePage.expectToBeOnCommunityPage();
});
it('displays the feedbacks link', async () => {
await testSubjects.existOrFail('giveFeedbackLink');
await testSubjects.click('giveFeedbackLink');
await pageObjects.svlSearchHomePage.expectToBeOnGiveFeedbackPage();
});
});
});
});
}