mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Enterprise Search] Engine overview layout stub (#83756)
* Set up Overview file * Finish Overview page logic, stub out empty/metric views * Stub in basic empty engine overview - Minus document creation button & API code example * Stub out EngineOverviewMetrics and unavailable empty prompt * Stub out EngineOverMetrics components (stats, charts, logs) * [Refactor] Pull out some document creation i18n strings to constants - They're repeated/reused by the DocumentCreationPopover component * PR feedback: Drop the regex * PR feedback: RecentLogs -> RecentApiLogs * PR feedback: Copy * PR feedback: Copy, sentence-casing * I forgot to rebase against my own PR :dead_inside:
This commit is contained in:
parent
235cef7d14
commit
44eba4f953
24 changed files with 841 additions and 8 deletions
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const TOTAL_DOCUMENTS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.analytics.totalDocuments',
|
||||
{ defaultMessage: 'Total documents' }
|
||||
);
|
||||
|
||||
export const TOTAL_API_OPERATIONS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.analytics.totalApiOperations',
|
||||
{ defaultMessage: 'Total API operations' }
|
||||
);
|
||||
|
||||
export const TOTAL_QUERIES = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueries',
|
||||
{ defaultMessage: 'Total queries' }
|
||||
);
|
||||
|
||||
export const TOTAL_CLICKS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks',
|
||||
{ defaultMessage: 'Total clicks' }
|
||||
);
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const RECENT_API_EVENTS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.apiLogs.recent',
|
||||
{ defaultMessage: 'Recent API events' }
|
||||
);
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiCode, EuiLink } from '@elastic/eui';
|
||||
|
||||
import { DOCS_PREFIX } from '../../routes';
|
||||
|
||||
export const DOCUMENT_CREATION_DESCRIPTION = (
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.appSearch.engine.documentCreation.description"
|
||||
defaultMessage="There are three ways to send documents to your engine for indexing. You can paste raw JSON, upload a {jsonCode} file, or {postCode} to the {documentsApiLink} endpoint. Click on your choice below or see {apiStrong}."
|
||||
values={{
|
||||
jsonCode: <EuiCode>.json</EuiCode>,
|
||||
postCode: <EuiCode>POST</EuiCode>,
|
||||
documentsApiLink: (
|
||||
<EuiLink target="_blank" href={`${DOCS_PREFIX}/indexing-documents-guide.html`}>
|
||||
documents API
|
||||
</EuiLink>
|
||||
),
|
||||
apiStrong: <strong>Indexing by API</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DOCUMENT_API_INDEXING_TITLE = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.documentCreation.api.title',
|
||||
{ defaultMessage: 'Indexing by API' }
|
||||
);
|
||||
|
||||
export const DOCUMENT_API_INDEXING_DESCRIPTION = (
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.appSearch.engine.documentCreation.api.description"
|
||||
defaultMessage="The {documentsApiLink} can be used to add new documents to your engine, update documents, retrieve documents by id, and delete documents. There are a variety of {clientLibrariesLink} to help you get started."
|
||||
values={{
|
||||
documentsApiLink: (
|
||||
<EuiLink target="_blank" href={`${DOCS_PREFIX}/indexing-documents-guide.html`}>
|
||||
documents API
|
||||
</EuiLink>
|
||||
),
|
||||
clientLibrariesLink: (
|
||||
<EuiLink target="_blank" href={`${DOCS_PREFIX}/api-clients.html`}>
|
||||
client libraries
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
|
@ -9,10 +9,6 @@ import { i18n } from '@kbn/i18n';
|
|||
// TODO: It's very likely that we'll move these i18n constants to their respective component
|
||||
// folders once those are migrated over. This is a temporary way of DRYing them out for now.
|
||||
|
||||
export const OVERVIEW_TITLE = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.title',
|
||||
{ defaultMessage: 'Overview' }
|
||||
);
|
||||
export const ANALYTICS_TITLE = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.analytics.title',
|
||||
{ defaultMessage: 'Analytics' }
|
||||
|
|
|
@ -28,8 +28,8 @@ import {
|
|||
} from '../../routes';
|
||||
import { getAppSearchUrl } from '../../../shared/enterprise_search_url';
|
||||
import { ENGINES_TITLE } from '../engines';
|
||||
import { OVERVIEW_TITLE } from '../engine_overview';
|
||||
import {
|
||||
OVERVIEW_TITLE,
|
||||
ANALYTICS_TITLE,
|
||||
DOCUMENTS_TITLE,
|
||||
SCHEMA_TITLE,
|
||||
|
|
|
@ -18,6 +18,7 @@ jest.mock('../../../shared/flash_messages', () => ({
|
|||
import { setQueuedErrorMessage } from '../../../shared/flash_messages';
|
||||
|
||||
import { Loading } from '../../../shared/loading';
|
||||
import { EngineOverview } from '../engine_overview';
|
||||
|
||||
import { EngineRouter } from './';
|
||||
|
||||
|
@ -71,7 +72,7 @@ describe('EngineRouter', () => {
|
|||
const wrapper = shallow(<EngineRouter />);
|
||||
|
||||
expect(wrapper.find(Switch)).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="EngineOverviewTODO"]')).toHaveLength(1);
|
||||
expect(wrapper.find(EngineOverview)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders an analytics view', () => {
|
||||
|
|
|
@ -31,8 +31,8 @@ import {
|
|||
// ENGINE_API_LOGS_PATH,
|
||||
} from '../../routes';
|
||||
import { ENGINES_TITLE } from '../engines';
|
||||
import { OVERVIEW_TITLE } from '../engine_overview';
|
||||
import {
|
||||
OVERVIEW_TITLE,
|
||||
ANALYTICS_TITLE,
|
||||
// DOCUMENTS_TITLE,
|
||||
// SCHEMA_TITLE,
|
||||
|
@ -46,6 +46,7 @@ import {
|
|||
} from './constants';
|
||||
|
||||
import { Loading } from '../../../shared/loading';
|
||||
import { EngineOverview } from '../engine_overview';
|
||||
|
||||
import { EngineLogic } from './';
|
||||
|
||||
|
@ -100,7 +101,7 @@ export const EngineRouter: React.FC = () => {
|
|||
)}
|
||||
<Route>
|
||||
<SetPageChrome trail={[...engineBreadcrumb, OVERVIEW_TITLE]} />
|
||||
<div data-test-subj="EngineOverviewTODO">Overview</div>
|
||||
<EngineOverview />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { UnavailablePrompt } from './unavailable_prompt';
|
||||
export { TotalStats } from './total_stats';
|
||||
export { TotalCharts } from './total_charts';
|
||||
export { RecentApiLogs } from './recent_api_logs';
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { setMockValues } from '../../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
|
||||
import { EuiButtonTo } from '../../../../shared/react_router_helpers';
|
||||
|
||||
import { RecentApiLogs } from './recent_api_logs';
|
||||
|
||||
describe('RecentApiLogs', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues({
|
||||
engineName: 'some-engine',
|
||||
});
|
||||
wrapper = shallow(<RecentApiLogs />);
|
||||
});
|
||||
|
||||
it('renders the recent API logs table', () => {
|
||||
expect(wrapper.find('h2').text()).toEqual('Recent API events');
|
||||
expect(wrapper.find(EuiButtonTo).prop('to')).toEqual('/engines/some-engine/api-logs');
|
||||
// TODO: expect(wrapper.find(ApiLogsTable)).toHaveLength(1)
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import {
|
||||
EuiPageContent,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContentHeaderSection,
|
||||
EuiPageContentBody,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { EuiButtonTo } from '../../../../shared/react_router_helpers';
|
||||
|
||||
import { ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes';
|
||||
import { RECENT_API_EVENTS } from '../../api_logs/constants';
|
||||
import { VIEW_API_LOGS } from '../constants';
|
||||
|
||||
import { EngineLogic } from '../../engine';
|
||||
|
||||
export const RecentApiLogs: React.FC = () => {
|
||||
const { engineName } = useValues(EngineLogic);
|
||||
const engineRoute = getEngineRoute(engineName);
|
||||
|
||||
return (
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader responsive={false}>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiTitle size="xs">
|
||||
<h2>{RECENT_API_EVENTS}</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeaderSection>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiButtonTo to={engineRoute + ENGINE_API_LOGS_PATH} size="s">
|
||||
{VIEW_API_LOGS}
|
||||
</EuiButtonTo>
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
TODO: API Logs Table
|
||||
{/* <ApiLogsTable hidePagination={true} /> */}
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { setMockValues } from '../../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
|
||||
import { EuiButtonTo } from '../../../../shared/react_router_helpers';
|
||||
|
||||
import { TotalCharts } from './total_charts';
|
||||
|
||||
describe('TotalCharts', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues({
|
||||
engineName: 'some-engine',
|
||||
startDate: '1970-01-01',
|
||||
endDate: '1970-01-08',
|
||||
queriesPerDay: [0, 1, 2, 3, 5, 10, 50],
|
||||
operationsPerDay: [0, 0, 0, 0, 0, 0, 0],
|
||||
});
|
||||
wrapper = shallow(<TotalCharts />);
|
||||
});
|
||||
|
||||
it('renders the total queries chart', () => {
|
||||
const chart = wrapper.find('[data-test-subj="TotalQueriesChart"]');
|
||||
|
||||
expect(chart.find('h2').text()).toEqual('Total queries');
|
||||
expect(chart.find(EuiButtonTo).prop('to')).toEqual('/engines/some-engine/analytics');
|
||||
// TODO: find chart component
|
||||
});
|
||||
|
||||
it('renders the total API operations chart', () => {
|
||||
const chart = wrapper.find('[data-test-subj="TotalApiOperationsChart"]');
|
||||
|
||||
expect(chart.find('h2').text()).toEqual('Total API operations');
|
||||
expect(chart.find(EuiButtonTo).prop('to')).toEqual('/engines/some-engine/api-logs');
|
||||
// TODO: find chart component
|
||||
});
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPageContent,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContentHeaderSection,
|
||||
EuiPageContentBody,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { EuiButtonTo } from '../../../../shared/react_router_helpers';
|
||||
|
||||
import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes';
|
||||
import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants';
|
||||
import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants';
|
||||
|
||||
import { EngineLogic } from '../../engine';
|
||||
import { EngineOverviewLogic } from '../';
|
||||
|
||||
export const TotalCharts: React.FC = () => {
|
||||
const { engineName } = useValues(EngineLogic);
|
||||
const engineRoute = getEngineRoute(engineName);
|
||||
|
||||
const {
|
||||
// startDate,
|
||||
// endDate,
|
||||
// queriesPerDay,
|
||||
// operationsPerDay,
|
||||
} = useValues(EngineOverviewLogic);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiPageContent data-test-subj="TotalQueriesChart">
|
||||
<EuiPageContentHeader responsive={false}>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiTitle size="xs">
|
||||
<h2>{TOTAL_QUERIES}</h2>
|
||||
</EuiTitle>
|
||||
<EuiText size="s" color="subdued">
|
||||
{LAST_7_DAYS}
|
||||
</EuiText>
|
||||
</EuiPageContentHeaderSection>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiButtonTo to={engineRoute + ENGINE_ANALYTICS_PATH} size="s">
|
||||
{VIEW_ANALYTICS}
|
||||
</EuiButtonTo>
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
TODO: Analytics chart
|
||||
{/* <EngineAnalytics
|
||||
data={[queriesPerDay]}
|
||||
startDate={new Date(startDate)}
|
||||
endDate={new Date(endDate)}
|
||||
/> */}
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPageContent data-test-subj="TotalApiOperationsChart">
|
||||
<EuiPageContentHeader responsive={false}>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiTitle size="xs">
|
||||
<h2>{TOTAL_API_OPERATIONS}</h2>
|
||||
</EuiTitle>
|
||||
<EuiText size="s" color="subdued">
|
||||
{LAST_7_DAYS}
|
||||
</EuiText>
|
||||
</EuiPageContentHeaderSection>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiButtonTo to={engineRoute + ENGINE_API_LOGS_PATH} size="s">
|
||||
{VIEW_API_LOGS}
|
||||
</EuiButtonTo>
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
TODO: API Logs chart
|
||||
{/* <EngineAnalytics
|
||||
data={[operationsPerDay]}
|
||||
startDate={new Date(startDate)}
|
||||
endDate={new Date(endDate)}
|
||||
/> */}
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { setMockValues } from '../../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { EuiStat } from '@elastic/eui';
|
||||
|
||||
import { TotalStats } from './total_stats';
|
||||
|
||||
describe('TotalStats', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues({
|
||||
totalQueries: 11,
|
||||
documentCount: 22,
|
||||
totalClicks: 33,
|
||||
});
|
||||
wrapper = shallow(<TotalStats />);
|
||||
});
|
||||
|
||||
it('renders the total queries stat', () => {
|
||||
expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1);
|
||||
|
||||
const card = wrapper.find(EuiStat).at(0);
|
||||
expect(card.prop('title')).toEqual(11);
|
||||
expect(card.prop('description')).toEqual('Total queries');
|
||||
});
|
||||
|
||||
it('renders the total documents stat', () => {
|
||||
expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1);
|
||||
|
||||
const card = wrapper.find(EuiStat).at(1);
|
||||
expect(card.prop('title')).toEqual(22);
|
||||
expect(card.prop('description')).toEqual('Total documents');
|
||||
});
|
||||
|
||||
it('renders the total clicks stat', () => {
|
||||
expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1);
|
||||
|
||||
const card = wrapper.find(EuiStat).at(2);
|
||||
expect(card.prop('title')).toEqual(33);
|
||||
expect(card.prop('description')).toEqual('Total clicks');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useValues } from 'kea';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui';
|
||||
|
||||
import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants';
|
||||
|
||||
import { EngineOverviewLogic } from '../';
|
||||
|
||||
export const TotalStats: React.FC = () => {
|
||||
const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="TotalQueriesCard">
|
||||
<EuiStat title={totalQueries} description={TOTAL_QUERIES} titleColor="primary" />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="TotalDocumentsCard">
|
||||
<EuiStat title={documentCount} description={TOTAL_DOCUMENTS} titleColor="primary" />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="TotalClicksCard">
|
||||
<EuiStat title={totalClicks} description={TOTAL_CLICKS} titleColor="primary" />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
||||
import { UnavailablePrompt } from './unavailable_prompt';
|
||||
|
||||
describe('UnavailablePrompt', () => {
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<UnavailablePrompt />);
|
||||
expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
||||
export const UnavailablePrompt: React.FC = () => (
|
||||
<EuiEmptyPrompt
|
||||
iconType="clock"
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableTitle', {
|
||||
defaultMessage: 'Dashboard metrics are currently unavailable',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<p>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableBody', {
|
||||
defaultMessage: 'Please try again in a few minutes.',
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
);
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const OVERVIEW_TITLE = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.title',
|
||||
{ defaultMessage: 'Overview' }
|
||||
);
|
||||
|
||||
export const VIEW_ANALYTICS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.analyticsLink',
|
||||
{ defaultMessage: 'View analytics' }
|
||||
);
|
||||
|
||||
export const VIEW_API_LOGS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.apiLogsLink',
|
||||
{ defaultMessage: 'View API logs' }
|
||||
);
|
||||
|
||||
export const LAST_7_DAYS = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.chartDuration',
|
||||
{ defaultMessage: 'Last 7 days' }
|
||||
);
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import '../../../__mocks__/shallow_useeffect.mock';
|
||||
import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { Loading } from '../../../shared/loading';
|
||||
import { EmptyEngineOverview } from './engine_overview_empty';
|
||||
import { EngineOverviewMetrics } from './engine_overview_metrics';
|
||||
import { EngineOverview } from './';
|
||||
|
||||
describe('EngineOverview', () => {
|
||||
const values = {
|
||||
dataLoading: false,
|
||||
documentCount: 0,
|
||||
myRole: {},
|
||||
isMetaEngine: false,
|
||||
};
|
||||
const actions = {
|
||||
pollForOverviewMetrics: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues(values);
|
||||
setMockActions(actions);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find('[data-test-subj="EngineOverview"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('initializes data on mount', () => {
|
||||
shallow(<EngineOverview />);
|
||||
expect(actions.pollForOverviewMetrics).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('renders a loading component if async data is still loading', () => {
|
||||
setMockValues({ ...values, dataLoading: true });
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('EmptyEngineOverview', () => {
|
||||
it('renders when the engine has no documents & the user can add documents', () => {
|
||||
const myRole = { canManageEngineDocuments: true, canViewEngineCredentials: true };
|
||||
setMockValues({ ...values, myRole, documentCount: 0 });
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find(EmptyEngineOverview)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EngineOverviewMetrics', () => {
|
||||
it('renders when the engine has documents', () => {
|
||||
setMockValues({ ...values, documentCount: 1 });
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders when the user does not have the ability to add documents', () => {
|
||||
const myRole = { canManageEngineDocuments: false, canViewEngineCredentials: false };
|
||||
setMockValues({ ...values, myRole });
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('always renders for meta engines', () => {
|
||||
setMockValues({ ...values, isMetaEngine: true });
|
||||
const wrapper = shallow(<EngineOverview />);
|
||||
expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { useActions, useValues } from 'kea';
|
||||
|
||||
import { AppLogic } from '../../app_logic';
|
||||
import { EngineLogic } from '../engine';
|
||||
import { Loading } from '../../../shared/loading';
|
||||
|
||||
import { EngineOverviewLogic } from './';
|
||||
import { EmptyEngineOverview } from './engine_overview_empty';
|
||||
import { EngineOverviewMetrics } from './engine_overview_metrics';
|
||||
|
||||
export const EngineOverview: React.FC = () => {
|
||||
const {
|
||||
myRole: { canManageEngineDocuments, canViewEngineCredentials },
|
||||
} = useValues(AppLogic);
|
||||
const { isMetaEngine } = useValues(EngineLogic);
|
||||
|
||||
const { pollForOverviewMetrics } = useActions(EngineOverviewLogic);
|
||||
const { dataLoading, documentCount } = useValues(EngineOverviewLogic);
|
||||
|
||||
useEffect(() => {
|
||||
pollForOverviewMetrics();
|
||||
}, []);
|
||||
|
||||
if (dataLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const engineHasDocuments = documentCount > 0;
|
||||
const canAddDocuments = canManageEngineDocuments && canViewEngineCredentials;
|
||||
const showEngineOverview = engineHasDocuments || !canAddDocuments || isMetaEngine;
|
||||
|
||||
return (
|
||||
<div data-test-subj="EngineOverview">
|
||||
{showEngineOverview ? <EngineOverviewMetrics /> : <EmptyEngineOverview />}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import '../../../__mocks__/enterprise_search_url.mock';
|
||||
import { setMockValues } from '../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { CURRENT_MAJOR_VERSION } from '../../../../../common/version';
|
||||
|
||||
import { EmptyEngineOverview } from './engine_overview_empty';
|
||||
|
||||
describe('EmptyEngineOverview', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues({
|
||||
engineName: 'empty-engine',
|
||||
});
|
||||
wrapper = shallow(<EmptyEngineOverview />);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.find('h1').text()).toEqual('Engine setup');
|
||||
expect(wrapper.find('h2').text()).toEqual('Setting up the “empty-engine” engine');
|
||||
expect(wrapper.find('h3').text()).toEqual('Indexing by API');
|
||||
});
|
||||
|
||||
it('renders correctly versioned documentation URLs', () => {
|
||||
expect(wrapper.find(EuiButton).prop('href')).toEqual(
|
||||
`https://www.elastic.co/guide/en/app-search/${CURRENT_MAJOR_VERSION}/index.html`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiPageContent,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContentBody,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiButton,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { EngineLogic } from '../engine';
|
||||
|
||||
import { DOCS_PREFIX } from '../../routes';
|
||||
import {
|
||||
DOCUMENT_CREATION_DESCRIPTION,
|
||||
DOCUMENT_API_INDEXING_TITLE,
|
||||
DOCUMENT_API_INDEXING_DESCRIPTION,
|
||||
} from '../document_creation/constants';
|
||||
// TODO
|
||||
// import { DocumentCreationButtons, CodeExample } from '../document_creation'
|
||||
|
||||
export const EmptyEngineOverview: React.FC = () => {
|
||||
const { engineName } = useValues(EngineLogic);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.heading', {
|
||||
defaultMessage: 'Engine setup',
|
||||
})}
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiButton href={`${DOCS_PREFIX}/index.html`} target="_blank">
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.overview.empty.headingAction',
|
||||
{ defaultMessage: 'View documentation' }
|
||||
)}
|
||||
</EuiButton>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.subheading', {
|
||||
defaultMessage: 'Setting up the “{engineName}” engine',
|
||||
values: { engineName },
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText color="subdued">
|
||||
<p>{DOCUMENT_CREATION_DESCRIPTION}</p>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
{/* TODO: <DocumentCreationButtons /> */}
|
||||
</EuiPageContentBody>
|
||||
|
||||
<EuiPageContentHeader>
|
||||
<EuiTitle>
|
||||
<h3>{DOCUMENT_API_INDEXING_TITLE}</h3>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText color="subdued">
|
||||
<p>{DOCUMENT_API_INDEXING_DESCRIPTION}</p>
|
||||
<p>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.apiExample', {
|
||||
defaultMessage:
|
||||
'To see the API in action, you can experiment with the example request below using a command line or a client library.',
|
||||
})}
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
{/* <DocumentApiCodeExample engineName={engineName} apiKey={apiKey} /> */}
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { setMockValues } from '../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components';
|
||||
import { EngineOverviewMetrics } from './engine_overview_metrics';
|
||||
|
||||
describe('EngineOverviewMetrics', () => {
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<EngineOverviewMetrics />);
|
||||
expect(wrapper.find('h1').text()).toEqual('Engine overview');
|
||||
});
|
||||
|
||||
it('renders an unavailable prompt if engine data is still indexing', () => {
|
||||
setMockValues({ apiLogsUnavailable: true });
|
||||
const wrapper = shallow(<EngineOverviewMetrics />);
|
||||
expect(wrapper.find(UnavailablePrompt)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders total stats, charts, and recent logs when metrics are available', () => {
|
||||
setMockValues({ apiLogsUnavailable: false });
|
||||
const wrapper = shallow(<EngineOverviewMetrics />);
|
||||
expect(wrapper.find(TotalStats)).toHaveLength(1);
|
||||
expect(wrapper.find(TotalCharts)).toHaveLength(1);
|
||||
expect(wrapper.find(RecentApiLogs)).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiPageHeader, EuiTitle, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { EngineOverviewLogic } from './';
|
||||
|
||||
import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components';
|
||||
|
||||
export const EngineOverviewMetrics: React.FC = () => {
|
||||
const { apiLogsUnavailable } = useValues(EngineOverviewLogic);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPageHeader>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.heading', {
|
||||
defaultMessage: 'Engine overview',
|
||||
})}
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeader>
|
||||
{apiLogsUnavailable ? (
|
||||
<UnavailablePrompt />
|
||||
) : (
|
||||
<>
|
||||
<TotalStats />
|
||||
<EuiSpacer size="xl" />
|
||||
<TotalCharts />
|
||||
<EuiSpacer size="xl" />
|
||||
<RecentApiLogs />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,3 +5,5 @@
|
|||
*/
|
||||
|
||||
export { EngineOverviewLogic } from './engine_overview_logic';
|
||||
export { EngineOverview } from './engine_overview';
|
||||
export { OVERVIEW_TITLE } from './constants';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue