Skip to content

Commit

Permalink
List pipelines (#62785)
Browse files Browse the repository at this point in the history
* First iteration of ingest table

* Add action placeholders

* Refactor list and address PR feedback

Refactored the list into smaller pieces and assemble in main.tsx

Also addressed feedback on copy, removed unused notifications dep

* WiP on flyout

Showing name in title

* Add reload button

* Finish first version of flyout

* Slight update to copy

* `delete` -> `edit`

* Address PR feedback

Copy and a11y updates

* Add on failure JSON to flyout if it is available

* Add details json block file and remove ununsed import

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
jloleysens and elasticmachine authored Apr 9, 2020
1 parent ac0ac2b commit 699ebef
Show file tree
Hide file tree
Showing 9 changed files with 423 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiTitle,
EuiDescriptionList,
EuiSpacer,
EuiFlyoutFooter,
EuiFlexGroup,
EuiFlexItem,
EuiButtonEmpty,
} from '@elastic/eui';
import { Pipeline } from '../../../../common/types';

import { PipelineDetailsJsonBlock } from './details_json_block';

export interface Props {
pipeline: Pipeline;
onEditClick: () => void;
onDeleteClick: () => void;
onClose: () => void;
}

export const PipelineDetails: FunctionComponent<Props> = ({
pipeline,
onClose,
onEditClick,
onDeleteClick,
}) => {
const descriptionListItems = [
{
title: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.descriptionTitle', {
defaultMessage: 'Description',
}),
description: pipeline.description ?? '',
},
];

if (pipeline.version) {
descriptionListItems.push({
title: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.versionTitle', {
defaultMessage: 'Version',
}),
description: String(pipeline.version),
});
}

return (
<EuiFlyout
onClose={onClose}
aria-labelledby="pipelineDetailsFlyoutTitle"
size="m"
maxWidth={550}
>
<EuiFlyoutHeader>
<EuiTitle id="pipelineDetailsFlyoutTitle">
<h2>{pipeline.name}</h2>
</EuiTitle>
</EuiFlyoutHeader>

<EuiFlyoutBody>
<EuiDescriptionList listItems={descriptionListItems} />

<EuiSpacer size="m" />

<PipelineDetailsJsonBlock
htmlForId="pipelineDetailsProcessorsJson"
label={i18n.translate('xpack.ingestPipelines.list.pipelineDetails.processorsTitle', {
defaultMessage: 'Processors JSON',
})}
json={pipeline.processors}
/>

{/* On Failure Processor JSON */}
{pipeline.onFailure?.length && (
<>
<EuiSpacer size="m" />
<PipelineDetailsJsonBlock
htmlForId="pipelineDetailsOnFailureProcessorsJson"
label={i18n.translate(
'xpack.ingestPipelines.list.pipelineDetails.failureProcessorsTitle',
{
defaultMessage: 'On failure processors JSON',
}
)}
json={pipeline.onFailure}
/>
</>
)}
{/* End On Failure Processor JSON */}
</EuiFlyoutBody>

<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={onClose} flush="left">
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.closeButtonLabel', {
defaultMessage: 'Close',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexGroup gutterSize="none" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onEditClick}>
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.editButtonLabel', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="danger" onClick={onDeleteClick}>
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteButtonLabel', {
defaultMessage: 'Delete',
})}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
);
};
Original file line number Diff line number Diff line change
@@ -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 React, { FunctionComponent } from 'react';
import { EuiCodeBlock, EuiText } from '@elastic/eui';

export interface Props {
htmlForId: string;
label: string;
json: Record<string, any>;
}

export const PipelineDetailsJsonBlock: FunctionComponent<Props> = ({ label, htmlForId, json }) => (
<>
<EuiText size="s">
<label htmlFor={htmlForId}>
<b>{label}</b>
</label>
</EuiText>
<EuiCodeBlock paddingSize="s" id={htmlForId} language="json" overflowHeight={200} isCopyable>
{JSON.stringify(json, null, 2)}
</EuiCodeBlock>
</>
);
Original file line number Diff line number Diff line change
@@ -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 React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';

