mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Beats Management translations (#25228)
* Beats Management translations * Fix merge issues * Revert translations for config * Fix tslint error * Add map for config translations * Use Map * Fix tslint * Update i18n ids * Fix tslint * Remove commented code * Revert translation of Filebeat and Metricbeat because they should be translated * Update message id
This commit is contained in:
parent
64e26c4a3f
commit
64081cdcc7
35 changed files with 1295 additions and 273 deletions
|
@ -12,13 +12,11 @@
|
|||
"tableVis": "src/core_plugins/table_vis",
|
||||
"regionMap": "src/core_plugins/region_map",
|
||||
"statusPage": "src/core_plugins/status_page",
|
||||
"tagCloud": "src/core_plugins/tagcloud",
|
||||
"tagCloud": "src/core_plugins/tagcloud",
|
||||
"tileMap": "src/core_plugins/tile_map",
|
||||
"timelion": "src/core_plugins/timelion",
|
||||
"tsvb": "src/core_plugins/metrics",
|
||||
"tagCloud": "src/core_plugins/tagcloud",
|
||||
"tsvb": "src/core_plugins/metrics",
|
||||
"xpack.beatsManagement": "x-pack/plugins/beats_management",
|
||||
"xpack.graph": "x-pack/plugins/graph",
|
||||
"xpack.grokDebugger": "x-pack/plugins/grokdebugger",
|
||||
"xpack.idxMgmt": "x-pack/plugins/index_management",
|
||||
|
|
|
@ -6,25 +6,30 @@
|
|||
|
||||
// @ts-ignore
|
||||
import { EuiBasicTable, EuiLink } from '@elastic/eui';
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { ConfigurationBlock } from '../../common/domain_types';
|
||||
import { supportedConfigs } from '../config_schemas';
|
||||
import { getSupportedConfig } from '../config_schemas_translations_map';
|
||||
|
||||
interface ComponentProps {
|
||||
configs: ConfigurationBlock[];
|
||||
onConfigClick: (action: 'edit' | 'delete', config: ConfigurationBlock) => any;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
export const ConfigList: React.SFC<ComponentProps> = props => (
|
||||
const ConfigListUi: React.SFC<ComponentProps> = props => (
|
||||
<EuiBasicTable
|
||||
items={props.configs || []}
|
||||
columns={[
|
||||
{
|
||||
field: 'type',
|
||||
name: 'Type',
|
||||
name: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.typeColumnName',
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
truncateText: false,
|
||||
render: (value: string, config: ConfigurationBlock) => {
|
||||
const type = supportedConfigs.find((sc: any) => sc.value === config.type);
|
||||
const type = getSupportedConfig().find((sc: any) => sc.value === config.type);
|
||||
|
||||
return (
|
||||
<EuiLink onClick={() => props.onConfigClick('edit', config)}>
|
||||
|
@ -35,22 +40,43 @@ export const ConfigList: React.SFC<ComponentProps> = props => (
|
|||
},
|
||||
{
|
||||
field: 'module',
|
||||
name: 'Module',
|
||||
name: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.moduleColumnName',
|
||||
defaultMessage: 'Module',
|
||||
}),
|
||||
truncateText: false,
|
||||
render: (value: string) => {
|
||||
return value || 'N/A';
|
||||
return (
|
||||
value ||
|
||||
props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.moduleColumn.notAvailibaleLabel',
|
||||
defaultMessage: 'N/A',
|
||||
})
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
name: 'Description',
|
||||
name: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.descriptionColumnName',
|
||||
defaultMessage: 'Description',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'Actions',
|
||||
name: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.actionsColumnName',
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
{
|
||||
name: 'Remove',
|
||||
description: 'Remove this config from tag',
|
||||
name: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.actions.removeButtonAriaLabel',
|
||||
defaultMessage: 'Remove',
|
||||
}),
|
||||
description: props.intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagTable.actions.removeTooltip',
|
||||
defaultMessage: 'Remove this config from tag',
|
||||
}),
|
||||
type: 'icon',
|
||||
icon: 'trash',
|
||||
onClick: (item: ConfigurationBlock) => props.onConfigClick('delete', item),
|
||||
|
@ -60,3 +86,5 @@ export const ConfigList: React.SFC<ComponentProps> = props => (
|
|||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ConfigList = injectI18n(ConfigListUi);
|
||||
|
|
|
@ -5,20 +5,22 @@
|
|||
*/
|
||||
|
||||
import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { ActionDefinition } from './table_type_configs';
|
||||
|
||||
interface ActionButtonProps {
|
||||
itemName: 'Beats' | 'Tags';
|
||||
actions: ActionDefinition[];
|
||||
intl: InjectedIntl;
|
||||
isPopoverVisible: boolean;
|
||||
actionHandler(action: string, payload?: any): void;
|
||||
hidePopover(): void;
|
||||
showPopover(): void;
|
||||
}
|
||||
|
||||
export function ActionButton(props: ActionButtonProps) {
|
||||
const { actions, actionHandler, hidePopover, isPopoverVisible, showPopover } = props;
|
||||
export const ActionButton = injectI18n((props: ActionButtonProps) => {
|
||||
const { actions, actionHandler, hidePopover, isPopoverVisible, showPopover, intl } = props;
|
||||
if (actions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -27,7 +29,10 @@ export function ActionButton(props: ActionButtonProps) {
|
|||
anchorPosition="downLeft"
|
||||
button={
|
||||
<EuiButton iconSide="right" iconType="arrowDown" onClick={showPopover}>
|
||||
Bulk Action
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.table.bulkActionButtonLabel"
|
||||
defaultMessage="Bulk Action"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
closePopover={hidePopover}
|
||||
|
@ -41,7 +46,13 @@ export function ActionButton(props: ActionButtonProps) {
|
|||
panels={[
|
||||
{
|
||||
id: 0,
|
||||
title: `Manage ${props.itemName}`,
|
||||
title: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.table.bulkActionMenuLabel',
|
||||
defaultMessage: 'Manage {itemName}',
|
||||
},
|
||||
{ itemName: props.itemName }
|
||||
),
|
||||
items: actions.map(action => ({
|
||||
...action,
|
||||
onClick: () => actionHandler(action.action),
|
||||
|
@ -51,4 +62,4 @@ export function ActionButton(props: ActionButtonProps) {
|
|||
/>
|
||||
</EuiPopover>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AssignmentActionType } from './table';
|
||||
|
||||
export interface AssignmentControlSchema {
|
||||
|
@ -22,20 +23,32 @@ export interface AssignmentControlSchema {
|
|||
export const beatsListAssignmentOptions: AssignmentControlSchema[] = [
|
||||
{
|
||||
grow: false,
|
||||
name: 'Unenroll selected',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsListAssignmentOptions.unenrollButtonLabel', {
|
||||
defaultMessage: 'Unenroll selected',
|
||||
}),
|
||||
showWarning: true,
|
||||
warningHeading: 'Unenroll selected beats?',
|
||||
warningMessage: 'The selected Beats will no longer use central management',
|
||||
warningHeading: i18n.translate(
|
||||
'xpack.beatsManagement.beatsListAssignmentOptions.unenrollBeatsWarninigTitle',
|
||||
{ defaultMessage: 'Unenroll selected beats?' }
|
||||
),
|
||||
warningMessage: i18n.translate(
|
||||
'xpack.beatsManagement.beatsListAssignmentOptions.unenrollBeatsWarninigMessage',
|
||||
{ defaultMessage: 'The selected Beats will no longer use central management' }
|
||||
),
|
||||
action: AssignmentActionType.Delete,
|
||||
danger: true,
|
||||
},
|
||||
{
|
||||
name: 'Set tags',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsListAssignmentOptions.setTagsButtonLabel', {
|
||||
defaultMessage: 'Set tags',
|
||||
}),
|
||||
grow: false,
|
||||
lazyLoad: true,
|
||||
panel: {
|
||||
id: 1,
|
||||
name: 'Assign tags',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsListAssignmentOptions.assignTagsName', {
|
||||
defaultMessage: 'Assign tags',
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -44,10 +57,18 @@ export const tagListAssignmentOptions: AssignmentControlSchema[] = [
|
|||
{
|
||||
danger: true,
|
||||
grow: false,
|
||||
name: 'Remove tag(s)',
|
||||
name: i18n.translate('xpack.beatsManagement.tagListAssignmentOptions.removeTagsButtonLabel', {
|
||||
defaultMessage: 'Remove tag(s)',
|
||||
}),
|
||||
showWarning: true,
|
||||
warningHeading: 'Remove tag(s)',
|
||||
warningMessage: 'Remove the tag?',
|
||||
warningHeading: i18n.translate(
|
||||
'xpack.beatsManagement.tagListAssignmentOptions.removeTagsWarninigTitle',
|
||||
{ defaultMessage: 'Remove tag(s)' }
|
||||
),
|
||||
warningMessage: i18n.translate(
|
||||
'xpack.beatsManagement.tagListAssignmentOptions.removeTagWarninigMessage',
|
||||
{ defaultMessage: 'Remove the tag?' }
|
||||
),
|
||||
action: AssignmentActionType.Delete,
|
||||
},
|
||||
];
|
||||
|
@ -56,10 +77,18 @@ export const tagConfigAssignmentOptions: AssignmentControlSchema[] = [
|
|||
{
|
||||
danger: true,
|
||||
grow: false,
|
||||
name: 'Remove tag(s)',
|
||||
name: i18n.translate('xpack.beatsManagement.tagConfigAssignmentOptions.removeTagsButtonLabel', {
|
||||
defaultMessage: 'Remove tag(s)',
|
||||
}),
|
||||
showWarning: true,
|
||||
warningHeading: 'Remove tag(s)',
|
||||
warningMessage: 'Remove the tag from the selected beat(s)?',
|
||||
warningHeading: i18n.translate(
|
||||
'xpack.beatsManagement.tagConfigAssignmentOptions.removeTagsWarninigTitle',
|
||||
{ defaultMessage: 'Remove tag(s)' }
|
||||
),
|
||||
warningMessage: i18n.translate(
|
||||
'xpack.beatsManagement.tagConfigAssignmentOptions.removeTagsWarninigMessage',
|
||||
{ defaultMessage: 'Remove the tag from the selected beat(s)?' }
|
||||
),
|
||||
action: AssignmentActionType.Delete,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { AutocompleteField } from '../autocomplete_field/index';
|
||||
import { OptionControl } from '../table_controls';
|
||||
|
@ -15,13 +16,15 @@ interface ControlBarProps {
|
|||
assignmentOptions: AssignmentOptionsType;
|
||||
kueryBarProps?: KueryBarProps;
|
||||
selectionCount: number;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
export function ControlBar(props: ControlBarProps) {
|
||||
function ControlBarUi(props: ControlBarProps) {
|
||||
const {
|
||||
assignmentOptions: { actionHandler, items, schema, type },
|
||||
kueryBarProps,
|
||||
selectionCount,
|
||||
intl,
|
||||
} = props;
|
||||
|
||||
if (type === 'none') {
|
||||
|
@ -41,9 +44,17 @@ export function ControlBar(props: ControlBarProps) {
|
|||
</EuiFlexItem>
|
||||
{kueryBarProps && (
|
||||
<EuiFlexItem>
|
||||
<AutocompleteField {...kueryBarProps} placeholder="Filter results" />
|
||||
<AutocompleteField
|
||||
{...kueryBarProps}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.table.filterResultsPlaceholder',
|
||||
defaultMessage: 'Filter results',
|
||||
})}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export const ControlBar = injectI18n(ControlBarUi);
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
EuiInMemoryTable,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AutocompleteSuggestion } from 'ui/autocomplete_providers';
|
||||
|
@ -92,7 +93,10 @@ export class Table extends React.Component<TableProps, TableState> {
|
|||
: {
|
||||
onSelectionChange: this.setSelection,
|
||||
selectable: () => true,
|
||||
selectableMessage: () => 'Select this beat',
|
||||
selectableMessage: () =>
|
||||
i18n.translate('xpack.beatsManagement.table.selectThisBeatTooltip', {
|
||||
defaultMessage: 'Select this beat',
|
||||
}),
|
||||
selection: this.state.selection,
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiToolTip, IconColor } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { first, sortBy, sortByOrder, uniq } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
|
@ -56,7 +57,9 @@ export const BeatsTableType: TableType = {
|
|||
columnDefinitions: [
|
||||
{
|
||||
field: 'name',
|
||||
name: 'Beat name',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.beatNameTitle', {
|
||||
defaultMessage: 'Beat name',
|
||||
}),
|
||||
render: (name: string, beat: CMPopulatedBeat) => (
|
||||
<ConnectedLink path={`/beat/${beat.id}`}>{name}</ConnectedLink>
|
||||
),
|
||||
|
@ -64,12 +67,16 @@ export const BeatsTableType: TableType = {
|
|||
},
|
||||
{
|
||||
field: 'type',
|
||||
name: 'Type',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.typeTitle', {
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'full_tags',
|
||||
name: 'Tags',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.tagsTitle', {
|
||||
defaultMessage: 'Tags',
|
||||
}),
|
||||
render: (value: string, beat: CMPopulatedBeat) => (
|
||||
<EuiFlexGroup wrap responsive={true} gutterSize="xs">
|
||||
{(sortBy(beat.full_tags, 'id') || []).map(tag => (
|
||||
|
@ -86,26 +93,60 @@ export const BeatsTableType: TableType = {
|
|||
{
|
||||
// TODO: update to use actual metadata field
|
||||
field: 'config_status',
|
||||
name: 'Config Status',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.configStatusTitle', {
|
||||
defaultMessage: 'Config Status',
|
||||
}),
|
||||
render: (value: string, beat: CMPopulatedBeat) => {
|
||||
let color: IconColor = 'success';
|
||||
let statusText = 'OK';
|
||||
let tooltipText = 'Beat successfully applied latest config';
|
||||
let statusText = i18n.translate('xpack.beatsManagement.beatsTable.configStatus.okLabel', {
|
||||
defaultMessage: 'OK',
|
||||
});
|
||||
let tooltipText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.okTooltip',
|
||||
{
|
||||
defaultMessage: 'Beat successfully applied latest config',
|
||||
}
|
||||
);
|
||||
|
||||
switch (beat.config_status) {
|
||||
case 'UNKNOWN':
|
||||
color = 'subdued';
|
||||
statusText = 'Offline';
|
||||
statusText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.offlineLabel',
|
||||
{
|
||||
defaultMessage: 'Offline',
|
||||
}
|
||||
);
|
||||
if (moment().diff(beat.last_checkin, 'minutes') >= 10) {
|
||||
tooltipText = 'This Beat has not connected to kibana in over 10min';
|
||||
tooltipText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.noConnectionTooltip',
|
||||
{
|
||||
defaultMessage: 'This Beat has not connected to kibana in over 10min',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
tooltipText = 'This Beat has not yet been started.';
|
||||
tooltipText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.notStartedTooltip',
|
||||
{
|
||||
defaultMessage: 'This Beat has not yet been started.',
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'ERROR':
|
||||
color = 'danger';
|
||||
statusText = 'Error';
|
||||
tooltipText = 'Please check the logs of this Beat for error details';
|
||||
statusText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.errorLabel',
|
||||
{
|
||||
defaultMessage: 'Error',
|
||||
}
|
||||
);
|
||||
tooltipText = i18n.translate(
|
||||
'xpack.beatsManagement.beatsTable.configStatus.errorTooltip',
|
||||
{
|
||||
defaultMessage: 'Please check the logs of this Beat for error details',
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -121,7 +162,9 @@ export const BeatsTableType: TableType = {
|
|||
},
|
||||
{
|
||||
field: 'full_tags',
|
||||
name: 'Last config update',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.lastConfigUpdateTitle', {
|
||||
defaultMessage: 'Last config update',
|
||||
}),
|
||||
render: (tags: BeatTag[]) =>
|
||||
tags.length ? (
|
||||
<span>
|
||||
|
@ -134,7 +177,9 @@ export const BeatsTableType: TableType = {
|
|||
controlDefinitions: (data: any[]) => ({
|
||||
actions: [
|
||||
{
|
||||
name: 'Disenroll Selected',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.disenrollSelectedLabel', {
|
||||
defaultMessage: 'Disenroll Selected',
|
||||
}),
|
||||
action: 'delete',
|
||||
danger: true,
|
||||
},
|
||||
|
@ -143,7 +188,9 @@ export const BeatsTableType: TableType = {
|
|||
{
|
||||
type: 'field_value_selection',
|
||||
field: 'type',
|
||||
name: 'Type',
|
||||
name: i18n.translate('xpack.beatsManagement.beatsTable.typeLabel', {
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
options: uniq(data.map(({ type }: { type: any }) => ({ value: type })), 'value'),
|
||||
},
|
||||
],
|
||||
|
@ -155,7 +202,9 @@ export const TagsTableType: TableType = {
|
|||
columnDefinitions: [
|
||||
{
|
||||
field: 'id',
|
||||
name: 'Tag name',
|
||||
name: i18n.translate('xpack.beatsManagement.tagsTable.tagNameTitle', {
|
||||
defaultMessage: 'Tag name',
|
||||
}),
|
||||
render: (id: string, tag: BeatTag) => (
|
||||
<ConnectedLink path={`/tag/edit/${tag.id}`}>
|
||||
<TagBadge tag={tag} />
|
||||
|
@ -167,7 +216,9 @@ export const TagsTableType: TableType = {
|
|||
{
|
||||
align: 'right',
|
||||
field: 'configuration_blocks',
|
||||
name: 'Configurations',
|
||||
name: i18n.translate('xpack.beatsManagement.tagsTable.configurationsTitle', {
|
||||
defaultMessage: 'Configurations',
|
||||
}),
|
||||
render: (configurationBlocks: ConfigurationBlock[]) => (
|
||||
<div>{configurationBlocks.length}</div>
|
||||
),
|
||||
|
@ -176,7 +227,9 @@ export const TagsTableType: TableType = {
|
|||
{
|
||||
align: 'right',
|
||||
field: 'last_updated',
|
||||
name: 'Last update',
|
||||
name: i18n.translate('xpack.beatsManagement.tagsTable.lastUpdateTitle', {
|
||||
defaultMessage: 'Last update',
|
||||
}),
|
||||
render: (lastUpdate: Date) => <div>{moment(lastUpdate).fromNow()}</div>,
|
||||
sortable: true,
|
||||
},
|
||||
|
@ -184,7 +237,9 @@ export const TagsTableType: TableType = {
|
|||
controlDefinitions: (data: any) => ({
|
||||
actions: [
|
||||
{
|
||||
name: 'Remove Selected',
|
||||
name: i18n.translate('xpack.beatsManagement.tagsTable.removeSelectedLabel', {
|
||||
defaultMessage: 'Remove Selected',
|
||||
}),
|
||||
action: 'delete',
|
||||
danger: true,
|
||||
},
|
||||
|
@ -198,7 +253,9 @@ export const BeatDetailTagsTable: TableType = {
|
|||
columnDefinitions: [
|
||||
{
|
||||
field: 'id',
|
||||
name: 'Tag name',
|
||||
name: i18n.translate('xpack.beatsManagement.beatTagsTable.tagNameTitle', {
|
||||
defaultMessage: 'Tag name',
|
||||
}),
|
||||
render: (id: string, tag: BeatTag) => (
|
||||
<ConnectedLink path={`/tag/edit/${tag.id}`}>
|
||||
<TagBadge tag={tag} />
|
||||
|
@ -210,14 +267,18 @@ export const BeatDetailTagsTable: TableType = {
|
|||
{
|
||||
align: 'right',
|
||||
field: 'configuration_blocks',
|
||||
name: 'Configurations',
|
||||
name: i18n.translate('xpack.beatsManagement.beatTagsTable.configurationsTitle', {
|
||||
defaultMessage: 'Configurations',
|
||||
}),
|
||||
render: (configurations: ConfigurationBlock[]) => <span>{configurations.length}</span>,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
field: 'last_updated',
|
||||
name: 'Last update',
|
||||
name: i18n.translate('xpack.beatsManagement.beatTagsTable.lastUpdateTitle', {
|
||||
defaultMessage: 'Last update',
|
||||
}),
|
||||
render: (lastUpdate: string) => <span>{moment(lastUpdate).fromNow()}</span>,
|
||||
sortable: true,
|
||||
},
|
||||
|
@ -227,12 +288,16 @@ export const BeatDetailTagsTable: TableType = {
|
|||
filters: [],
|
||||
primaryActions: [
|
||||
{
|
||||
name: 'Add Tag',
|
||||
name: i18n.translate('xpack.beatsManagement.beatTagsTable.addTagLabel', {
|
||||
defaultMessage: 'Add Tag',
|
||||
}),
|
||||
action: 'add',
|
||||
danger: false,
|
||||
},
|
||||
{
|
||||
name: 'Remove Selected',
|
||||
name: i18n.translate('xpack.beatsManagement.beatTagsTable.removeSelectedLabel', {
|
||||
defaultMessage: 'Remove Selected',
|
||||
}),
|
||||
action: 'remove',
|
||||
danger: true,
|
||||
},
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
EuiOverlayMask,
|
||||
EuiTextColor,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { AssignmentActionType } from '../table';
|
||||
|
||||
|
@ -60,14 +61,33 @@ export class ActionControl extends React.PureComponent<ActionControlProps, Actio
|
|||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
buttonColor={danger ? 'danger' : 'primary'}
|
||||
cancelButtonText="Cancel"
|
||||
confirmButtonText="Confirm"
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.confirmModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
}
|
||||
confirmButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.confirmModal.confirmButtonLabel"
|
||||
defaultMessage="Confirm"
|
||||
/>
|
||||
}
|
||||
onConfirm={() => {
|
||||
actionHandler(action);
|
||||
this.setState({ showModal: false });
|
||||
}}
|
||||
onCancel={() => this.setState({ showModal: false })}
|
||||
title={warningHeading ? warningHeading : 'Confirm'}
|
||||
title={
|
||||
warningHeading ? (
|
||||
warningHeading
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.confirmModal.confirmWarningTitle"
|
||||
defaultMessage="Confirm"
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
{warningMessage}
|
||||
</EuiConfirmModal>
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
EuiToolTipProps,
|
||||
} from '@elastic/eui';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { isArray } from 'lodash';
|
||||
import React from 'react';
|
||||
import { AssignmentControlSchema } from '../table';
|
||||
|
@ -24,6 +25,7 @@ import { ActionControl } from './action_control';
|
|||
import { TagBadgeList } from './tag_badge_list';
|
||||
|
||||
interface ComponentProps {
|
||||
intl: InjectedIntl;
|
||||
itemType: string;
|
||||
items?: any[];
|
||||
schema: AssignmentControlSchema[];
|
||||
|
@ -40,7 +42,7 @@ interface FixedEuiToolTipProps extends EuiToolTipProps {
|
|||
}
|
||||
const FixedEuiToolTip = (EuiToolTip as any) as React.SFC<FixedEuiToolTipProps>;
|
||||
|
||||
export class OptionControl extends React.PureComponent<ComponentProps, ComponentState> {
|
||||
class OptionControlUi extends React.PureComponent<ComponentProps, ComponentState> {
|
||||
constructor(props: ComponentProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -53,7 +55,7 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
schemaOrArray: AssignmentControlSchema | AssignmentControlSchema[],
|
||||
panels: any = []
|
||||
) {
|
||||
const { items, actionHandler } = this.props;
|
||||
const { items, actionHandler, intl } = this.props;
|
||||
|
||||
let schema: AssignmentControlSchema | null = null;
|
||||
let schemaArray: AssignmentControlSchema[] | null = null;
|
||||
|
@ -91,14 +93,23 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
});
|
||||
} else {
|
||||
if (items === undefined) {
|
||||
panel.content = 'Unknown Error.';
|
||||
panel.content = intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tableControls.unknownErrorMessage',
|
||||
defaultMessage: 'Unknown Error.',
|
||||
});
|
||||
} else if (items.length === 0) {
|
||||
panel.content = (
|
||||
<EuiPanel>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="l" type="bolt" />}
|
||||
title="No tags found."
|
||||
description="Please create a new configuration tag."
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tableControls.noTagsFoundTitle',
|
||||
defaultMessage: 'No tags found.',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tableControls.noTagsFoundDescription',
|
||||
defaultMessage: 'Please create a new configuration tag.',
|
||||
})}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
|
@ -121,7 +132,7 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { itemType, selectionCount, schema } = this.props;
|
||||
const { itemType, selectionCount, schema, intl } = this.props;
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
|
@ -131,8 +142,21 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
delay="long"
|
||||
content={
|
||||
selectionCount === 0
|
||||
? `Select ${itemType} to perform operations such as setting tags and unenrolling Beats.`
|
||||
: `Manage your selected ${itemType}`
|
||||
? intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.tableControls.selectItemDescription',
|
||||
defaultMessage:
|
||||
'Select {itemType} to perform operations such as setting tags and unenrolling Beats.',
|
||||
},
|
||||
{ itemType }
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.tableControls.manageSelectedItemDescription',
|
||||
defaultMessage: 'Manage your selected {itemType}',
|
||||
},
|
||||
{ itemType }
|
||||
)
|
||||
}
|
||||
>
|
||||
<EuiButton
|
||||
|
@ -146,7 +170,11 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
});
|
||||
}}
|
||||
>
|
||||
Manage {itemType}
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tableControls.manageItemButtonLabel"
|
||||
defaultMessage="Manage {itemType}"
|
||||
values={{ itemType }}
|
||||
/>
|
||||
</EuiButton>
|
||||
</FixedEuiToolTip>
|
||||
}
|
||||
|
@ -164,3 +192,5 @@ export class OptionControl extends React.PureComponent<ComponentProps, Component
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const OptionControl = injectI18n(OptionControlUi);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
// @ts-ignore
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import Formsy, { addValidationRule, FieldValue, FormData } from 'formsy-react';
|
||||
import yaml from 'js-yaml';
|
||||
import { get } from 'lodash';
|
||||
|
@ -55,6 +56,7 @@ addValidationRule('isYaml', (values: FormData, value: FieldValue) => {
|
|||
});
|
||||
|
||||
interface ComponentProps {
|
||||
intl: InjectedIntl;
|
||||
values: ConfigurationBlock;
|
||||
schema: YamlConfigSchema[];
|
||||
id: string;
|
||||
|
@ -62,7 +64,7 @@ interface ComponentProps {
|
|||
canSubmit(canIt: boolean): any;
|
||||
}
|
||||
|
||||
export class ConfigForm extends React.Component<ComponentProps, any> {
|
||||
class ConfigFormUi extends React.Component<ComponentProps, any> {
|
||||
private form = React.createRef<HTMLButtonElement>();
|
||||
constructor(props: ComponentProps) {
|
||||
super(props);
|
||||
|
@ -97,6 +99,7 @@ export class ConfigForm extends React.Component<ComponentProps, any> {
|
|||
this.props.onSubmit(model);
|
||||
};
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<br />
|
||||
|
@ -181,9 +184,15 @@ export class ConfigForm extends React.Component<ComponentProps, any> {
|
|||
)}
|
||||
helpText={schema.ui.helpText}
|
||||
label={schema.ui.label}
|
||||
options={[{ value: '', text: 'Please Select An Option' }].concat(
|
||||
schema.options || []
|
||||
)}
|
||||
options={[
|
||||
{
|
||||
value: '',
|
||||
text: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagConfig.selectOptionLabel',
|
||||
defaultMessage: 'Please Select An Option',
|
||||
}),
|
||||
},
|
||||
].concat(schema.options || [])}
|
||||
validations={schema.validations}
|
||||
validationError={schema.error}
|
||||
required={schema.required}
|
||||
|
@ -225,3 +234,4 @@ export class ConfigForm extends React.Component<ComponentProps, any> {
|
|||
);
|
||||
}
|
||||
}
|
||||
export const ConfigForm = injectI18n(ConfigFormUi, { withRef: true });
|
||||
|
|
|
@ -27,20 +27,23 @@ import {
|
|||
EuiTabbedContent,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { ConfigurationBlock } from '../../../../common/domain_types';
|
||||
import { supportedConfigs } from '../../../config_schemas';
|
||||
import { getSupportedConfig } from '../../../config_schemas_translations_map';
|
||||
import { ConfigForm } from './config_form';
|
||||
|
||||
interface ComponentProps {
|
||||
intl: InjectedIntl;
|
||||
configBlock?: ConfigurationBlock;
|
||||
onClose(): any;
|
||||
onSave?(config: ConfigurationBlock): any;
|
||||
}
|
||||
|
||||
export class ConfigView extends React.Component<ComponentProps, any> {
|
||||
class ConfigViewUi extends React.Component<ComponentProps, any> {
|
||||
private form = React.createRef<any>();
|
||||
private editMode: boolean;
|
||||
private supportedConfigs = getSupportedConfig();
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.editMode = props.configBlock !== undefined;
|
||||
|
@ -48,7 +51,7 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
this.state = {
|
||||
valid: false,
|
||||
configBlock: props.configBlock || {
|
||||
type: supportedConfigs[0].value,
|
||||
type: this.supportedConfigs[0].value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -62,42 +65,77 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
}));
|
||||
};
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
return (
|
||||
<EuiFlyout onClose={this.props.onClose}>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle size="m">
|
||||
<h2>
|
||||
{this.editMode
|
||||
? this.props.onSave
|
||||
? 'Edit configuration block'
|
||||
: 'View configuration block'
|
||||
: 'Add configuration block'}
|
||||
{this.editMode ? (
|
||||
this.props.onSave ? (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.editConfigurationTitle"
|
||||
defaultMessage="Edit configuration block"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.viewConfigurationTitle"
|
||||
defaultMessage="View configuration block"
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.addConfigurationTitle"
|
||||
defaultMessage="Add configuration block"
|
||||
/>
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiFormRow label="Type">
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.typeLabel"
|
||||
defaultMessage="Type"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiSelect
|
||||
options={supportedConfigs}
|
||||
options={this.supportedConfigs}
|
||||
value={this.state.configBlock.type}
|
||||
disabled={this.editMode}
|
||||
onChange={this.onValueChange('type')}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label="Description">
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.descriptionLabel"
|
||||
defaultMessage="Description"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
value={this.state.configBlock.description}
|
||||
disabled={!this.props.onSave}
|
||||
onChange={this.onValueChange('description')}
|
||||
placeholder="Description (optional)"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagConfig.descriptionPlaceholder',
|
||||
defaultMessage: 'Description (optional)',
|
||||
})}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<h3>
|
||||
{
|
||||
(supportedConfigs.find(config => this.state.configBlock.type === config.value) as any)
|
||||
.text
|
||||
}
|
||||
configuration
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.configurationTypeText"
|
||||
defaultMessage="{configType} configuration"
|
||||
values={{
|
||||
configType: (this.supportedConfigs.find(
|
||||
config => this.state.configBlock.type === config.value
|
||||
) as any).text,
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
<EuiHorizontalRule />
|
||||
|
||||
|
@ -119,12 +157,14 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
ref={this.form}
|
||||
values={this.state.configBlock}
|
||||
id={
|
||||
(supportedConfigs.find(config => this.state.configBlock.type === config.value) as any)
|
||||
.value
|
||||
(this.supportedConfigs.find(
|
||||
config => this.state.configBlock.type === config.value
|
||||
) as any).value
|
||||
}
|
||||
schema={
|
||||
(supportedConfigs.find(config => this.state.configBlock.type === config.value) as any)
|
||||
.config
|
||||
(this.supportedConfigs.find(
|
||||
config => this.state.configBlock.type === config.value
|
||||
) as any).config
|
||||
}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
|
@ -132,7 +172,10 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={this.props.onClose}>
|
||||
Close
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
{this.props.onSave && (
|
||||
|
@ -141,12 +184,15 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
disabled={!this.state.valid}
|
||||
fill
|
||||
onClick={() => {
|
||||
if (this.form.current) {
|
||||
this.form.current.submit();
|
||||
if (this.form.current && this.form.current.getWrappedInstance()) {
|
||||
this.form.current.getWrappedInstance().submit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tagConfig.saveButtonLabel"
|
||||
defaultMessage="Save"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
@ -156,3 +202,5 @@ export class ConfigView extends React.Component<ComponentProps, any> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const ConfigView = injectI18n(ConfigViewUi);
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import 'brace/mode/yaml';
|
||||
import 'brace/theme/github';
|
||||
import { isEqual } from 'lodash';
|
||||
|
@ -37,6 +38,7 @@ interface TagEditProps {
|
|||
onDetachBeat: (beatIds: string[]) => void;
|
||||
onTagChange: (field: keyof BeatTag, value: string) => any;
|
||||
attachedBeats: CMBeat[] | null;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface TagEditState {
|
||||
|
@ -45,7 +47,7 @@ interface TagEditState {
|
|||
selectedConfigIndex?: number;
|
||||
}
|
||||
|
||||
export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
||||
class TagEditUi extends React.PureComponent<TagEditProps, TagEditState> {
|
||||
constructor(props: TagEditProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -56,17 +58,25 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { tag, attachedBeats } = this.props;
|
||||
const { tag, attachedBeats, intl } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Tag details</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.tagDetailsTitle"
|
||||
defaultMessage="Tag details"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText color="subdued">
|
||||
<p>
|
||||
A tag is a group of configuration blocks that you can apply to one or more Beats.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.tagDetailsDescription"
|
||||
defaultMessage="A tag is a group of configuration blocks that you can apply to one or more Beats."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<div>
|
||||
|
@ -76,7 +86,12 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
<EuiFlexItem>
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
label="Tag Name"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.tagNameLabel"
|
||||
defaultMessage="Tag Name"
|
||||
/>
|
||||
}
|
||||
isInvalid={!!this.getNameError(tag.id)}
|
||||
error={this.getNameError(tag.id) || undefined}
|
||||
>
|
||||
|
@ -86,11 +101,19 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
onChange={this.updateTag('id')}
|
||||
disabled={this.props.mode === 'edit'}
|
||||
value={tag.id}
|
||||
placeholder="Tag name (required)"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tag.tagNamePlaceholder',
|
||||
defaultMessage: 'Tag name (required)',
|
||||
})}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{this.props.mode === 'create' && (
|
||||
<EuiFormRow label="Tag Color">
|
||||
<EuiFormRow
|
||||
label={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tag.tagColorLabel',
|
||||
defaultMessage: 'Tag Color',
|
||||
})}
|
||||
>
|
||||
<EuiColorPicker color={tag.color} onChange={this.updateTag('color')} />
|
||||
</EuiFormRow>
|
||||
)}
|
||||
|
@ -107,13 +130,20 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Configuration blocks</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.tagConfigurationsTitle"
|
||||
defaultMessage="Configuration blocks"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText color="subdued">
|
||||
<p>
|
||||
A tag can have configuration blocks for different types of Beats. For example, a tag
|
||||
can have two Metricbeat configuration blocks and one Filebeat input configuration
|
||||
block.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.tagConfigurationsDescription"
|
||||
defaultMessage="A tag can have configuration blocks for different types of Beats. For example, a tag
|
||||
can have two Metricbeat configuration blocks and one Filebeat input configuration block."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
|
@ -143,7 +173,10 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
this.setState({ showFlyout: true });
|
||||
}}
|
||||
>
|
||||
Add configuration block
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.addConfigurationButtonLabel"
|
||||
defaultMessage="Add configuration block"
|
||||
/>
|
||||
</EuiButton>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
|
@ -154,7 +187,12 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
<EuiHorizontalRule />
|
||||
|
||||
<EuiTitle size="xs">
|
||||
<h3>Beats with this tag</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.beatsAssignedToTagTitle"
|
||||
defaultMessage="Beats with this tag"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<Table
|
||||
assignmentOptions={{
|
||||
|
@ -198,8 +236,12 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
}
|
||||
|
||||
private getNameError = (name: string) => {
|
||||
const { intl } = this.props;
|
||||
if (name && name !== '' && name.search(/^[a-zA-Z0-9-]+$/) === -1) {
|
||||
return 'Tag name must consist of letters, numbers, and dashes only';
|
||||
return intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tag.tagName.validationErrorMessage',
|
||||
defaultMessage: 'Tag name must consist of letters, numbers, and dashes only',
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -219,3 +261,5 @@ export class TagEdit extends React.PureComponent<TagEditProps, TagEditState> {
|
|||
? this.props.onTagChange(key, value)
|
||||
: (e: any) => this.props.onTagChange(key, e.target ? e.target.value : e);
|
||||
}
|
||||
|
||||
export const TagEdit = injectI18n(TagEditUi);
|
||||
|
|
|
@ -10,24 +10,24 @@ const filebeatInputConfig: YamlConfigSchema[] = [
|
|||
{
|
||||
id: 'paths',
|
||||
ui: {
|
||||
label: 'Paths',
|
||||
label: 'filebeatInputConfig.paths.ui.label',
|
||||
type: 'multi-input',
|
||||
helpText: 'Put each of the paths on a seperate line',
|
||||
helpText: 'filebeatInputConfig.paths.ui.helpText',
|
||||
placeholder: `first/path/to/file.json second/path/to/otherfile.json`,
|
||||
},
|
||||
validations: 'isPaths',
|
||||
error: 'One file path per line',
|
||||
error: 'filebeatInputConfig.paths.error',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
ui: {
|
||||
label: 'Other Config',
|
||||
label: 'filebeatInputConfig.other.ui.label',
|
||||
type: 'code',
|
||||
helpText: 'Use YAML format to specify other settings for the Filebeat Input',
|
||||
helpText: 'filebeatInputConfig.other.ui.helpText',
|
||||
},
|
||||
validations: 'isYaml',
|
||||
error: 'Use valid YAML format',
|
||||
error: 'filebeatInputConfig.other.error',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -35,7 +35,7 @@ const filebeatModuleConfig: YamlConfigSchema[] = [
|
|||
{
|
||||
id: 'module',
|
||||
ui: {
|
||||
label: 'Module',
|
||||
label: 'filebeatModuleConfig.module.ui.label',
|
||||
type: 'select',
|
||||
},
|
||||
options: [
|
||||
|
@ -108,18 +108,18 @@ const filebeatModuleConfig: YamlConfigSchema[] = [
|
|||
text: 'traefik',
|
||||
},
|
||||
],
|
||||
error: 'Please select a module',
|
||||
error: 'filebeatModuleConfig.module.error',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
ui: {
|
||||
label: 'Other Config',
|
||||
label: 'filebeatModuleConfig.other.ui.label',
|
||||
type: 'code',
|
||||
helpText: 'Use YAML format to specify other settings for the Filebeat Module',
|
||||
helpText: 'filebeatModuleConfig.other.ui.helpText',
|
||||
},
|
||||
validations: 'isYaml',
|
||||
error: 'Use valid YAML format',
|
||||
error: 'filebeatModuleConfig.other.error',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -127,7 +127,7 @@ const metricbeatModuleConfig: YamlConfigSchema[] = [
|
|||
{
|
||||
id: 'module',
|
||||
ui: {
|
||||
label: 'Module',
|
||||
label: 'metricbeatModuleConfig.module.ui.label',
|
||||
type: 'select',
|
||||
},
|
||||
options: [
|
||||
|
@ -272,41 +272,41 @@ const metricbeatModuleConfig: YamlConfigSchema[] = [
|
|||
text: 'zookeeper',
|
||||
},
|
||||
],
|
||||
error: 'Please select a module',
|
||||
error: 'metricbeatModuleConfig.module.error',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'hosts',
|
||||
ui: {
|
||||
label: 'Hosts',
|
||||
label: 'metricbeatModuleConfig.hosts.ui.label',
|
||||
type: 'multi-input',
|
||||
helpText: 'Put each of the paths on a seperate line',
|
||||
helpText: 'metricbeatModuleConfig.hosts.ui.helpText',
|
||||
placeholder: `somehost.local otherhost.local`,
|
||||
},
|
||||
validations: 'isHosts',
|
||||
error: 'One file host per line',
|
||||
error: 'metricbeatModuleConfig.hosts.error',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'period',
|
||||
ui: {
|
||||
label: 'Period',
|
||||
label: 'metricbeatModuleConfig.period.ui.label',
|
||||
type: 'input',
|
||||
},
|
||||
defaultValue: '10s',
|
||||
validations: 'isPeriod',
|
||||
error: 'Invalid Period, must be formatted as `10s` for 10 seconds',
|
||||
error: 'metricbeatModuleConfig.period.error',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
ui: {
|
||||
label: 'Other Config',
|
||||
label: 'metricbeatModuleConfig.other.ui.label',
|
||||
type: 'code',
|
||||
helpText: 'Use YAML format to specify other settings for the Metricbeat Module',
|
||||
helpText: 'metricbeatModuleConfig.other.ui.helpText',
|
||||
},
|
||||
validations: 'isYaml',
|
||||
error: 'Use valid YAML format',
|
||||
error: 'metricbeatModuleConfig.other.error',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -314,7 +314,7 @@ const outputConfig: YamlConfigSchema[] = [
|
|||
{
|
||||
id: 'output',
|
||||
ui: {
|
||||
label: 'Output Type',
|
||||
label: 'outputConfig.output.ui.label',
|
||||
type: 'select',
|
||||
},
|
||||
options: [
|
||||
|
@ -335,42 +335,54 @@ const outputConfig: YamlConfigSchema[] = [
|
|||
text: 'Console',
|
||||
},
|
||||
],
|
||||
error: 'Please select an output type',
|
||||
error: 'outputConfig.output.error',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: '{{output}}.hosts',
|
||||
ui: {
|
||||
label: 'Hosts',
|
||||
label: 'outputConfig.hosts.ui.label',
|
||||
type: 'multi-input',
|
||||
},
|
||||
validations: 'isHosts',
|
||||
error: 'One file host per line',
|
||||
error: 'outputConfig.hosts.error',
|
||||
parseValidResult: v => v.split('\n'),
|
||||
},
|
||||
{
|
||||
id: '{{output}}.username',
|
||||
ui: {
|
||||
label: 'Username',
|
||||
label: 'outputConfig.username.ui.label',
|
||||
type: 'input',
|
||||
},
|
||||
validations: 'isString',
|
||||
error: 'Unprocessable username',
|
||||
error: 'outputConfig.username.error',
|
||||
},
|
||||
{
|
||||
id: '{{output}}.password',
|
||||
ui: {
|
||||
label: 'Password',
|
||||
label: 'outputConfig.password.ui.label',
|
||||
type: 'password',
|
||||
},
|
||||
validations: 'isString',
|
||||
error: 'Unprocessable password',
|
||||
error: 'outputConfig.password.error',
|
||||
},
|
||||
];
|
||||
|
||||
export const supportedConfigs = [
|
||||
{ text: 'Filebeat Input', value: 'filebeat.inputs', config: filebeatInputConfig },
|
||||
{ text: 'Filebeat Module', value: 'filebeat.modules', config: filebeatModuleConfig },
|
||||
{ text: 'Metricbeat Module', value: 'metricbeat.modules', config: metricbeatModuleConfig },
|
||||
{ text: 'Output', value: 'output', config: outputConfig },
|
||||
{
|
||||
text: 'supportedConfigs.filebeatInput.text',
|
||||
value: 'filebeat.inputs',
|
||||
config: filebeatInputConfig,
|
||||
},
|
||||
{
|
||||
text: 'supportedConfigs.filebeatModule.text',
|
||||
value: 'filebeat.modules',
|
||||
config: filebeatModuleConfig,
|
||||
},
|
||||
{
|
||||
text: 'supportedConfigs.metricbeatModule.text',
|
||||
value: 'metricbeat.modules',
|
||||
config: metricbeatModuleConfig,
|
||||
},
|
||||
{ text: 'supportedConfigs.output.text', value: 'output', config: outputConfig },
|
||||
];
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { supportedConfigs } from './config_schemas';
|
||||
import { YamlConfigSchema } from './lib/lib';
|
||||
|
||||
interface ConfigSchema {
|
||||
text: string;
|
||||
value: string;
|
||||
config: YamlConfigSchema[];
|
||||
}
|
||||
|
||||
let translatedConfigs: ConfigSchema[];
|
||||
const supportedConfigLabelsMap = new Map<string, string>([
|
||||
[
|
||||
'filebeatInputConfig.paths.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.pathsLabel', {
|
||||
defaultMessage: 'Paths',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatInputConfig.paths.ui.helpText',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.pathsDescription', {
|
||||
defaultMessage: 'Put each of the paths on a separate line',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatInputConfig.paths.error',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.pathsErrorMessage', {
|
||||
defaultMessage: 'One file path per line',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatInputConfig.other.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.otherConfigLabel', {
|
||||
defaultMessage: 'Other Config',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatInputConfig.other.ui.helpText',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.otherConfigDescription', {
|
||||
defaultMessage: 'Use YAML format to specify other settings for the Filebeat Input',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatInputConfig.other.error',
|
||||
i18n.translate('xpack.beatsManagement.filebeatInputConfig.otherConfigErrorMessage', {
|
||||
defaultMessage: 'Use valid YAML format',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatModuleConfig.module.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.filebeatModuleConfig.moduleLabel', {
|
||||
defaultMessage: 'Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatModuleConfig.module.error',
|
||||
i18n.translate('xpack.beatsManagement.filebeatModuleConfig.moduleErrorMessage', {
|
||||
defaultMessage: 'Please select a module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatModuleConfig.other.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.filebeatModuleConfig.otherConfigLabel', {
|
||||
defaultMessage: 'Other Config',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatModuleConfig.other.ui.helpText',
|
||||
i18n.translate('xpack.beatsManagement.filebeatModuleConfig.moduleDescription', {
|
||||
defaultMessage: 'Use YAML format to specify other settings for the Filebeat Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'filebeatModuleConfig.other.error',
|
||||
i18n.translate('xpack.beatsManagement.filebeatModuleConfig.otherConfigErrorMessage', {
|
||||
defaultMessage: 'Use valid YAML format',
|
||||
}),
|
||||
],
|
||||
|
||||
[
|
||||
'metricbeatModuleConfig.module.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.moduleLabel', {
|
||||
defaultMessage: 'Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.module.error',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.moduleErrorMessage', {
|
||||
defaultMessage: 'Please select a module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.hosts.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.hostsLabel', {
|
||||
defaultMessage: 'Hosts',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.hosts.ui.helpText',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.hostsDescription', {
|
||||
defaultMessage: 'Put each of the paths on a seperate line',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.hosts.error',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.hostsErrorMessage', {
|
||||
defaultMessage: 'One file host per line',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.period.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.periodLabel', {
|
||||
defaultMessage: 'Period',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.period.error',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.periodErrorMessage', {
|
||||
defaultMessage: 'Invalid Period, must be formatted as `10s` for 10 seconds',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.other.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.otherConfigLabel', {
|
||||
defaultMessage: 'Other Config',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.other.ui.helpText',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.otherConfigDescription', {
|
||||
defaultMessage: 'Use YAML format to specify other settings for the Metricbeat Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'metricbeatModuleConfig.other.error',
|
||||
i18n.translate('xpack.beatsManagement.metricbeatModuleConfig.otherConfigErrorMessage', {
|
||||
defaultMessage: 'Use valid YAML format',
|
||||
}),
|
||||
],
|
||||
|
||||
[
|
||||
'outputConfig.output.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.outputTypeLabel', {
|
||||
defaultMessage: 'Output Type',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.output.error',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.outputTypeErrorMessage', {
|
||||
defaultMessage: 'Please select an output type',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.hosts.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.hostsLabel', {
|
||||
defaultMessage: 'Hosts',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.hosts.error',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.hostsErrorMessage', {
|
||||
defaultMessage: 'One file host per line',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.username.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.usernameLabel', {
|
||||
defaultMessage: 'Username',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.username.error',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.usernameErrorMessage', {
|
||||
defaultMessage: 'Unprocessable username',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.password.ui.label',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.passwordLabel', {
|
||||
defaultMessage: 'Password',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'outputConfig.password.error',
|
||||
i18n.translate('xpack.beatsManagement.outputConfig.passwordErrorMessage', {
|
||||
defaultMessage: 'Unprocessable password',
|
||||
}),
|
||||
],
|
||||
|
||||
[
|
||||
'supportedConfigs.filebeatInput.text',
|
||||
i18n.translate('xpack.beatsManagement.tagConfig.filebeatInputLabel', {
|
||||
defaultMessage: 'Filebeat Input',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'supportedConfigs.filebeatModule.text',
|
||||
i18n.translate('xpack.beatsManagement.tagConfig.filebeatModuleLabel', {
|
||||
defaultMessage: 'Filebeat Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'supportedConfigs.metricbeatModule.text',
|
||||
i18n.translate('xpack.beatsManagement.tagConfig.metricbeatModuleLabel', {
|
||||
defaultMessage: 'Metricbeat Module',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'supportedConfigs.output.text',
|
||||
i18n.translate('xpack.beatsManagement.tagConfig.outputLabel', {
|
||||
defaultMessage: 'Output',
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
export const getSupportedConfig = () => {
|
||||
if (translatedConfigs) {
|
||||
return translatedConfigs;
|
||||
}
|
||||
|
||||
translatedConfigs = cloneDeep(supportedConfigs);
|
||||
|
||||
translatedConfigs.forEach(({ text, config }) => {
|
||||
if (text) {
|
||||
text = supportedConfigLabelsMap.get(text) || '';
|
||||
}
|
||||
|
||||
config.forEach(yanlConfig => {
|
||||
if (yanlConfig.ui.label) {
|
||||
yanlConfig.ui.label = supportedConfigLabelsMap.get(yanlConfig.ui.label) || '';
|
||||
}
|
||||
if (yanlConfig.ui.helpText) {
|
||||
yanlConfig.ui.helpText = supportedConfigLabelsMap.get(yanlConfig.ui.helpText);
|
||||
}
|
||||
if (yanlConfig.error) {
|
||||
yanlConfig.error = supportedConfigLabelsMap.get(yanlConfig.error) || '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return translatedConfigs;
|
||||
};
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
import * as euiVars from '@elastic/eui/dist/eui_theme_k6_light.json';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { BASE_PATH } from '../common/constants';
|
||||
|
@ -14,13 +16,21 @@ import { FrontendLibs } from './lib/lib';
|
|||
import { PageRouter } from './router';
|
||||
|
||||
function startApp(libs: FrontendLibs) {
|
||||
libs.framework.registerManagementSection('beats', 'Central Management (Beta)', BASE_PATH);
|
||||
libs.framework.registerManagementSection(
|
||||
'beats',
|
||||
i18n.translate('xpack.beatsManagement.managementMainPage.centralManagementLinkLabel', {
|
||||
defaultMessage: 'Central Management (Beta)',
|
||||
}),
|
||||
BASE_PATH
|
||||
);
|
||||
libs.framework.render(
|
||||
<ThemeProvider theme={{ eui: euiVars }}>
|
||||
<BreadcrumbProvider>
|
||||
<PageRouter libs={libs} />
|
||||
</BreadcrumbProvider>
|
||||
</ThemeProvider>
|
||||
<I18nProvider>
|
||||
<ThemeProvider theme={{ eui: euiVars }}>
|
||||
<BreadcrumbProvider>
|
||||
<PageRouter libs={libs} />
|
||||
</BreadcrumbProvider>
|
||||
</ThemeProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import { IModule, IScope } from 'angular';
|
|||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
BufferedKibanaServiceCall,
|
||||
FrameworkAdapter,
|
||||
|
@ -103,7 +104,9 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
|
|||
if (this.hasValidLicense()) {
|
||||
const registerSection = () =>
|
||||
this.management.register(pluginId, {
|
||||
display: 'Beats', // TODO these need to be config options not hard coded in the adapter
|
||||
display: i18n.translate('xpack.beatsManagement.beatsDislayName', {
|
||||
defaultMessage: 'Beats',
|
||||
}), // TODO these need to be config options not hard coded in the adapter
|
||||
icon: 'logoBeats',
|
||||
order: 30,
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ import { uiModules } from 'ui/modules';
|
|||
import routes from 'ui/routes';
|
||||
|
||||
import { INDEX_NAMES } from '../../../common/constants/index_names';
|
||||
import { supportedConfigs } from '../../config_schemas';
|
||||
import { getSupportedConfig } from '../../config_schemas_translations_map';
|
||||
import { RestBeatsAdapter } from '../adapters/beats/rest_beats_adapter';
|
||||
import { RestElasticsearchAdapter } from '../adapters/elasticsearch/rest';
|
||||
import { KibanaFrameworkAdapter } from '../adapters/framework/kibana_framework_adapter';
|
||||
|
@ -34,7 +34,7 @@ export function compose(): FrontendLibs {
|
|||
const api = new AxiosRestAPIAdapter(chrome.getXsrfToken(), chrome.getBasePath());
|
||||
const esAdapter = new RestElasticsearchAdapter(api, INDEX_NAMES.BEATS);
|
||||
|
||||
const tags = new TagsLib(new RestTagsAdapter(api), supportedConfigs);
|
||||
const tags = new TagsLib(new RestTagsAdapter(api), getSupportedConfig());
|
||||
const tokens = new RestTokensAdapter(api);
|
||||
const beats = new BeatsLib(new RestBeatsAdapter(api), {
|
||||
tags,
|
||||
|
|
|
@ -21,7 +21,7 @@ import { BeatsLib } from '../beats';
|
|||
import { FrontendDomainLibs, FrontendLibs } from '../lib';
|
||||
|
||||
import { AutocompleteSuggestion } from 'ui/autocomplete_providers';
|
||||
import { supportedConfigs } from '../../config_schemas';
|
||||
import { getSupportedConfig } from '../../config_schemas_translations_map';
|
||||
import { TagsLib } from '../tags';
|
||||
import { MemoryElasticsearchAdapter } from './../adapters/elasticsearch/memory';
|
||||
import { ElasticsearchLib } from './../elasticsearch';
|
||||
|
@ -36,7 +36,7 @@ export function compose(
|
|||
mockKueryToEsQuery,
|
||||
suggestions
|
||||
);
|
||||
const tags = new TagsLib(new MemoryTagsAdapter([]), supportedConfigs);
|
||||
const tags = new TagsLib(new MemoryTagsAdapter([]), getSupportedConfig());
|
||||
const tokens = new MemoryTokensAdapter();
|
||||
const beats = new BeatsLib(new MemoryBeatsAdapter([]), { tags });
|
||||
|
||||
|
|
|
@ -4,10 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
export class NotFoundPage extends React.PureComponent {
|
||||
public render() {
|
||||
return <div>No content found</div>;
|
||||
return (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.noContentFoundErrorMessage"
|
||||
defaultMessage="No content found"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { first, sortByOrder } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
|
@ -20,14 +21,20 @@ export const BeatDetailsActionSection = ({ beat }: BeatDetailsActionSectionProps
|
|||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
Type:
|
||||
<strong>{beat.type}</strong>.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.actionSectionTypeLabel"
|
||||
defaultMessage="Type: {beatType}."
|
||||
values={{ beatType: <strong>{beat.type}</strong> }}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
Version:
|
||||
<strong>{beat.version}</strong>.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.actionSectionVersionLabel"
|
||||
defaultMessage="Version: {beatVersion}."
|
||||
values={{ beatVersion: <strong>{beat.version}</strong> }}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
{/* TODO: We need a populated field before we can run this code
|
||||
|
@ -40,19 +47,30 @@ export const BeatDetailsActionSection = ({ beat }: BeatDetailsActionSectionProps
|
|||
beat.full_tags.length > 0 && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
Last Config Update:{' '}
|
||||
<strong>
|
||||
{moment(
|
||||
first(sortByOrder(beat.full_tags, 'last_updated')).last_updated
|
||||
).fromNow()}
|
||||
</strong>
|
||||
.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.lastConfigUpdateMessage"
|
||||
defaultMessage="Last Config Update: {lastUpdateTime}."
|
||||
values={{
|
||||
lastUpdateTime: (
|
||||
<strong>
|
||||
{moment(
|
||||
first(sortByOrder(beat.full_tags, 'last_updated')).last_updated
|
||||
).fromNow()}
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<div>Beat not found</div>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.beatNotFoundMessage"
|
||||
defaultMessage="Beat not found"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { FrontendLibs } from '../../lib/lib';
|
||||
|
||||
|
@ -11,4 +12,11 @@ interface BeatActivityPageProps {
|
|||
libs: FrontendLibs;
|
||||
}
|
||||
|
||||
export const BeatActivityPage = (props: BeatActivityPageProps) => <div>Beat Activity View</div>;
|
||||
export const BeatActivityPage = (props: BeatActivityPageProps) => (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.beatActivityViewTitle"
|
||||
defaultMessage="Beat Activity View"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { flatten, get } from 'lodash';
|
||||
import React from 'react';
|
||||
import { TABLE_CONFIG } from '../../../common/constants';
|
||||
|
@ -20,17 +21,18 @@ import { BeatTag, CMPopulatedBeat, ConfigurationBlock } from '../../../common/do
|
|||
import { ConnectedLink } from '../../components/connected_link';
|
||||
import { TagBadge } from '../../components/tag';
|
||||
import { ConfigView } from '../../components/tag/config_view/index';
|
||||
import { supportedConfigs } from '../../config_schemas';
|
||||
import { getSupportedConfig } from '../../config_schemas_translations_map';
|
||||
|
||||
interface PageProps {
|
||||
beat: CMPopulatedBeat | undefined;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface PageState {
|
||||
selectedConfig: ConfigurationBlock | null;
|
||||
}
|
||||
|
||||
export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
||||
class BeatDetailPageUi extends React.PureComponent<PageProps, PageState> {
|
||||
constructor(props: PageProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -40,9 +42,16 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
}
|
||||
public render() {
|
||||
const props = this.props;
|
||||
const { beat } = props;
|
||||
const { beat, intl } = props;
|
||||
if (!beat) {
|
||||
return <div>Beat not found</div>;
|
||||
return (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.beatNotFoundErrorTitle"
|
||||
defaultMessage="Beat not found"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const configurationBlocks = flatten(
|
||||
beat.full_tags.map((tag: BeatTag) => {
|
||||
|
@ -54,7 +63,7 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
...beat,
|
||||
...configuration,
|
||||
displayValue: get(
|
||||
supportedConfigs.find(config => config.value === configuration.type),
|
||||
getSupportedConfig().find(config => config.value === configuration.type),
|
||||
'text',
|
||||
null
|
||||
),
|
||||
|
@ -65,7 +74,10 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
const columns = [
|
||||
{
|
||||
field: 'displayValue',
|
||||
name: 'Type',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatConfigurations.typeColumnName',
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
sortable: true,
|
||||
render: (value: string | null, configuration: any) => (
|
||||
<EuiLink
|
||||
|
@ -81,17 +93,26 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
},
|
||||
{
|
||||
field: 'module',
|
||||
name: 'Module',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatConfigurations.moduleColumnName',
|
||||
defaultMessage: 'Module',
|
||||
}),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
name: 'Description',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatConfigurations.descriptionColumnName',
|
||||
defaultMessage: 'Description',
|
||||
}),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'tagId',
|
||||
name: 'Tag',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatConfigurations.tagColumnName',
|
||||
defaultMessage: 'Tag',
|
||||
}),
|
||||
render: (id: string, block: any) => (
|
||||
<ConnectedLink path={`/tag/edit/${id}`}>
|
||||
<TagBadge
|
||||
|
@ -108,13 +129,21 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h4>Configurations</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.detailsConfigurationTitle"
|
||||
defaultMessage="Configurations"
|
||||
/>
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
You can have multiple configurations applied to an individual tag. These
|
||||
configurations can repeat or mix types as necessary. For example, you may utilize
|
||||
three metricbeat configurations alongside one input and filebeat configuration.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beat.detailsConfigurationDescription"
|
||||
defaultMessage="You can have multiple configurations applied to an individual tag. These configurations can repeat
|
||||
or mix types as necessary. For example, you may utilize three metricbeat configurations alongside one input and
|
||||
filebeat configuration."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
|
@ -132,3 +161,4 @@ export class BeatDetailPage extends React.PureComponent<PageProps, PageState> {
|
|||
);
|
||||
}
|
||||
}
|
||||
export const BeatDetailPage = injectI18n(BeatDetailPageUi);
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
// @ts-ignore types for EuiTabs not currently available
|
||||
EuiTabs,
|
||||
} from '@elastic/eui';
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { CMPopulatedBeat } from '../../../common/domain_types';
|
||||
|
@ -32,6 +33,7 @@ interface BeatDetailsPageProps extends URLStateProps<AppURLState> {
|
|||
history: any;
|
||||
libs: FrontendLibs;
|
||||
match: Match;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface BeatDetailsPageState {
|
||||
|
@ -63,6 +65,7 @@ class BeatDetailsPageComponent extends React.PureComponent<
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
const { beat } = this.state;
|
||||
let id;
|
||||
let name;
|
||||
|
@ -72,13 +75,33 @@ class BeatDetailsPageComponent extends React.PureComponent<
|
|||
name = beat.name;
|
||||
}
|
||||
const title = this.state.isLoading
|
||||
? 'Loading'
|
||||
: `Beat: ${name || 'No name receved from beat'} (id: ${id})`;
|
||||
? intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beat.loadingTitle',
|
||||
defaultMessage: 'Loading',
|
||||
})
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beat.beatNameAndIdTitle',
|
||||
defaultMessage: 'Beat: {nameOrNoName} (id: {id})',
|
||||
},
|
||||
{
|
||||
nameOrNoName:
|
||||
name ||
|
||||
intl.formatHTMLMessage({
|
||||
id: 'xpack.beatsManagement.beat.noNameReceivedFromBeatTitle',
|
||||
defaultMessage: 'No name received from beat',
|
||||
}),
|
||||
id,
|
||||
}
|
||||
);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: `/beat/${id}`,
|
||||
name: 'Config',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beat.configTabLabel',
|
||||
defaultMessage: 'Config',
|
||||
}),
|
||||
disabled: false,
|
||||
},
|
||||
// {
|
||||
|
@ -88,7 +111,10 @@ class BeatDetailsPageComponent extends React.PureComponent<
|
|||
// },
|
||||
{
|
||||
id: `/beat/${id}/tags`,
|
||||
name: 'Configuration Tags',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beat.configurationTagsTabLabel',
|
||||
defaultMessage: 'Configuration Tags',
|
||||
}),
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
|
@ -141,12 +167,18 @@ class BeatDetailsPageComponent extends React.PureComponent<
|
|||
}
|
||||
|
||||
private async loadBeat() {
|
||||
const { intl } = this.props;
|
||||
const { beatId } = this.props.match.params;
|
||||
let beat;
|
||||
try {
|
||||
beat = await this.props.libs.beats.get(beatId);
|
||||
if (!beat) {
|
||||
throw new Error('beat not found');
|
||||
throw new Error(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beat.beatNotFoundErrorMessage',
|
||||
defaultMessage: 'beat not found',
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
|
@ -154,4 +186,6 @@ class BeatDetailsPageComponent extends React.PureComponent<
|
|||
this.setState({ beat, isLoading: false });
|
||||
}
|
||||
}
|
||||
export const BeatDetailsPage = withUrlState<BeatDetailsPageProps>(BeatDetailsPageComponent);
|
||||
const BeatDetailsPageUi = withUrlState<BeatDetailsPageProps>(BeatDetailsPageComponent);
|
||||
|
||||
export const BeatDetailsPage = injectI18n(BeatDetailsPageUi);
|
||||
|
|
|
@ -3,11 +3,24 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import * as React from 'react';
|
||||
import { NoDataLayout } from '../components/layouts/no_data';
|
||||
|
||||
export const EnforceSecurityPage: React.SFC<any> = () => (
|
||||
<NoDataLayout title="Security is not enabled" actionSection={[]}>
|
||||
<p>You must enable security in Kibana and Elasticsearch to use Beats central management.</p>
|
||||
export const EnforceSecurityPage = injectI18n(({ intl }) => (
|
||||
<NoDataLayout
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.disabledSecurityTitle',
|
||||
defaultMessage: 'Security is not enabled',
|
||||
})}
|
||||
actionSection={[]}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.disabledSecurityDescription"
|
||||
defaultMessage="You must enable security in Kibana and Elasticsearch to use Beats central management."
|
||||
/>
|
||||
</p>
|
||||
</NoDataLayout>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -3,14 +3,25 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import * as React from 'react';
|
||||
import { NoDataLayout } from '../components/layouts/no_data';
|
||||
|
||||
export const InvalidLicensePage: React.SFC<any> = () => (
|
||||
<NoDataLayout title="Expired license" actionSection={[]}>
|
||||
export const InvalidLicensePage = injectI18n(({ intl }) => (
|
||||
<NoDataLayout
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.invalidLicenseTitle',
|
||||
defaultMessage: 'Expired license',
|
||||
})}
|
||||
actionSection={[]}
|
||||
>
|
||||
<p>
|
||||
Your current license is expired. Enrolled Beats will continue to work, but you need a valid
|
||||
license to access the Beats Management UI.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.invalidLicenseDescription"
|
||||
defaultMessage="Your current license is expired. Enrolled Beats will continue to work, but you need a valid
|
||||
license to access the Beats Management UI."
|
||||
/>
|
||||
</p>
|
||||
</NoDataLayout>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -4,10 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
export class ActivityPage extends React.PureComponent {
|
||||
public render() {
|
||||
return <div>activity logs view</div>;
|
||||
return (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.activityLogsViewTitle"
|
||||
defaultMessage="activity logs view"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
EuiModalHeaderTitle,
|
||||
EuiOverlayMask,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { flatten, intersection, sortBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
|
@ -31,6 +32,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
import { EnrollBeatPage } from './enroll_fragment';
|
||||
|
||||
interface BeatsPageProps extends URLStateProps<AppURLState> {
|
||||
intl: InjectedIntl;
|
||||
libs: FrontendLibs;
|
||||
location: any;
|
||||
beats: CMPopulatedBeat[];
|
||||
|
@ -47,7 +49,7 @@ interface ActionAreaProps extends URLStateProps<AppURLState>, RouteComponentProp
|
|||
libs: FrontendLibs;
|
||||
}
|
||||
|
||||
export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageState> {
|
||||
class BeatsPageUi extends React.PureComponent<BeatsPageProps, BeatsPageState> {
|
||||
public static ActionArea = (props: ActionAreaProps) => (
|
||||
<React.Fragment>
|
||||
<EuiButtonEmpty
|
||||
|
@ -60,7 +62,10 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
);
|
||||
}}
|
||||
>
|
||||
Learn how to install beats
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beats.installBeatsLearningButtonLabel"
|
||||
defaultMessage="Learn how to install beats"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
<EuiButton
|
||||
size="s"
|
||||
|
@ -69,7 +74,10 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
props.goTo(`/overview/beats/enroll`);
|
||||
}}
|
||||
>
|
||||
Enroll Beats
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beats.enrollBeatsButtonLabel"
|
||||
defaultMessage="Enroll Beats"
|
||||
/>
|
||||
</EuiButton>
|
||||
|
||||
{props.location.pathname === '/overview/beats/enroll' && (
|
||||
|
@ -81,7 +89,12 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
style={{ width: '640px' }}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>Enroll a new Beat</EuiModalHeaderTitle>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beats.enrollNewBeatsTitle"
|
||||
defaultMessage="Enroll a new Beat"
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EnrollBeatPage {...props} />
|
||||
|
@ -217,13 +230,38 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
};
|
||||
|
||||
private notifyBeatDisenrolled = async (beats: CMPopulatedBeat[]) => {
|
||||
const { intl } = this.props;
|
||||
let title;
|
||||
let text;
|
||||
if (beats.length === 1) {
|
||||
title = `"${beats[0].name || beats[0].id}" disenrolled`;
|
||||
text = `Beat with ID "${beats[0].id}" was disenrolled.`;
|
||||
title = intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.beatDisenrolledNotificationTitle',
|
||||
defaultMessage: '{firstBeatNameOrId} disenrolled',
|
||||
},
|
||||
{
|
||||
firstBeatNameOrId: `"${beats[0].name || beats[0].id}"`,
|
||||
}
|
||||
);
|
||||
text = intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.beatDisenrolledNotificationDescription',
|
||||
defaultMessage: 'Beat with ID {firstBeatId} was disenrolled.',
|
||||
},
|
||||
{
|
||||
firstBeatId: `"${beats[0].id}"`,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
title = `${beats.length} beats disenrolled`;
|
||||
title = intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.disenrolledBeatsNotificationTitle',
|
||||
defaultMessage: '{beatsLength} beats disenrolled',
|
||||
},
|
||||
{
|
||||
beatsLength: beats.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
@ -241,18 +279,59 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
assignments: BeatsTagAssignment[],
|
||||
tag: string
|
||||
) => {
|
||||
const actionName = action === 'remove' ? 'Removed' : 'Added';
|
||||
const preposition = action === 'remove' ? 'from' : 'to';
|
||||
const beatMessage =
|
||||
assignments.length && assignments.length === 1
|
||||
? `beat "${this.getNameForBeatId(assignments[0].beatId)}"`
|
||||
: `${assignments.length} beats`;
|
||||
const { intl } = this.props;
|
||||
const notificationMessage =
|
||||
action === 'remove'
|
||||
? intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.removedNotificationDescription',
|
||||
defaultMessage:
|
||||
'Removed tag {tag} from {assignmentsLength, plural, one {beat {beatName}} other {# beats}}.',
|
||||
},
|
||||
{
|
||||
tag: `"${tag}"`,
|
||||
assignmentsLength: assignments.length,
|
||||
beatName: `"${this.getNameForBeatId(assignments[0].beatId)}"`,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.addedNotificationDescription',
|
||||
defaultMessage:
|
||||
'Added tag {tag} to {assignmentsLength, plural, one {beat {beatName}} other {# beats}}.',
|
||||
},
|
||||
{
|
||||
tag: `"${tag}"`,
|
||||
assignmentsLength: assignments.length,
|
||||
beatName: `"${this.getNameForBeatId(assignments[0].beatId)}"`,
|
||||
}
|
||||
);
|
||||
const notificationTitle =
|
||||
action === 'remove'
|
||||
? intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.removedNotificationTitle',
|
||||
defaultMessage: '{assignmentsLength, plural, one {Tag} other {Tags}} removed',
|
||||
},
|
||||
{
|
||||
assignmentsLength: assignments.length,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.beats.addedNotificationTitle',
|
||||
defaultMessage: '{assignmentsLength, plural, one {Tag} other {Tags}} added',
|
||||
},
|
||||
{
|
||||
assignmentsLength: assignments.length,
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
notifications: this.state.notifications.concat({
|
||||
color: 'success',
|
||||
id: `tag-${moment.now()}`,
|
||||
text: <p>{`${actionName} tag "${tag}" ${preposition} ${beatMessage}.`}</p>,
|
||||
title: `Tag ${actionName}`,
|
||||
text: <p>{notificationMessage}</p>,
|
||||
title: notificationTitle,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
@ -311,3 +390,8 @@ export class BeatsPage extends React.PureComponent<BeatsPageProps, BeatsPageStat
|
|||
// reduce to result
|
||||
.reduce((acc, cur) => acc || cur, false);
|
||||
}
|
||||
|
||||
const BeatsPageWrapped = injectI18n(BeatsPageUi);
|
||||
export const BeatsPage = BeatsPageWrapped as typeof BeatsPageWrapped & {
|
||||
ActionArea: typeof BeatsPageUi['ActionArea'];
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import 'brace/mode/yaml';
|
||||
|
||||
import 'brace/theme/github';
|
||||
|
@ -18,6 +19,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
interface TagPageProps extends URLStateProps<AppURLState> {
|
||||
libs: FrontendLibs;
|
||||
match: any;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface TagPageState {
|
||||
|
@ -25,7 +27,7 @@ interface TagPageState {
|
|||
tag: BeatTag;
|
||||
}
|
||||
|
||||
export class CreateTagFragment extends React.PureComponent<TagPageProps, TagPageState> {
|
||||
class CreateTagFragment extends React.PureComponent<TagPageProps, TagPageState> {
|
||||
private mode: 'edit' | 'create' = 'create';
|
||||
constructor(props: TagPageProps) {
|
||||
super(props);
|
||||
|
@ -79,7 +81,10 @@ export class CreateTagFragment extends React.PureComponent<TagPageProps, TagPage
|
|||
}
|
||||
onClick={this.saveTag}
|
||||
>
|
||||
Save & Continue
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.createTag.saveAndContinueButtonLabel"
|
||||
defaultMessage="Save & Continue"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -97,9 +102,15 @@ export class CreateTagFragment extends React.PureComponent<TagPageProps, TagPage
|
|||
};
|
||||
|
||||
private saveTag = async () => {
|
||||
const { intl } = this.props;
|
||||
const newTag = await this.props.libs.tags.upsertTag(this.state.tag as BeatTag);
|
||||
if (!newTag) {
|
||||
return alert('error saving tag');
|
||||
return alert(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.createTag.errorSavingTagTitle',
|
||||
defaultMessage: 'error saving tag',
|
||||
})
|
||||
);
|
||||
}
|
||||
this.props.setUrlState({
|
||||
createdTag: newTag.id,
|
||||
|
@ -107,4 +118,6 @@ export class CreateTagFragment extends React.PureComponent<TagPageProps, TagPage
|
|||
this.props.goTo(`/overview/initial/finish`);
|
||||
};
|
||||
}
|
||||
export const CreateTagPageFragment = withUrlState<TagPageProps>(CreateTagFragment);
|
||||
const CreateTagPageFragmentUi = withUrlState<TagPageProps>(CreateTagFragment);
|
||||
|
||||
export const CreateTagPageFragment = injectI18n(CreateTagPageFragmentUi);
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
EuiSelect,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { capitalize } from 'lodash';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
@ -26,6 +27,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
interface BeatsProps extends URLStateProps<AppURLState>, RouteComponentProps<any> {
|
||||
match: any;
|
||||
libs: FrontendLibs;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
export class EnrollBeat extends React.Component<BeatsProps, any> {
|
||||
private pinging = false;
|
||||
|
@ -83,7 +85,7 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
if (this.props.urlState.enrollmentToken && !this.state.enrolledBeat) {
|
||||
this.waitForToken(this.props.urlState.enrollmentToken);
|
||||
}
|
||||
const { goTo } = this.props;
|
||||
const { goTo, intl } = this.props;
|
||||
|
||||
const actions = [];
|
||||
|
||||
|
@ -91,18 +93,27 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
case '/overview/initial/beats':
|
||||
actions.push({
|
||||
goTo: '/overview/initial/tag',
|
||||
name: 'Continue',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.continueButtonLabel',
|
||||
defaultMessage: 'Continue',
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case '/overview/beats/enroll':
|
||||
actions.push({
|
||||
goTo: '/overview/beats/enroll',
|
||||
name: 'Enroll another Beat',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.enrollAnotherBeatButtonLabel',
|
||||
defaultMessage: 'Enroll another Beat',
|
||||
}),
|
||||
newToken: true,
|
||||
});
|
||||
actions.push({
|
||||
goTo: '/overview/beats',
|
||||
name: 'Done',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.doneButtonLabel',
|
||||
defaultMessage: 'Done',
|
||||
}),
|
||||
clearToken: true,
|
||||
});
|
||||
break;
|
||||
|
@ -117,15 +128,26 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Beat type:</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.beatTypeTitle"
|
||||
defaultMessage="Beat type:"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSelect
|
||||
value={this.state.beatType}
|
||||
options={[
|
||||
{ value: 'filebeat', text: 'Filebeat' },
|
||||
{ value: 'metricbeat', text: 'Metricbeat' },
|
||||
{
|
||||
value: 'filebeat',
|
||||
text: 'Filebeat',
|
||||
},
|
||||
{
|
||||
value: 'metricbeat',
|
||||
text: 'Metricbeat',
|
||||
},
|
||||
]}
|
||||
onChange={(e: any) => this.setState({ beatType: e.target.value })}
|
||||
fullWidth={true}
|
||||
|
@ -140,7 +162,12 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Platform:</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.platformTitle"
|
||||
defaultMessage="Platform:"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -176,8 +203,13 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
On the host where your {capitalize(this.state.beatType)} is installed,
|
||||
run:
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.yourBeatTypeHostTitle"
|
||||
defaultMessage="On the host where your {beatType} is installed, run:"
|
||||
values={{
|
||||
beatType: capitalize(this.state.beatType),
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
|
@ -187,7 +219,14 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
className="euiFieldText euiFieldText--fullWidth"
|
||||
style={{ textAlign: 'left' }}
|
||||
>
|
||||
$ {this.state.command} enroll {window.location.protocol}
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.stateCommandEnrollLocationProtocolTitle"
|
||||
defaultMessage="$ {stateCommand} enroll {locationProtocol}"
|
||||
values={{
|
||||
stateCommand: this.state.command,
|
||||
locationProtocol: window.location.protocol,
|
||||
}}
|
||||
/>
|
||||
{`//`}
|
||||
{window.location.host}
|
||||
{this.props.libs.framework.baseURLPath
|
||||
|
@ -203,7 +242,15 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Waiting for {capitalize(this.state.beatType)} to enroll...</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.waitingBeatTypeToEnrollTitle"
|
||||
defaultMessage="Waiting for {beatType} to enroll…"
|
||||
values={{
|
||||
beatType: capitalize(this.state.beatType),
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -218,7 +265,10 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
)}
|
||||
{this.state.enrolledBeat && (
|
||||
<EuiModalBody>
|
||||
The Beat is now enrolled in central management:
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.beatEnrolledTitle"
|
||||
defaultMessage="The Beat is now enrolled in central management:"
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
@ -227,17 +277,32 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
columns={[
|
||||
{
|
||||
field: 'type',
|
||||
name: 'Beat Type',
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.beatTypeColumnName"
|
||||
defaultMessage="Beat Type"
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
name: 'Version',
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.versionColumnName"
|
||||
defaultMessage="Version"
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'host_name',
|
||||
name: 'Hostname',
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.hostnameColumnName"
|
||||
defaultMessage="Hostname"
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
},
|
||||
]}
|
||||
|
@ -276,4 +341,6 @@ export class EnrollBeat extends React.Component<BeatsProps, any> {
|
|||
}
|
||||
}
|
||||
|
||||
export const EnrollBeatPage = withUrlState<BeatsProps>(EnrollBeat);
|
||||
export const EnrollBeatPageUi = withUrlState<BeatsProps>(EnrollBeat);
|
||||
|
||||
export const EnrollBeatPage = injectI18n(EnrollBeatPageUi);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiPageContent } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { BeatTag, CMBeat } from '../../../common/domain_types';
|
||||
|
@ -14,6 +15,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
interface PageProps extends URLStateProps<AppURLState>, RouteComponentProps<any> {
|
||||
loadBeats: any;
|
||||
libs: FrontendLibs;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
export class FinishWalkthrough extends React.Component<PageProps, any> {
|
||||
constructor(props: PageProps) {
|
||||
|
@ -47,10 +49,22 @@ export class FinishWalkthrough extends React.Component<PageProps, any> {
|
|||
<EuiPageContent>
|
||||
<EuiEmptyPrompt
|
||||
iconType="logoBeats"
|
||||
title={<h2>Your Beat is enrolled. What's next?</h2>}
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.nextStepTitle"
|
||||
defaultMessage="Your Beat is enrolled. What's next?"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<React.Fragment>
|
||||
<p>Start your Beat to check for configuration errors, then click Done.</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.nextStepDescription"
|
||||
defaultMessage="Start your Beat to check for configuration errors, then click Done."
|
||||
/>
|
||||
</p>
|
||||
</React.Fragment>
|
||||
}
|
||||
actions={
|
||||
|
@ -61,7 +75,10 @@ export class FinishWalkthrough extends React.Component<PageProps, any> {
|
|||
goTo('/overview/beats');
|
||||
}}
|
||||
>
|
||||
Done
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.firstBeatEnrollingDoneButtonLabel"
|
||||
defaultMessage="Done"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
/>
|
||||
|
@ -75,16 +92,32 @@ export class FinishWalkthrough extends React.Component<PageProps, any> {
|
|||
beats.map(({ id }) => ({ beatId: id, tag: tag.id }));
|
||||
|
||||
private assignTagToBeat = async () => {
|
||||
const { intl } = this.props;
|
||||
if (!this.props.urlState.enrollmentToken) {
|
||||
return alert('Invalid URL, no enrollmentToken found');
|
||||
return alert(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.assignTagToBeatInvalidURLNoTokenFountTitle',
|
||||
defaultMessage: 'Invalid URL, no enrollmentToken found',
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!this.props.urlState.createdTag) {
|
||||
return alert('Invalid URL, no createdTag found');
|
||||
return alert(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.assignTagToBeatInvalidURLNoTagFoundTitle',
|
||||
defaultMessage: 'Invalid URL, no createdTag found',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const beat = await this.props.libs.beats.getBeatWithToken(this.props.urlState.enrollmentToken);
|
||||
if (!beat) {
|
||||
return alert('Error: Beat not enrolled properly');
|
||||
return alert(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.assignTagToBeatNotEnrolledProperlyTitle',
|
||||
defaultMessage: 'Error: Beat not enrolled properly',
|
||||
})
|
||||
);
|
||||
}
|
||||
const tags = await this.props.libs.tags.getTagsWithIds([this.props.urlState.createdTag]);
|
||||
|
||||
|
@ -98,4 +131,6 @@ export class FinishWalkthrough extends React.Component<PageProps, any> {
|
|||
};
|
||||
}
|
||||
|
||||
export const FinishWalkthroughPage = withUrlState<PageProps>(FinishWalkthrough);
|
||||
const FinishWalkthroughPageUi = withUrlState<PageProps>(FinishWalkthrough);
|
||||
|
||||
export const FinishWalkthroughPage = injectI18n(FinishWalkthroughPageUi);
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
EuiTabs,
|
||||
} from '@elastic/eui';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { CMPopulatedBeat } from '../../../common/domain_types';
|
||||
|
@ -32,6 +33,7 @@ import { TagsPage } from './tags';
|
|||
interface MainPagesProps extends URLStateProps<AppURLState> {
|
||||
libs: FrontendLibs;
|
||||
location: any;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface MainPagesState {
|
||||
|
@ -68,6 +70,7 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
if (
|
||||
this.state.loadedBeatsAtLeastOnce &&
|
||||
this.state.unfilteredBeats.length === 0 &&
|
||||
|
@ -78,7 +81,12 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
const tabs = [
|
||||
{
|
||||
id: '/overview/beats',
|
||||
name: 'Enrolled Beats',
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beats.enrolledBeatsTabTitle"
|
||||
defaultMessage="Enrolled Beats"
|
||||
/>
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
// {
|
||||
|
@ -88,7 +96,12 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
// },
|
||||
{
|
||||
id: '/overview/tags',
|
||||
name: 'Configuration tags',
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.beats.configurationTagsTabTitle"
|
||||
defaultMessage="Configuration tags"
|
||||
/>
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
|
@ -96,19 +109,28 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
const walkthroughSteps = [
|
||||
{
|
||||
id: '/overview/initial/beats',
|
||||
name: 'Enroll Beat',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.enrollBeatStepLabel',
|
||||
defaultMessage: 'Enroll Beat',
|
||||
}),
|
||||
disabled: false,
|
||||
page: EnrollBeatPage,
|
||||
},
|
||||
{
|
||||
id: '/overview/initial/tag',
|
||||
name: 'Create tag',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.createTagStepLabel',
|
||||
defaultMessage: 'Create tag',
|
||||
}),
|
||||
disabled: false,
|
||||
page: CreateTagPageFragment,
|
||||
},
|
||||
{
|
||||
id: '/overview/initial/finish',
|
||||
name: 'finish',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.finishStepLabel',
|
||||
defaultMessage: 'Finish',
|
||||
}),
|
||||
disabled: false,
|
||||
page: FinishWalkthroughPage,
|
||||
},
|
||||
|
@ -117,16 +139,27 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
if (this.props.location.pathname === '/overview/initial/help') {
|
||||
return (
|
||||
<NoDataLayout
|
||||
title="Beats central management (Beta)"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.beatsCentralManagementTitle',
|
||||
defaultMessage: 'Beats central management (Beta)',
|
||||
})}
|
||||
actionSection={
|
||||
<ConnectedLink path="/overview/initial/beats">
|
||||
<EuiButton color="primary" fill>
|
||||
Enroll Beat
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.enrollBeatButtonLabel"
|
||||
defaultMessage="Enroll Beat"
|
||||
/>
|
||||
</EuiButton>
|
||||
</ConnectedLink>
|
||||
}
|
||||
>
|
||||
<p>Manage your configurations in a central location.</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.enrollBeat.beatsCentralManagementDescription"
|
||||
defaultMessage="Manage your configurations in a central location."
|
||||
/>
|
||||
</p>
|
||||
</NoDataLayout>
|
||||
);
|
||||
}
|
||||
|
@ -134,7 +167,10 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
if (this.props.location.pathname.includes('/overview/initial')) {
|
||||
return (
|
||||
<WalkthroughLayout
|
||||
title="Get started with Beats central management"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.enrollBeat.getStartedBeatsCentralManagementTitle',
|
||||
defaultMessage: 'Get started with Beats central management',
|
||||
})}
|
||||
walkthroughSteps={walkthroughSteps}
|
||||
goTo={this.props.goTo}
|
||||
activePath={this.props.location.pathname}
|
||||
|
@ -171,7 +207,10 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
|
||||
return (
|
||||
<PrimaryLayout
|
||||
title="Beats"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatsRouteTitle',
|
||||
defaultMessage: 'Beats',
|
||||
})}
|
||||
actionSection={
|
||||
<Switch>
|
||||
<Route
|
||||
|
@ -192,7 +231,10 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
<EuiTabs>{renderedTabs}</EuiTabs>
|
||||
|
||||
<RouteWithBreadcrumb
|
||||
title="Beats List"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.beatsListRouteTitle',
|
||||
defaultMessage: 'Beats List',
|
||||
})}
|
||||
path="/overview/beats/:action?/:enrollmentToken?"
|
||||
render={(props: any) => (
|
||||
<BeatsPage
|
||||
|
@ -205,7 +247,10 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
)}
|
||||
/>
|
||||
<RouteWithBreadcrumb
|
||||
title="Activity Overview"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.activityOverviewRouteTitle',
|
||||
defaultMessage: 'Activity Overview',
|
||||
})}
|
||||
path="/overview/activity"
|
||||
exact={true}
|
||||
render={(props: any) => (
|
||||
|
@ -213,7 +258,10 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
)}
|
||||
/>
|
||||
<RouteWithBreadcrumb
|
||||
title="Tags List"
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tagsListRouteTitle',
|
||||
defaultMessage: 'Tags List',
|
||||
})}
|
||||
path="/overview/tags"
|
||||
exact={true}
|
||||
render={(props: any) => <TagsPage {...this.props} libs={this.props.libs} {...props} />}
|
||||
|
@ -251,4 +299,6 @@ class MainPagesComponent extends React.PureComponent<MainPagesProps, MainPagesSt
|
|||
};
|
||||
}
|
||||
|
||||
export const MainPages = withUrlState<MainPagesProps>(MainPagesComponent);
|
||||
const MainPagesUi = withUrlState<MainPagesProps>(MainPagesComponent);
|
||||
|
||||
export const MainPages = injectI18n(MainPagesUi);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { BeatTag } from '../../../common/domain_types';
|
||||
import { AppURLState } from '../../app';
|
||||
|
@ -16,6 +17,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
|
||||
interface TagsPageProps extends URLStateProps<AppURLState> {
|
||||
libs: FrontendLibs;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface TagsPageState {
|
||||
|
@ -23,7 +25,7 @@ interface TagsPageState {
|
|||
tableRef: any;
|
||||
}
|
||||
|
||||
export class TagsPage extends React.PureComponent<TagsPageProps, TagsPageState> {
|
||||
class TagsPageUi extends React.PureComponent<TagsPageProps, TagsPageState> {
|
||||
public static ActionArea = ({ goTo }: TagsPageProps) => (
|
||||
<EuiButton
|
||||
size="s"
|
||||
|
@ -32,7 +34,10 @@ export class TagsPage extends React.PureComponent<TagsPageProps, TagsPageState>
|
|||
goTo('/tag/create');
|
||||
}}
|
||||
>
|
||||
Add Tag
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tags.addTagButtonLabel"
|
||||
defaultMessage="Add Tag"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
|
||||
|
@ -78,13 +83,18 @@ export class TagsPage extends React.PureComponent<TagsPageProps, TagsPageState>
|
|||
}
|
||||
|
||||
private handleTagsAction = async (action: AssignmentActionType, payload: any) => {
|
||||
const { intl } = this.props;
|
||||
switch (action) {
|
||||
case AssignmentActionType.Delete:
|
||||
const tags = this.getSelectedTags().map((tag: BeatTag) => tag.id);
|
||||
const success = await this.props.libs.tags.delete(tags);
|
||||
if (!success) {
|
||||
alert(
|
||||
'Some of these tags might be assigned to beats. Please ensure tags being removed are not activly assigned'
|
||||
intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tags.someTagsMightBeAssignedToBeatsTitle',
|
||||
defaultMessage:
|
||||
'Some of these tags might be assigned to beats. Please ensure tags being removed are not activly assigned',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.loadTags();
|
||||
|
@ -109,3 +119,8 @@ export class TagsPage extends React.PureComponent<TagsPageProps, TagsPageState>
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
const TagsPageWrapped = injectI18n(TagsPageUi);
|
||||
export const TagsPage = TagsPageWrapped as typeof TagsPageWrapped & {
|
||||
ActionArea: typeof TagsPageUi['ActionArea'];
|
||||
};
|
||||
|
|
|
@ -3,14 +3,26 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import * as React from 'react';
|
||||
import { NoDataLayout } from '../components/layouts/no_data';
|
||||
|
||||
export const NoAccessPage: React.SFC<any> = () => (
|
||||
<NoDataLayout title="Access denied" actionSection={[]}>
|
||||
export const NoAccessPage = injectI18n(({ intl }) => (
|
||||
<NoDataLayout
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.noAccess.accessDeniedTitle',
|
||||
defaultMessage: 'Access denied',
|
||||
})}
|
||||
actionSection={[]}
|
||||
>
|
||||
<p>
|
||||
You are not authorized to access Beats central management. To use Beats central management,
|
||||
you need the privileges granted by the `beats_admin` role.
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.noAccess.accessDeniedDescription"
|
||||
defaultMessage="You are not authorized to access Beats central management. To use Beats central management,
|
||||
you need the privileges granted by the {beatsAdminRole} role."
|
||||
values={{ beatsAdminRole: '`beats_admin`' }}
|
||||
/>
|
||||
</p>
|
||||
</NoDataLayout>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'brace/theme/github';
|
|||
|
||||
import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import * as euiVars from '@elastic/eui/dist/eui_theme_k6_light.json';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { sample } from 'lodash';
|
||||
import React from 'react';
|
||||
import { UNIQUENESS_ENFORCING_TYPES } from 'x-pack/plugins/beats_management/common/constants';
|
||||
|
@ -21,6 +22,7 @@ import { FrontendLibs } from '../../lib/lib';
|
|||
interface TagPageProps extends URLStateProps<AppURLState> {
|
||||
libs: FrontendLibs;
|
||||
match: any;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface TagPageState {
|
||||
|
@ -56,9 +58,26 @@ export class TagPageComponent extends React.PureComponent<TagPageProps, TagPageS
|
|||
}
|
||||
}
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<PrimaryLayout
|
||||
title={this.mode === 'create' ? 'Create Tag' : `Update Tag: ${this.state.tag.id}`}
|
||||
title={
|
||||
this.mode === 'create'
|
||||
? intl.formatMessage({
|
||||
id: 'xpack.beatsManagement.tag.createTagTitle',
|
||||
defaultMessage: 'Create Tag',
|
||||
})
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: 'xpack.beatsManagement.tag.updateTagTitle',
|
||||
defaultMessage: 'Update Tag: {tagId}',
|
||||
},
|
||||
{
|
||||
tagId: this.state.tag.id,
|
||||
}
|
||||
)
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<TagEdit
|
||||
|
@ -91,12 +110,18 @@ export class TagPageComponent extends React.PureComponent<TagPageProps, TagPageS
|
|||
}
|
||||
onClick={this.saveTag}
|
||||
>
|
||||
Save
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.saveButtonLabel"
|
||||
defaultMessage="Save"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={() => this.props.goTo('/overview/tags')}>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
id="xpack.beatsManagement.tag.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -141,4 +166,6 @@ export class TagPageComponent extends React.PureComponent<TagPageProps, TagPageS
|
|||
.map(({ type }) => UNIQUENESS_ENFORCING_TYPES.some(uniqueType => uniqueType === type))
|
||||
.reduce((acc, cur) => (cur ? acc + 1 : acc), 0);
|
||||
}
|
||||
export const TagPage = withUrlState<TagPageProps>(TagPageComponent);
|
||||
export const TagPageUi = withUrlState<TagPageProps>(TagPageComponent);
|
||||
|
||||
export const TagPage = injectI18n(TagPageUi);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { HashRouter, Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { Header } from './components/layouts/header';
|
||||
|
@ -26,11 +27,15 @@ export const PageRouter: React.SFC<{ libs: FrontendLibs }> = ({ libs }) => {
|
|||
breadcrumbs={[
|
||||
{
|
||||
href: '#/management',
|
||||
text: 'Management',
|
||||
text: i18n.translate('xpack.beatsManagement.router.managementTitle', {
|
||||
defaultMessage: 'Management',
|
||||
}),
|
||||
},
|
||||
{
|
||||
href: '#/management/beats_management',
|
||||
text: 'Beats',
|
||||
text: i18n.translate('xpack.beatsManagement.router.beatsTitle', {
|
||||
defaultMessage: 'Beats',
|
||||
}),
|
||||
},
|
||||
...breadcrumbs,
|
||||
]}
|
||||
|
@ -55,11 +60,16 @@ export const PageRouter: React.SFC<{ libs: FrontendLibs }> = ({ libs }) => {
|
|||
<Route path="/overview" render={(props: any) => <MainPages {...props} libs={libs} />} />
|
||||
<RouteWithBreadcrumb
|
||||
title={params => {
|
||||
return `Beats: ${params.beatId}`;
|
||||
return i18n.translate('xpack.beatsManagement.router.beatTitle', {
|
||||
defaultMessage: 'Beats: {beatId}',
|
||||
values: { beatId: params.beatId },
|
||||
});
|
||||
}}
|
||||
parentBreadcrumbs={[
|
||||
{
|
||||
text: 'Beats List',
|
||||
text: i18n.translate('xpack.beatsManagement.router.beatsListTitle', {
|
||||
defaultMessage: 'Beats List',
|
||||
}),
|
||||
href: '#/management/beats_management/overview/beats',
|
||||
},
|
||||
]}
|
||||
|
@ -69,13 +79,20 @@ export const PageRouter: React.SFC<{ libs: FrontendLibs }> = ({ libs }) => {
|
|||
<RouteWithBreadcrumb
|
||||
title={params => {
|
||||
if (params.action === 'create') {
|
||||
return 'Create Tag';
|
||||
return i18n.translate('xpack.beatsManagement.router.createTagTitle', {
|
||||
defaultMessage: 'Create Tag',
|
||||
});
|
||||
}
|
||||
return `Tag: ${params.tagid}`;
|
||||
return i18n.translate('xpack.beatsManagement.router.tagTitle', {
|
||||
defaultMessage: 'Tag: {tagId}',
|
||||
values: { tagId: params.tagid },
|
||||
});
|
||||
}}
|
||||
parentBreadcrumbs={[
|
||||
{
|
||||
text: 'Tags List',
|
||||
text: i18n.translate('xpack.beatsManagement.router.tagsListTitle', {
|
||||
defaultMessage: 'Tags List',
|
||||
}),
|
||||
href: '#/management/beats_management/overview/tags',
|
||||
},
|
||||
]}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue