/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import React, { ChangeEvent, Component, Fragment } from "react"; import { EuiBasicTable, EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem, EuiForm, EuiModal, EuiModalBody, EuiModalFooter, EuiModalHeader, EuiModalHeaderTitle, EuiOverlayMask, EuiSpacer, EuiTableSelectionType, EuiCheckbox, EuiText, EuiFormRow, EuiPanel, EuiTitle, EuiFormHelpText, EuiHorizontalRule, EuiIcon, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem, EuiCallOut, EuiTableSortingType, // @ts-ignore Pagination, // @ts-ignore Criteria, } from "@elastic/eui"; import { AddFieldsColumns } from "../../utils/constants"; import { DEFAULT_PAGE_SIZE_OPTIONS } from "../../../Rollups/utils/constants"; import { isNumericMapping } from "../../utils/helpers"; import { FieldItem, MetricItem } from "../../../../../models/interfaces"; interface MetricsCalculationProps { fieldsOption: FieldItem[]; selectedMetrics: MetricItem[]; metricError: string; onMetricSelectionChange: (selectedFields: MetricItem[]) => void; } interface MetricsCalculationState { isModalVisible: boolean; searchText: string; selectedFieldType: EuiComboBoxOptionOption[]; selectedFields: FieldItem[]; allSelectedFields: FieldItem[]; from: number; size: number; sortField: string; sortDirection: string; isDisableOpen: boolean; isEnableOpen: boolean; metricsShown: MetricItem[]; } export default class MetricsCalculation extends Component { constructor(props: MetricsCalculationProps) { super(props); const { selectedMetrics } = this.props; this.state = { isModalVisible: false, searchText: "", selectedFieldType: [], selectedFields: [], allSelectedFields: [], metricsShown: selectedMetrics.slice(0, 10), from: 0, size: 10, sortField: "source_field", sortDirection: "desc", isDisableOpen: false, isEnableOpen: false, }; } closeModal = () => this.setState({ isModalVisible: false }); showModal = () => this.setState({ isModalVisible: true }); closeDisable = () => this.setState({ isDisableOpen: false }); showDisable = () => this.setState({ isDisableOpen: true }); closeEnable = () => this.setState({ isEnableOpen: false }); showEnable = () => this.setState({ isEnableOpen: true }); onChangeFieldType = (options: EuiComboBoxOptionOption[]): void => { this.setState({ selectedFieldType: options }); }; onSelectionChange = (selectedFields: FieldItem[]): void => { this.setState({ selectedFields }); }; onClickAdd() { const { onMetricSelectionChange, selectedMetrics } = this.props; const { selectedFields, allSelectedFields, from, size } = this.state; // Clone selectedMetrics let updatedMetrics = Array.from(selectedMetrics); const toAddFields = Array.from(selectedFields); // Loop through selectedFields to see if existing Metrics are removed selectedMetrics.map((metric) => { if (allSelectedFields.includes(metric.source_field)) { const index = toAddFields.indexOf(metric.source_field); toAddFields.splice(index, 1); } }); const toAdd: MetricItem[] = toAddFields.map((field) => { return { source_field: field, all: false, min: false, max: false, sum: false, avg: false, value_count: false, }; }); const result = updatedMetrics.length ? updatedMetrics.concat(toAdd) : toAdd; onMetricSelectionChange(result); this.setState({ allSelectedFields: allSelectedFields.concat(toAddFields), metricsShown: result.slice(from, from + size), }); this.forceUpdate(); } deleteField(item: MetricItem) { const { selectedMetrics, onMetricSelectionChange } = this.props; const { selectedFields, allSelectedFields, from, size } = this.state; selectedMetrics.splice(selectedMetrics.indexOf(item), 1); selectedFields.splice(selectedFields.indexOf(item.source_field), 1); allSelectedFields.splice(allSelectedFields.indexOf(item.source_field), 1); onMetricSelectionChange(selectedMetrics); this.setState({ selectedFields, allSelectedFields, metricsShown: selectedMetrics.slice(from, size) }); } setChecked = (e: ChangeEvent, method: string, item: MetricItem): void => { const { selectedMetrics, onMetricSelectionChange } = this.props; const index = selectedMetrics.indexOf(item); const newItem: MetricItem = item; //There should be a smarter way to do this switch (method) { case "all": { if (!item.all) { newItem.all = true; newItem.min = true; newItem.max = true; newItem.sum = true; newItem.avg = true; newItem.value_count = true; } else { newItem.all = false; } break; } case "min": { newItem.min = !item.min; break; } case "max": { newItem.max = !item.max; break; } case "sum": { newItem.sum = !item.sum; break; } case "avg": { newItem.avg = !item.avg; break; } case "value_count": { newItem.value_count = !item.value_count; break; } } selectedMetrics[index] = newItem; onMetricSelectionChange(selectedMetrics); }; onClickDisable(method: string) { const { selectedMetrics, onMetricSelectionChange } = this.props; selectedMetrics.map((metric) => { metric[method] = false; }); onMetricSelectionChange(selectedMetrics); } onClickEnable(method: string) { const { selectedMetrics, onMetricSelectionChange } = this.props; selectedMetrics.map((metric) => { metric[method] = true; }); onMetricSelectionChange(selectedMetrics); } onTableChange = ({ page: tablePage, sort }: Criteria): void => { const { index: page, size } = tablePage; const { field: sortField, direction: sortDirection } = sort; const { selectedMetrics } = this.props; this.setState({ from: page * size, size, sortField, sortDirection, metricsShown: selectedMetrics.slice(page * size, page * size + size), }); }; render() { const { fieldsOption, selectedMetrics, metricError } = this.props; const { isModalVisible, selectedFields, isDisableOpen, isEnableOpen, from, size, sortDirection, sortField, metricsShown } = this.state; const page = Math.floor(from / size); const pagination: Pagination = { pageIndex: page, pageSize: size, pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS, totalItemCount: selectedMetrics.length, }; const sorting: EuiTableSortingType = { sort: { direction: sortDirection, field: sortField, }, }; const selection: EuiTableSelectionType = { selectable: (field) => isNumericMapping(field.type), onSelectionChange: this.onSelectionChange, initialSelected: selectedFields, }; const metricsColumns = [ { field: "source_field.label", name: "Field Name", }, { field: "all", name: "All", align: "center", truncateText: true, render: (all: boolean, item: MetricItem) => ( this.setChecked(e, "all", item)} data-test-subj={`all-${item.source_field.label}`} /> ), }, { field: "min", name: "Min", align: "center", render: (min: boolean, item: MetricItem) => ( this.setChecked(e, "min", item)} data-test-subj={`min-${item.source_field.label}`} /> ), }, { field: "max", name: "Max", align: "center", render: (max: boolean, item: MetricItem) => ( this.setChecked(e, "max", item)} data-test-subj={`max-${item.source_field.label}`} /> ), }, { field: "sum", name: "Sum", align: "center", render: (sum: boolean, item: MetricItem) => ( this.setChecked(e, "sum", item)} data-test-subj={`sum-${item.source_field.label}`} /> ), }, { field: "avg", name: "Avg", align: "center", render: (avg: boolean, item: MetricItem) => ( this.setChecked(e, "avg", item)} data-test-subj={`avg-${item.source_field.label}`} /> ), }, { field: "value_count", name: "Value count", align: "center", render: (value_count: boolean, item: MetricItem) => ( this.setChecked(e, "value_count", item)} data-test-subj={`valueCount-${item.source_field.label}`} /> ), }, { name: "Actions", actions: [ { render: (item: MetricItem) => { return ( this.deleteField(item)} data-test-subj={`delete-${item.source_field.label}`} /> ); }, }, ], }, ]; const disableActions = [ { this.closeDisable(); this.onClickDisable("min"); }} data-test-subj="disable_min" > Min , { this.closeDisable(); this.onClickDisable("max"); }} data-test-subj="disable_max" > Max , { this.closeDisable(); this.onClickDisable("sum"); }} data-test-subj="disable_sum" > Sum , { this.closeDisable(); this.onClickDisable("avg"); }} data-test-subj="disable_avg" > Avg , { this.closeDisable(); this.onClickDisable("value_count"); }} data-test-subj="disable_value_count" > Value count , ]; const enableActions = [ { this.closeEnable(); this.onClickEnable("min"); }} data-test-subj="enable_min" > Min , { this.closeEnable(); this.onClickEnable("max"); }} data-test-subj="enable_max" > Max , { this.closeEnable(); this.onClickEnable("sum"); }} data-test-subj="enable_sum" > Sum , { this.closeEnable(); this.onClickEnable("avg"); }} data-test-subj="enable_avg" > Avg , { this.closeEnable(); this.onClickEnable("value_count"); }} data-test-subj="enable_value_count" > Value count , ]; return (

Additional metrics

{` (${selectedMetrics.length})`}

– optional
Disable all } isOpen={isDisableOpen} closePopover={this.closeDisable} panelPaddingSize="none" anchorPosition="downLeft" > Enable all } isOpen={isEnableOpen} closePopover={this.closeEnable} panelPaddingSize="none" anchorPosition="downLeft" > Add fields
You can aggregate additional fields from the source index into the target index. Rollup supports the terms aggregation (for all field types) and histogram aggregation (for numeric fields).
{metricError != "" && (

{metricError}

)} No fields added for metrics Add fields } tableLayout="auto" onChange={this.onTableChange} pagination={pagination} sorting={sorting} /> {isModalVisible && ( Add fields Cancel { this.closeModal(); this.onClickAdd(); }} data-test-subj="addFieldsMetricAdd" > Add )}
); } }