interface Props {
onClick: () => void;
}

export const EmptyList: FunctionComponent<Props> = ({ onClick }) => (
<EuiEmptyPrompt
iconType="managementApp"
title={
<h2>
{i18n.translate('xpack.ingestPipelines.list.table.emptyPromptTitle', {
defaultMessage: 'Create your first pipeline',
})}
</h2>
}
actions={
<EuiButton onClick={onClick}>
{i18n.translate('xpack.ingestPipelines.list.table.emptyPrompt.createButtonLabel', {
defaultMessage: 'Create pipeline',
})}
</EuiButton>
}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { PipelinesList } from './pipelines_list';
export { PipelinesList } from './main';
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import {
EuiPageBody,
EuiPageContent,
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiButtonEmpty,
EuiCallOut,
} from '@elastic/eui';

import { EuiSpacer, EuiText } from '@elastic/eui';

import { Pipeline } from '../../../../common/types';
import { useKibana, SectionLoading } from '../../../shared_imports';
import { UIM_PIPELINES_LIST_LOAD } from '../../constants';

import { EmptyList } from './empty_list';
import { PipelineTable } from './table';
import { PipelineDetails } from './details';

export const PipelinesList: React.FunctionComponent = () => {
const { services } = useKibana();

const [selectedPipeline, setSelectedPipeline] = useState<Pipeline | undefined>(undefined);

// Track component loaded
useEffect(() => {
services.metric.trackUiMetric(UIM_PIPELINES_LIST_LOAD);
}, [services.metric]);

const { data, isLoading, error, sendRequest } = services.api.useLoadPipelines();

let content: React.ReactNode;

if (isLoading) {
content = (
<SectionLoading>
<FormattedMessage
id="xpack.ingestPipelines.list.loadingMessage"
defaultMessage="Loading pipelines..."
/>
</SectionLoading>
);
} else if (data?.length) {
content = (
<PipelineTable
onReloadClick={() => {
sendRequest();
}}
onEditPipelineClick={() => {}}
onDeletePipelineClick={() => {}}
onViewPipelineClick={setSelectedPipeline}
pipelines={data}
/>
);
} else {
content = <EmptyList onClick={() => {}} />;
}

return (
<>
<EuiPageBody>
<EuiPageContent>
<EuiTitle size="l">
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<h1 data-test-subj="appTitle">
<FormattedMessage
id="xpack.ingestPipelines.list.listTitle"
defaultMessage="Ingest Pipelines"
/>
</h1>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
href={services.documentation.getIngestNodeUrl()}
target="_blank"
iconType="help"
>
<FormattedMessage
id="xpack.ingestPipelines.list.pipelinesDocsLinkText"
defaultMessage="Ingest Pipelines docs"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
<EuiSpacer size="s" />
<EuiTitle size="s">
<EuiText color="subdued">
<FormattedMessage
id="xpack.ingestPipelines.list.pipelinesDescription"
defaultMessage="Use ingest node pipelines to pre-process documents before indexing."
/>
</EuiText>
</EuiTitle>
<EuiSpacer size="m" />
{/* Error call out or pipeline table */}
{error ? (
<EuiCallOut
iconType="faceSad"
color="danger"
title={i18n.translate('xpack.ingestPipelines.list.loadErrorTitle', {
defaultMessage: 'Cannot load pipelines, please refresh the page to try again.',
})}
/>
) : (
content
)}
</EuiPageContent>
</EuiPageBody>
{selectedPipeline && (
<PipelineDetails
pipeline={selectedPipeline}
onClose={() => setSelectedPipeline(undefined)}
onDeleteClick={() => {}}
onEditClick={() => {}}
/>
)}
</>
);
};
Loading

0 comments on commit 699ebef

Please sign in to comment.