From 4c5e20f2d01cd4a8684c8b2c7f5d9ed777bf68a4 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Tue, 1 Nov 2022 18:12:29 -0700 Subject: [PATCH 1/4] adding vizualization components Signed-off-by: Shenoy Pratik --- .../common/constants/metrics.ts | 11 + .../common/types/metrics.ts | 22 ++ .../custom_panels/helpers/utils.tsx | 120 ++++++++++- .../visualization_container.tsx | 58 +++-- .../components/metrics/helpers/utils.tsx | 87 ++++++++ .../public/components/metrics/index.tsx | 178 +++++++++++---- .../components/metrics/top_menu/top_menu.scss | 16 ++ .../components/metrics/top_menu/top_menu.tsx | 204 ++++++++++++++++++ .../components/metrics/view/empty_view.tsx | 25 ++- .../components/metrics/view/metrics_grid.scss | 8 + .../components/metrics/view/metrics_grid.tsx | 193 +++++++++++++++++ .../metrics/view/metrics_table_view.tsx | 10 - 12 files changed, 848 insertions(+), 84 deletions(-) create mode 100644 dashboards-observability/common/types/metrics.ts create mode 100644 dashboards-observability/public/components/metrics/top_menu/top_menu.scss create mode 100644 dashboards-observability/public/components/metrics/top_menu/top_menu.tsx create mode 100644 dashboards-observability/public/components/metrics/view/metrics_grid.scss create mode 100644 dashboards-observability/public/components/metrics/view/metrics_grid.tsx delete mode 100644 dashboards-observability/public/components/metrics/view/metrics_table_view.tsx diff --git a/dashboards-observability/common/constants/metrics.ts b/dashboards-observability/common/constants/metrics.ts index 370bd47e1..04b9034eb 100644 --- a/dashboards-observability/common/constants/metrics.ts +++ b/dashboards-observability/common/constants/metrics.ts @@ -9,3 +9,14 @@ export const PPL_PROMETHEUS_CATALOG_REQUEST = // redux export const REDUX_SLICE_METRICS = 'metrics'; + +export const resolutionOptions = [ + // { value: 'ms', text: 'milliseconds' }, + { value: 's', text: 'seconds' }, + { value: 'm', text: 'minutes' }, + { value: 'h', text: 'hours' }, + { value: 'd', text: 'days' }, + { value: 'M', text: 'Months' }, + { value: 'q', text: 'quarters' }, + { value: 'y', text: 'years' }, +]; diff --git a/dashboards-observability/common/types/metrics.ts b/dashboards-observability/common/types/metrics.ts new file mode 100644 index 000000000..64ebd07ab --- /dev/null +++ b/dashboards-observability/common/types/metrics.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisualizationType } from './custom_panels'; + +export interface MetricData { + metricId: string; + metricType: 'savedCustomMetric' | 'prometheusMetric'; + metricName: string; +} + +export interface MetricType extends VisualizationType { + id: string; + savedVisualizationId: string; + x: number; + y: number; + w: number; + h: number; + metricType: 'savedCustomMetric' | 'prometheusMetric'; +} diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index 5f3c67ac9..70e16dd67 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -18,6 +18,7 @@ import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_pa import { VisualizationType, SavedVisualizationType } from '../../../../common/types/custom_panels'; import { Visualization } from '../../visualizations/visualization'; import { getVizContainerProps } from '../../../components/visualizations/charts/helpers'; +import { string } from 'joi'; /* * "Utils" This file contains different reused functions in operational panels @@ -88,7 +89,8 @@ const queryAccumulator = ( timestampField: string, startTime: string, endTime: string, - panelFilterQuery: string + panelFilterQuery: string, + spanParam: string | undefined ) => { const indexMatchArray = originalQuery.match(PPL_INDEX_REGEX); if (indexMatchArray == null) { @@ -100,7 +102,17 @@ const queryAccumulator = ( startTime )}' and ${timestampField} <= '${convertDateTime(endTime, false)}'`; const pplFilterQuery = panelFilterQuery === '' ? '' : ` | ${panelFilterQuery}`; - return indexPartOfQuery + timeQueryFilter + pplFilterQuery + filterPartOfQuery; + const finalQuery = indexPartOfQuery + timeQueryFilter + pplFilterQuery + filterPartOfQuery; + console.log('spanParam in query accu', spanParam); + if (spanParam === undefined) { + console.log('normal query'); + return finalQuery; + } else { + return finalQuery.replace( + new RegExp(`span\\(${timestampField},(.*?)\\)`), + `span(${timestampField},${spanParam})` + ); + } }; // PPL Service requestor @@ -154,6 +166,7 @@ export const getQueryResponse = ( type: string, startTime: string, endTime: string, + spanParam: string | undefined, setVisualizationData: React.Dispatch>, setIsLoading: React.Dispatch>, setIsError: React.Dispatch>, @@ -165,7 +178,14 @@ export const getQueryResponse = ( let finalQuery = ''; try { - finalQuery = queryAccumulator(query, timestampField, startTime, endTime, filterQuery); + finalQuery = queryAccumulator( + query, + timestampField, + startTime, + endTime, + filterQuery, + spanParam + ); } catch (error) { const errorMessage = 'Issue in building final query'; setIsError(errorMessage); @@ -173,6 +193,7 @@ export const getQueryResponse = ( setIsLoading(false); return; } + console.log('query here:', finalQuery); pplServiceRequestor(pplService, finalQuery, type, setVisualizationData, setIsLoading, setIsError); }; @@ -185,6 +206,7 @@ export const renderSavedVisualization = async ( startTime: string, endTime: string, filterQuery: string, + spanParam: string | undefined, setVisualizationTitle: React.Dispatch>, setVisualizationType: React.Dispatch>, setVisualizationData: React.Dispatch>, @@ -219,6 +241,7 @@ export const renderSavedVisualization = async ( visualization.type, startTime, endTime, + spanParam, setVisualizationData, setIsLoading, setIsError, @@ -227,6 +250,83 @@ export const renderSavedVisualization = async ( ); }; +const createCatalogVisualizationMetaData = ( + catalogSource: string, + visualizationQuery: string, + visualizationType: string, + visualizationTimeField: string +) => { + return { + name: catalogSource, + description: '', + query: visualizationQuery, + type: visualizationType, + selected_date_range: { + start: 'now/y', + end: 'now', + text: '', + }, + selected_timestamp: { + name: visualizationTimeField, + type: 'timestamp', + }, + selected_fields: { + text: '', + tokens: [], + }, + }; +}; + +//Creates a catalogVisualization for a runtime catalog based PPL query and runs getQueryResponse +export const renderCatalogVisualization = async ( + http: CoreStart['http'], + pplService: PPLService, + catalogSource: string, + startTime: string, + endTime: string, + filterQuery: string, + spanParam: string | undefined, + setVisualizationTitle: React.Dispatch>, + setVisualizationType: React.Dispatch>, + setVisualizationData: React.Dispatch>, + setVisualizationMetaData: React.Dispatch>, + setIsLoading: React.Dispatch>, + setIsError: React.Dispatch>, + spanResolution?: string +) => { + setIsLoading(true); + setIsError(''); + + const visualizationType = 'line'; + const visualizationTimeField = '@timestamp'; + const visualizationQuery = `source = ${catalogSource} | stats avg(@value) by span(${visualizationTimeField},1h)`; + + const visualizationMetaData = createCatalogVisualizationMetaData( + catalogSource, + visualizationQuery, + visualizationType, + visualizationTimeField + ); + setVisualizationTitle(catalogSource); + setVisualizationType(visualizationType); + + setVisualizationMetaData(visualizationMetaData); + + getQueryResponse( + pplService, + visualizationQuery, + visualizationType, + startTime, + endTime, + spanParam, + setVisualizationData, + setIsLoading, + setIsError, + filterQuery, + visualizationTimeField + ); +}; + // Function to store recently used time filters and set start and end time. export const onTimeChange = ( start: ShortDate, @@ -305,3 +405,17 @@ export const displayVisualization = (metaData: any, data: any, type: string) => /> ); }; + +// ; diff --git a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx index bb1a25e04..d89b7d6e9 100644 --- a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx +++ b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx @@ -20,7 +20,11 @@ import { import React, { useEffect, useMemo, useState } from 'react'; import { CoreStart } from '../../../../../../../src/core/public'; import PPLService from '../../../../services/requests/ppl'; -import { displayVisualization, renderSavedVisualization } from '../../helpers/utils'; +import { + displayVisualization, + renderCatalogVisualization, + renderSavedVisualization, +} from '../../helpers/utils'; import './visualization_container.scss'; /* @@ -40,6 +44,8 @@ import './visualization_container.scss'; * pplFilterValue: string with panel PPL filter value * showFlyout: function to show the flyout * removeVisualization: function to remove all the visualizations + * catalogVisualization: boolean pointing if the container is used for catalog metrics + * spanParam: Override the span(timestamp, 1h) in visualization to span(timestamp, spanParam) */ interface Props { @@ -57,6 +63,8 @@ interface Props { cloneVisualization?: (visualzationTitle: string, savedVisualizationId: string) => void; showFlyout?: (isReplacement?: boolean | undefined, replaceVizId?: string | undefined) => void; removeVisualization?: (visualizationId: string) => void; + catalogVisualization?: boolean; + spanParam?: string; } export const VisualizationContainer = ({ @@ -74,6 +82,8 @@ export const VisualizationContainer = ({ cloneVisualization, showFlyout, removeVisualization, + catalogVisualization, + spanParam, }: Props) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [disablePopover, setDisablePopover] = useState(false); @@ -125,20 +135,38 @@ export const VisualizationContainer = ({ } const loadVisaulization = async () => { - await renderSavedVisualization( - http, - pplService, - savedVisualizationId, - fromTime, - toTime, - pplFilterValue, - setVisualizationTitle, - setVisualizationType, - setVisualizationData, - setVisualizationMetaData, - setIsLoading, - setIsError - ); + if (catalogVisualization) + await renderCatalogVisualization( + http, + pplService, + savedVisualizationId, + fromTime, + toTime, + pplFilterValue, + spanParam, + setVisualizationTitle, + setVisualizationType, + setVisualizationData, + setVisualizationMetaData, + setIsLoading, + setIsError + ); + else + await renderSavedVisualization( + http, + pplService, + savedVisualizationId, + fromTime, + toTime, + pplFilterValue, + spanParam, + setVisualizationTitle, + setVisualizationType, + setVisualizationData, + setVisualizationMetaData, + setIsLoading, + setIsError + ); }; const memoisedVisualizationBox = useMemo( diff --git a/dashboards-observability/public/components/metrics/helpers/utils.tsx b/dashboards-observability/public/components/metrics/helpers/utils.tsx index 11804262d..a992c41c6 100644 --- a/dashboards-observability/public/components/metrics/helpers/utils.tsx +++ b/dashboards-observability/public/components/metrics/helpers/utils.tsx @@ -14,6 +14,7 @@ import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_pa import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/shared'; import PPLService from '../../../services/requests/ppl'; import { CoreStart } from '../../../../../../src/core/public'; +import { MetricType } from '../../../../common/types/metrics'; export const convertDateTime = (datetime: string, isStart = true, formatted = true) => { let returnTime: undefined | Moment; @@ -58,3 +59,89 @@ export const getVisualizations = (http: CoreStart['http']) => { console.error('Issue in fetching all saved visualizations', err); }); }; + +interface boxType { + x1: number; + y1: number; + x2: number; + y2: number; +} + +const defaultHeight = 3; +const defaultWidth = 12; + +const calculatOverlapArea = (bb1: boxType, bb2: boxType) => { + const x_left = Math.max(bb1.x1, bb2.x1); + const y_top = Math.max(bb1.y1, bb2.y1); + const x_right = Math.min(bb1.x2, bb2.x2); + const y_bottom = Math.min(bb1.y2, bb2.y2); + + if (x_right < x_left || y_bottom < y_top) return 0; + return (x_right - x_left) * (y_bottom - y_top); +}; + +const getTotalOverlapArea = (panelVisualizations: MetricType[]) => { + const newVizBox = { x1: 0, y1: 0, x2: defaultWidth, y2: defaultHeight }; + const currentVizBoxes = panelVisualizations.map((visualization) => { + return { + x1: visualization.x, + y1: visualization.y, + x2: visualization.x + visualization.w, + y2: visualization.y + visualization.h, + }; + }); + + let isOverlapping = 0; + currentVizBoxes.map((viz) => { + isOverlapping += calculatOverlapArea(viz, newVizBox); + }); + return isOverlapping; +}; + +// We want to check if the new visualization being added, can be placed at { x: 0, y: 0, w: 6, h: 4 }; +// To check this we try to calculate overlap between all the current visualizations and new visualization +// if there is no overalap (i.e Total Overlap Area is 0), we place the new viz. in default position +// else, we add it to the bottom of the panel +export const getNewVizDimensions = (panelVisualizations: MetricType[]) => { + let maxY: number = 0; + let maxYH: number = 0; + + // check if we can place the new visualization at default location + if (getTotalOverlapArea(panelVisualizations) === 0) { + return { x: 0, y: 0, w: defaultWidth, h: defaultHeight }; + } + + // else place the new visualization at the bottom of the panel + panelVisualizations.map((panelVisualization: MetricType) => { + if (panelVisualization.y >= maxY) { + maxY = panelVisualization.y; + maxYH = panelVisualization.h; + } + }); + + return { x: 0, y: maxY + maxYH, w: defaultWidth, h: defaultHeight }; +}; + +export const getMinSpanInterval = (start: any, end: any) => { + const momentStart = dateMath.parse(start)!; + const momentEnd = dateMath.parse(end, { roundUp: true })!; + const diffSeconds = momentEnd.unix() - momentStart.unix(); + console.log('diffSeconds', diffSeconds); + let minInterval; + // // less than 1 second + // if (diffSeconds <= 1) minInterval = 'ms'; + // less than 2 minutes + if (diffSeconds <= 60 * 2) minInterval = 's'; + // less than 2 hours + else if (diffSeconds <= 3600 * 2) minInterval = 'm'; + // less than 2 days + else if (diffSeconds <= 86400 * 2) minInterval = 'h'; + // less than 1 month + else if (diffSeconds <= 86400 * 31) minInterval = 'd'; + // less than 3 months + else if (diffSeconds <= 86400 * 93) minInterval = 'w'; + // less than 1 year + else if (diffSeconds <= 86400 * 366) minInterval = 'M'; + + return minInterval; +}; diff --git a/dashboards-observability/public/components/metrics/index.tsx b/dashboards-observability/public/components/metrics/index.tsx index 9287001fe..7d63f232b 100644 --- a/dashboards-observability/public/components/metrics/index.tsx +++ b/dashboards-observability/public/components/metrics/index.tsx @@ -8,33 +8,30 @@ import { EuiButtonIcon, EuiPage, EuiPageBody, - EuiSpacer, - EuiSuperDatePicker, - EuiSuperDatePickerProps, - EuiText, - EuiTitle, - EuiTabbedContent, - OnRefreshChangeProps, - OnRefreshProps, + htmlIdGenerator, OnTimeChangeProps, ShortDate, - EuiPageContentBody, - EuiFlexGroup, - EuiFlexItem, } from '@elastic/eui'; import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; -import React, { Fragment, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Route, RouteComponentProps } from 'react-router-dom'; import classNames from 'classnames'; -import { useSelector } from 'react-redux'; import { StaticContext } from 'react-router-dom'; -import { CUSTOM_PANELS_API_PREFIX } from ' ../../../common/constants/custom_panels'; -import { uiSettingsService } from '../../../common/utils'; import { ChromeBreadcrumb, CoreStart } from '../../../../../src/core/public'; -import { onTimeChange } from './helpers/utils'; +import { getMinSpanInterval, getNewVizDimensions, onTimeChange } from './helpers/utils'; import { Sidebar } from './sidebar/sidebar'; import { EmptyMetricsView } from './view/empty_view'; import PPLService from '../../services/requests/ppl'; +import { TopMenu } from './top_menu/top_menu'; +import { MetricData, MetricType } from '../../../common/types/metrics'; +import { MetricsGrid } from './view/metrics_grid'; +import { useDispatch, useSelector } from 'react-redux'; +import { + deSelectMetric, + selectedMetricsSelector, + selectMetric, +} from './redux/slices/metrics_slice'; +import { resolutionOptions } from '../../../common/constants/metrics'; interface MetricsProps { http: CoreStart['http']; @@ -47,28 +44,101 @@ interface MetricsProps { export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService }: MetricsProps) => { // Date picker constants const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); - const [start, setStart] = useState('now-30m'); - const [end, setEnd] = useState('now'); + const [startTime, setStartTime] = useState('now-1d'); + const [endTime, setEndTime] = useState('now'); const [dateDisabled, setDateDisabled] = useState(false); + // Top panel + const [IsTopPanelDisabled, setIsTopPanelDisabled] = useState(false); + const [editMode, setEditMode] = useState(false); + const [onRefresh, setOnRefresh] = useState(false); + const [editActionType, setEditActionType] = useState(''); + const [resolutionValue, setResolutionValue] = useState(resolutionOptions[2].value); + const [spanValue, setSpanValue] = useState(1); + const resolutionSelectId = htmlIdGenerator('resolutionSelect')(); + // Side bar constants const [isSidebarClosed, setIsSidebarClosed] = useState(false); - // Date Picker functions - // Empty functions for now - const onRefreshFilters = (startTime: ShortDate, endTime: ShortDate) => {}; + // Metrics constants + const [panelVisualizations, setPanelVisualizations] = useState([]); + const dispatch = useDispatch(); + const selectedMetrics = useSelector(selectedMetricsSelector); + + const handleAddMetric = (metric: any) => dispatch(selectMetric(metric)); + + const handleRemoveMetric = (metric: any) => { + dispatch(deSelectMetric(metric)); + }; + + const onRefreshFilters = (startTime: ShortDate, endTime: ShortDate) => { + console.log('spanParam', spanValue + resolutionValue); + // const autoResolutionValue = getMinSpanInterval(startTime, endTime); + // setResolutionValue( + // resolutionOptions.filter((option) => option.value === autoResolutionValue)[0].value + // ); + setOnRefresh(!onRefresh); + }; + const onDatePickerChange = (props: OnTimeChangeProps) => { onTimeChange( props.start, props.end, recentlyUsedRanges, setRecentlyUsedRanges, - setStart, - setEnd + setStartTime, + setEndTime ); onRefreshFilters(props.start, props.end); }; + const onEditClick = (savedVisualizationId: string) => { + window.location.assign(`#/event_analytics/explorer/${savedVisualizationId}`); + }; + + const updateVisualizations = (selectedMetrics: MetricData[]) => { + let metricVisualizations: MetricType[] = []; + + selectedMetrics.map((selectedMetric: any, index: number) => { + const newDimensions = getNewVizDimensions(metricVisualizations); + + const metricVisualization: MetricType = { + id: index + '', + savedVisualizationId: selectedMetric.id, + x: newDimensions.x, + y: newDimensions.y, + h: newDimensions.h, + w: newDimensions.w, + metricType: + selectedMetric.catalog === 'CUSTOM_METRICS' ? 'savedCustomMetric' : 'prometheusMetric', + }; + metricVisualizations.push(metricVisualization); + }); + + setPanelVisualizations(metricVisualizations); + }; + + const reloadVisualizations = () => { + updateVisualizations(selectedMetrics); + }; + + const onSideBarClick = () => { + console.log('side bar clicked'); + setIsSidebarClosed((staleState) => { + return !staleState; + }); + setTimeout(function () { + window.dispatchEvent(new Event('resize')); + }, 300); + }; + + useEffect(() => { + selectedMetrics.length > 0 ? setIsTopPanelDisabled(false) : setIsTopPanelDisabled(true); + updateVisualizations(selectedMetrics); + console.log('selectedMetrics', selectedMetrics); + setOnRefresh(!onRefresh); + }, [selectedMetrics]); + const mainSectionClassName = classNames({ 'col-md-9': !isSidebarClosed, 'col-md-12': isSidebarClosed, @@ -83,18 +153,25 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService }
- - - - - +
@@ -103,11 +180,7 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } iconType={isSidebarClosed ? 'menuRight' : 'menuLeft'} iconSize="m" size="s" - onClick={() => { - setIsSidebarClosed((staleState) => { - return !staleState; - }); - }} + onClick={() => onSideBarClick()} data-test-subj="collapseSideBarButton" aria-controls="discover-sidebar" aria-expanded={isSidebarClosed ? 'false' : 'true'} @@ -117,11 +190,26 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService }
-
-
- -
-
+ {selectedMetrics.length > 0 ? ( + + ) : ( + + )}
diff --git a/dashboards-observability/public/components/metrics/top_menu/top_menu.scss b/dashboards-observability/public/components/metrics/top_menu/top_menu.scss new file mode 100644 index 000000000..8c3be93ff --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/top_menu.scss @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +.resolutionSelect { + .euiFormControlLayout { + .euiFormControlLayout { + width: 116px; + } + } +} + +.resolutionSelectText { + width: 100px; +} \ No newline at end of file diff --git a/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx new file mode 100644 index 000000000..144680da6 --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx @@ -0,0 +1,204 @@ +import { + EuiPageHeader, + EuiPageHeaderSection, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiFieldText, + EuiSelect, + EuiSuperDatePicker, + htmlIdGenerator, + ShortDate, + OnTimeChangeProps, + EuiButton, +} from '@elastic/eui'; +import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import { uiSettingsService } from '../../../../common/utils'; +import React, { useState } from 'react'; + +import './top_menu.scss'; +import { MetricType } from '../../../../common/types/metrics'; +import { resolutionOptions } from '../../../../common/constants/metrics'; + +interface TopMenuProps { + IsTopPanelDisabled: boolean; + setIsTopPanelDisabled: React.Dispatch>; + startTime: ShortDate; + endTime: ShortDate; + onDatePickerChange: (props: OnTimeChangeProps) => void; + recentlyUsedRanges: DurationRange[]; + editMode: boolean; + setEditMode: React.Dispatch>; + setEditActionType: React.Dispatch>; + reloadVisualizations: () => void; + panelVisualizations: MetricType[]; + setPanelVisualizations: React.Dispatch>; + resolutionValue: string; + setResolutionValue: React.Dispatch>; + spanValue: number; + setSpanValue: React.Dispatch>; + resolutionSelectId: string; +} + +export const TopMenu = ({ + IsTopPanelDisabled, + setIsTopPanelDisabled, + startTime, + endTime, + onDatePickerChange, + recentlyUsedRanges, + editMode, + setEditMode, + setEditActionType, + reloadVisualizations, + panelVisualizations, + setPanelVisualizations, + resolutionValue, + setResolutionValue, + spanValue, + setSpanValue, + resolutionSelectId, +}: TopMenuProps) => { + const [originalPanelVisualizations, setOriginalPanelVisualizations] = useState([]); + + // toggle between panel edit mode + const editPanel = (editType: string) => { + // switch(editType){ + // case 'edit': { + // setOriginalPanelVisualizations([...panelVisualizations]); + // break; + // } + // // case 'save': { + // // setPanelVisualizations(tempPanelVisualizations) + // // break; + // // } + // case 'cancel': { + // setPanelVisualizations(originalPanelVisualizations); + // setOriginalPanelVisualizations([]); + // break; + // } + // default: { + // break; + // } + // } + + setEditMode(!editMode); + switch (editType) { + case 'edit': { + setOriginalPanelVisualizations([...panelVisualizations]); + break; + } + // case 'save': { + // setPanelVisualizations(tempPanelVisualizations) + // break; + // } + case 'cancel': { + setPanelVisualizations(originalPanelVisualizations); + setOriginalPanelVisualizations([]); + break; + } + default: { + break; + } + } + // if (editType === 'cancel') { + // // setEditMode(false); + // reloadVisualizations(); + // } + setEditActionType(editType); + }; + + const onResolutionChange = (e) => { + setResolutionValue(e.target.value); + }; + + const cancelButton = ( + editPanel('cancel')}> + Cancel + + ); + + const saveButton = ( + editPanel('save')}> + Save view + + ); + + const editButton = ( + editPanel('edit')} + isDisabled={IsTopPanelDisabled} + > + Edit view + + ); + return ( + <> + + + +

Metrics

+
+
+ + + +
+ setSpanValue(e.target.value)} + append={ + onResolutionChange(e)} + aria-label="resolutionSelect" + /> + } + disabled={IsTopPanelDisabled} + aria-label="resolutionField" + /> +
+
+ + + + + {}} + data-test-subj="metrics__savePopover" + iconType="arrowDown" + isDisabled={IsTopPanelDisabled} + > + Save + + +
+
+
+ + {editMode ? ( + <> + {cancelButton} + {saveButton} + + ) : ( + {editButton} + )} + + + ); +}; diff --git a/dashboards-observability/public/components/metrics/view/empty_view.tsx b/dashboards-observability/public/components/metrics/view/empty_view.tsx index a495e2d8c..f67b6e1a4 100644 --- a/dashboards-observability/public/components/metrics/view/empty_view.tsx +++ b/dashboards-observability/public/components/metrics/view/empty_view.tsx @@ -9,17 +9,20 @@ import './empty_view.scss'; export const EmptyMetricsView = () => { return ( -
- - - - -

No Metrics Selected

- - - Select a metric from the left sidepanel to view results. - -
+
+ +
+ + + +

No Metrics Selected

+ + + Select a metric from the left sidepanel to view results. + +
+
+
); }; diff --git a/dashboards-observability/public/components/metrics/view/metrics_grid.scss b/dashboards-observability/public/components/metrics/view/metrics_grid.scss new file mode 100644 index 000000000..ff866b444 --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/metrics_grid.scss @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + .metrics-grid-div { + min-height: 70vh; + } \ No newline at end of file diff --git a/dashboards-observability/public/components/metrics/view/metrics_grid.tsx b/dashboards-observability/public/components/metrics/view/metrics_grid.tsx new file mode 100644 index 000000000..354801b0d --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/metrics_grid.tsx @@ -0,0 +1,193 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import PPLService from 'public/services/requests/ppl'; +import React, { useEffect, useState } from 'react'; +import { CoreStart } from '../../../../../../src/core/public'; +import { Layout, Layouts, Responsive, WidthProvider } from 'react-grid-layout'; +import { useObservable } from 'react-use'; +import { VisualizationContainer } from '../../custom_panels/panel_modules/visualization_container'; +import { MetricType } from '../../../../common/types/metrics'; +import _ from 'lodash'; +import { mergeLayoutAndVisualizations } from '../../custom_panels/helpers/utils'; +import { getVizContainerProps } from '../../visualizations/charts/helpers'; + +import './metrics_grid.scss'; + +// HOC container to provide dynamic width for Grid layout +const ResponsiveGridLayout = WidthProvider(Responsive); + +interface MetricsGridProps { + http: CoreStart['http']; + chrome: CoreStart['chrome']; + panelVisualizations: MetricType[]; + setPanelVisualizations: React.Dispatch>; + editMode: boolean; + pplService: PPLService; + startTime: string; + endTime: string; + setStartTime: any; + setEndTime: any; + moveToEvents: (savedVisualizationId: string) => any; + onRefresh: boolean; + editActionType: string; + spanParam: string; +} + +export const MetricsGrid = ({ + http, + chrome, + panelVisualizations, + setPanelVisualizations, + editMode, + pplService, + startTime, + endTime, + setStartTime, + setEndTime, + moveToEvents, + onRefresh, + editActionType, + spanParam, +}: MetricsGridProps) => { + const [currentLayout, setCurrentLayout] = useState([]); + const [postEditLayout, setPostEditLayout] = useState([]); + const [gridData, setGridData] = useState(panelVisualizations.map(() => <>)); + const isLocked = useObservable(chrome.getIsNavDrawerLocked$()); + + // Reset Size of Visualizations when layout is changed + const layoutChanged = (currLayouts: Layout[], allLayouts: Layouts) => { + window.dispatchEvent(new Event('resize')); + setPostEditLayout(currLayouts); + }; + + const loadVizComponents = () => { + const gridDataComps = panelVisualizations.map((panelVisualization: MetricType, index) => + panelVisualization.metricType === 'savedCustomMetric' ? ( + + ) : ( + <> + + + ) + ); + setGridData(gridDataComps); + }; + + // Reload the Layout + const reloadLayout = () => { + const tempLayout: Layout[] = panelVisualizations.map((panelVisualization) => { + return { + i: panelVisualization.id, + x: panelVisualization.x, + y: panelVisualization.y, + w: panelVisualization.w, + h: panelVisualization.h, + static: !editMode, + } as Layout; + }); + setCurrentLayout(tempLayout); + }; + + // remove visualization from panel in edit mode + const removeVisualization = (visualizationId: string) => { + const newVisualizationList = _.reject(panelVisualizations, { + id: visualizationId, + }); + mergeLayoutAndVisualizations(postEditLayout, newVisualizationList, setPanelVisualizations); + }; + + // Save Visualization Layouts when not in edit mode anymore (after users saves the panel) + const saveVisualizationLayouts = async (panelID: string, visualizationParams: any) => {}; + + // Update layout whenever user edit gets completed + useEffect(() => { + if (editMode) { + reloadLayout(); + loadVizComponents(); + } + }, [editMode]); + + useEffect(() => { + if (editActionType === 'save') { + // const visualizationParams = postEditLayout.map((layout) => + // _.omit(layout, ['static', 'moved', 'isDraggable','isResizable', 'minW','minH','maxW','maxH']) + // ); + // saveVisualizationLayouts(panelId, visualizationParams); + console.log('click saved', postEditLayout); + mergeLayoutAndVisualizations(postEditLayout, panelVisualizations, setPanelVisualizations); + // setPanelVisualizations(visualizationParams); + // if (updateAvailabilityVizId) { + // updateAvailabilityVizId(panelVisualizations); + // } + } + }, [editActionType]); + + // Update layout whenever visualizations are updated + useEffect(() => { + reloadLayout(); + loadVizComponents(); + }, [panelVisualizations]); + + // Reset Size of Panel Grid when Nav Dock is Locked + useEffect(() => { + setTimeout(function () { + window.dispatchEvent(new Event('resize')); + }, 300); + }, [isLocked]); + + useEffect(() => { + loadVizComponents(); + }, [onRefresh]); + + useEffect(() => { + loadVizComponents(); + }, []); + + return ( + + {panelVisualizations.map((panelVisualization: MetricType, index) => ( +
{gridData[index]}
+ ))} +
+ ); +}; diff --git a/dashboards-observability/public/components/metrics/view/metrics_table_view.tsx b/dashboards-observability/public/components/metrics/view/metrics_table_view.tsx deleted file mode 100644 index 94c9bebd9..000000000 --- a/dashboards-observability/public/components/metrics/view/metrics_table_view.tsx +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; - -export const MetricsTableView = () => { - return
metrics_table_view
; -}; From c7d97d95e53ffbd34f4e3183b2272b09f6c489b6 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Wed, 2 Nov 2022 01:50:11 -0700 Subject: [PATCH 2/4] added metrics layout to redux Signed-off-by: Shenoy Pratik --- .../components/metrics/helpers/utils.tsx | 51 +++++++++- .../public/components/metrics/index.tsx | 71 +++----------- .../metrics/redux/slices/metrics_slice.ts | 49 +++++++++- .../components/metrics/top_menu/top_menu.tsx | 34 +------ .../components/metrics/view/metrics_grid.tsx | 98 ++++++++----------- 5 files changed, 154 insertions(+), 149 deletions(-) diff --git a/dashboards-observability/public/components/metrics/helpers/utils.tsx b/dashboards-observability/public/components/metrics/helpers/utils.tsx index a992c41c6..5a5aa8cc8 100644 --- a/dashboards-observability/public/components/metrics/helpers/utils.tsx +++ b/dashboards-observability/public/components/metrics/helpers/utils.tsx @@ -14,7 +14,9 @@ import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_pa import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/shared'; import PPLService from '../../../services/requests/ppl'; import { CoreStart } from '../../../../../../src/core/public'; -import { MetricType } from '../../../../common/types/metrics'; +import { MetricData, MetricType } from '../../../../common/types/metrics'; +import { Layout } from 'react-grid-layout'; +import { VisualizationType } from '../../../../common/types/custom_panels'; export const convertDateTime = (datetime: string, isStart = true, formatted = true) => { let returnTime: undefined | Moment; @@ -145,3 +147,50 @@ export const getMinSpanInterval = (start: any, end: any) => { return minInterval; }; + +// Merges new layout into visualizations +export const mergeLayoutAndMetrics = ( + layout: Layout[], + newVisualizationList: VisualizationType[] +) => { + const newPanelVisualizations: VisualizationType[] = []; + + for (let i = 0; i < newVisualizationList.length; i++) { + for (let j = 0; j < layout.length; j++) { + if (newVisualizationList[i].id == layout[j].i) { + newPanelVisualizations.push({ + ...newVisualizationList[i], + x: layout[j].x, + y: layout[j].y, + w: layout[j].w, + h: layout[j].h, + }); + } + } + } + return newPanelVisualizations; +}; + +export const updateMetricsVisualizations = (selectedMetrics: MetricData[]) => { + let metricVisualizations: MetricType[] = []; + + selectedMetrics.map((selectedMetric: any, index: number) => { + const newDimensions = getNewVizDimensions(metricVisualizations); + + const metricVisualization: MetricType = { + id: index + '', + savedVisualizationId: selectedMetric.id, + x: newDimensions.x, + y: newDimensions.y, + h: newDimensions.h, + w: newDimensions.w, + metricType: + selectedMetric.catalog === 'CUSTOM_METRICS' ? 'savedCustomMetric' : 'prometheusMetric', + }; + metricVisualizations.push(metricVisualization); + }); + + return metricVisualizations; + + // updateMetricsLayout(metricVisualizations); +}; diff --git a/dashboards-observability/public/components/metrics/index.tsx b/dashboards-observability/public/components/metrics/index.tsx index 7d63f232b..3ff6e36d4 100644 --- a/dashboards-observability/public/components/metrics/index.tsx +++ b/dashboards-observability/public/components/metrics/index.tsx @@ -18,19 +18,15 @@ import { Route, RouteComponentProps } from 'react-router-dom'; import classNames from 'classnames'; import { StaticContext } from 'react-router-dom'; import { ChromeBreadcrumb, CoreStart } from '../../../../../src/core/public'; -import { getMinSpanInterval, getNewVizDimensions, onTimeChange } from './helpers/utils'; +import { onTimeChange } from './helpers/utils'; import { Sidebar } from './sidebar/sidebar'; import { EmptyMetricsView } from './view/empty_view'; import PPLService from '../../services/requests/ppl'; import { TopMenu } from './top_menu/top_menu'; -import { MetricData, MetricType } from '../../../common/types/metrics'; +import { MetricType } from '../../../common/types/metrics'; import { MetricsGrid } from './view/metrics_grid'; -import { useDispatch, useSelector } from 'react-redux'; -import { - deSelectMetric, - selectedMetricsSelector, - selectMetric, -} from './redux/slices/metrics_slice'; +import { useSelector } from 'react-redux'; +import { metricsLayoutSelector, selectedMetricsSelector } from './redux/slices/metrics_slice'; import { resolutionOptions } from '../../../common/constants/metrics'; interface MetricsProps { @@ -42,11 +38,14 @@ interface MetricsProps { } export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService }: MetricsProps) => { + // Redux tools + const selectedMetrics = useSelector(selectedMetricsSelector); + const metricsLayout = useSelector(metricsLayoutSelector); + // Date picker constants const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); const [startTime, setStartTime] = useState('now-1d'); const [endTime, setEndTime] = useState('now'); - const [dateDisabled, setDateDisabled] = useState(false); // Top panel const [IsTopPanelDisabled, setIsTopPanelDisabled] = useState(false); @@ -62,21 +61,9 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } // Metrics constants const [panelVisualizations, setPanelVisualizations] = useState([]); - const dispatch = useDispatch(); - const selectedMetrics = useSelector(selectedMetricsSelector); - - const handleAddMetric = (metric: any) => dispatch(selectMetric(metric)); - - const handleRemoveMetric = (metric: any) => { - dispatch(deSelectMetric(metric)); - }; const onRefreshFilters = (startTime: ShortDate, endTime: ShortDate) => { console.log('spanParam', spanValue + resolutionValue); - // const autoResolutionValue = getMinSpanInterval(startTime, endTime); - // setResolutionValue( - // resolutionOptions.filter((option) => option.value === autoResolutionValue)[0].value - // ); setOnRefresh(!onRefresh); }; @@ -96,34 +83,7 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } window.location.assign(`#/event_analytics/explorer/${savedVisualizationId}`); }; - const updateVisualizations = (selectedMetrics: MetricData[]) => { - let metricVisualizations: MetricType[] = []; - - selectedMetrics.map((selectedMetric: any, index: number) => { - const newDimensions = getNewVizDimensions(metricVisualizations); - - const metricVisualization: MetricType = { - id: index + '', - savedVisualizationId: selectedMetric.id, - x: newDimensions.x, - y: newDimensions.y, - h: newDimensions.h, - w: newDimensions.w, - metricType: - selectedMetric.catalog === 'CUSTOM_METRICS' ? 'savedCustomMetric' : 'prometheusMetric', - }; - metricVisualizations.push(metricVisualization); - }); - - setPanelVisualizations(metricVisualizations); - }; - - const reloadVisualizations = () => { - updateVisualizations(selectedMetrics); - }; - const onSideBarClick = () => { - console.log('side bar clicked'); setIsSidebarClosed((staleState) => { return !staleState; }); @@ -134,11 +94,16 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } useEffect(() => { selectedMetrics.length > 0 ? setIsTopPanelDisabled(false) : setIsTopPanelDisabled(true); - updateVisualizations(selectedMetrics); - console.log('selectedMetrics', selectedMetrics); - setOnRefresh(!onRefresh); }, [selectedMetrics]); + useEffect(() => { + setPanelVisualizations(metricsLayout); + }, [metricsLayout]); + + useEffect(() => { + if (editMode) setIsTopPanelDisabled(true); + }, [editMode]); + const mainSectionClassName = classNames({ 'col-md-9': !isSidebarClosed, 'col-md-12': isSidebarClosed, @@ -155,7 +120,6 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } { @@ -56,16 +58,57 @@ const fetchRemoteMetrics = async (pplService: any) => { return dataSet; }; +const updateLayoutBySelection = (state: any, newMetric: any) => { + const newDimensions = getNewVizDimensions(state.metricsLayout); + + const metricVisualization: MetricType = { + id: newMetric.id, + savedVisualizationId: newMetric.id, + x: newDimensions.x, + y: newDimensions.y, + h: newDimensions.h, + w: newDimensions.w, + metricType: newMetric.catalog === 'CUSTOM_METRICS' ? 'savedCustomMetric' : 'prometheusMetric', + }; + state.metricsLayout = [...state.metricsLayout, metricVisualization]; +}; + +const updateLayoutByDeSelection = (state: any, newMetric: any) => { + const sortedMetricsLayout = state.metricsLayout.sort((a: MetricType, b: MetricType) => { + if (a.y > b.y) return 1; + if (a.y < b.y) return -1; + else return 0; + }); + + let newMetricsLayout = [] as MetricType[]; + let heightSubtract = 0; + + sortedMetricsLayout.map((metricLayout: MetricType) => { + if (metricLayout.id !== newMetric.id) { + metricLayout.y = metricLayout.y - heightSubtract; + newMetricsLayout.push(metricLayout); + } else { + heightSubtract = metricLayout.h; + } + }); + state.metricsLayout = newMetricsLayout; +}; + export const metricSlice = createSlice({ name: REDUX_SLICE_METRICS, initialState, reducers: { selectMetric: (state, { payload }) => { state.selected.push(payload.id); + updateLayoutBySelection(state, payload); }, deSelectMetric: (state, { payload }) => { + updateLayoutByDeSelection(state, payload); state.selected = state.selected.filter((id) => id !== payload.id); }, + updateMetricsLayout: (state, { payload }) => { + state.metricsLayout = payload; + }, }, extraReducers: (builder) => { builder.addCase(loadMetrics.fulfilled, (state, { payload }) => { @@ -74,7 +117,7 @@ export const metricSlice = createSlice({ }, }); -export const { deSelectMetric, selectMetric } = metricSlice.actions; +export const { deSelectMetric, selectMetric, updateMetricsLayout } = metricSlice.actions; export const metricsStateSelector = (state) => state.metrics; @@ -84,4 +127,6 @@ export const availableMetricsSelector = (state) => export const selectedMetricsSelector = (state) => state.metrics.metrics.filter((metric) => state.metrics.selected.includes(metric.id)); +export const metricsLayoutSelector = (state) => state.metrics.metricsLayout; + export default metricSlice.reducer; diff --git a/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx index 144680da6..d2aaf5492 100644 --- a/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx +++ b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx @@ -7,7 +7,6 @@ import { EuiFieldText, EuiSelect, EuiSuperDatePicker, - htmlIdGenerator, ShortDate, OnTimeChangeProps, EuiButton, @@ -22,7 +21,6 @@ import { resolutionOptions } from '../../../../common/constants/metrics'; interface TopMenuProps { IsTopPanelDisabled: boolean; - setIsTopPanelDisabled: React.Dispatch>; startTime: ShortDate; endTime: ShortDate; onDatePickerChange: (props: OnTimeChangeProps) => void; @@ -30,7 +28,6 @@ interface TopMenuProps { editMode: boolean; setEditMode: React.Dispatch>; setEditActionType: React.Dispatch>; - reloadVisualizations: () => void; panelVisualizations: MetricType[]; setPanelVisualizations: React.Dispatch>; resolutionValue: string; @@ -42,15 +39,13 @@ interface TopMenuProps { export const TopMenu = ({ IsTopPanelDisabled, - setIsTopPanelDisabled, startTime, endTime, onDatePickerChange, recentlyUsedRanges, editMode, - setEditMode, setEditActionType, - reloadVisualizations, + setEditMode, panelVisualizations, setPanelVisualizations, resolutionValue, @@ -63,35 +58,12 @@ export const TopMenu = ({ // toggle between panel edit mode const editPanel = (editType: string) => { - // switch(editType){ - // case 'edit': { - // setOriginalPanelVisualizations([...panelVisualizations]); - // break; - // } - // // case 'save': { - // // setPanelVisualizations(tempPanelVisualizations) - // // break; - // // } - // case 'cancel': { - // setPanelVisualizations(originalPanelVisualizations); - // setOriginalPanelVisualizations([]); - // break; - // } - // default: { - // break; - // } - // } - setEditMode(!editMode); switch (editType) { case 'edit': { setOriginalPanelVisualizations([...panelVisualizations]); break; } - // case 'save': { - // setPanelVisualizations(tempPanelVisualizations) - // break; - // } case 'cancel': { setPanelVisualizations(originalPanelVisualizations); setOriginalPanelVisualizations([]); @@ -101,10 +73,6 @@ export const TopMenu = ({ break; } } - // if (editType === 'cancel') { - // // setEditMode(false); - // reloadVisualizations(); - // } setEditActionType(editType); }; diff --git a/dashboards-observability/public/components/metrics/view/metrics_grid.tsx b/dashboards-observability/public/components/metrics/view/metrics_grid.tsx index 354801b0d..13f92d04f 100644 --- a/dashboards-observability/public/components/metrics/view/metrics_grid.tsx +++ b/dashboards-observability/public/components/metrics/view/metrics_grid.tsx @@ -12,7 +12,9 @@ import { VisualizationContainer } from '../../custom_panels/panel_modules/visual import { MetricType } from '../../../../common/types/metrics'; import _ from 'lodash'; import { mergeLayoutAndVisualizations } from '../../custom_panels/helpers/utils'; -import { getVizContainerProps } from '../../visualizations/charts/helpers'; +import { useDispatch } from 'react-redux'; +import { updateMetricsLayout, deSelectMetric } from '../redux/slices/metrics_slice'; +import { mergeLayoutAndMetrics } from '../helpers/utils'; import './metrics_grid.scss'; @@ -28,8 +30,6 @@ interface MetricsGridProps { pplService: PPLService; startTime: string; endTime: string; - setStartTime: any; - setEndTime: any; moveToEvents: (savedVisualizationId: string) => any; onRefresh: boolean; editActionType: string; @@ -45,16 +45,22 @@ export const MetricsGrid = ({ pplService, startTime, endTime, - setStartTime, - setEndTime, moveToEvents, onRefresh, editActionType, spanParam, }: MetricsGridProps) => { + // Redux tools + const dispatch = useDispatch(); + const updateLayout = (metric: any) => dispatch(updateMetricsLayout(metric)); + const handleRemoveMetric = (metric: any) => { + dispatch(deSelectMetric(metric)); + }; + const [currentLayout, setCurrentLayout] = useState([]); const [postEditLayout, setPostEditLayout] = useState([]); const [gridData, setGridData] = useState(panelVisualizations.map(() => <>)); + const [removeMetricsList, setRemoveMetricsList] = useState<{ id: string }[]>([]); const isLocked = useObservable(chrome.getIsNavDrawerLocked$()); // Reset Size of Visualizations when layout is changed @@ -64,46 +70,27 @@ export const MetricsGrid = ({ }; const loadVizComponents = () => { - const gridDataComps = panelVisualizations.map((panelVisualization: MetricType, index) => - panelVisualization.metricType === 'savedCustomMetric' ? ( - - ) : ( - <> - - - ) - ); + const gridDataComps = panelVisualizations.map((panelVisualization: MetricType, index) => ( + + )); setGridData(gridDataComps); }; @@ -116,6 +103,8 @@ export const MetricsGrid = ({ y: panelVisualization.y, w: panelVisualization.w, h: panelVisualization.h, + minW: 12, // restricting width of the metric visualization + maxW: 12, static: !editMode, } as Layout; }); @@ -127,12 +116,10 @@ export const MetricsGrid = ({ const newVisualizationList = _.reject(panelVisualizations, { id: visualizationId, }); + setRemoveMetricsList([...removeMetricsList, { id: visualizationId }]); mergeLayoutAndVisualizations(postEditLayout, newVisualizationList, setPanelVisualizations); }; - // Save Visualization Layouts when not in edit mode anymore (after users saves the panel) - const saveVisualizationLayouts = async (panelID: string, visualizationParams: any) => {}; - // Update layout whenever user edit gets completed useEffect(() => { if (editMode) { @@ -142,17 +129,12 @@ export const MetricsGrid = ({ }, [editMode]); useEffect(() => { + if (editActionType === 'cancel') { + setRemoveMetricsList([]); + } if (editActionType === 'save') { - // const visualizationParams = postEditLayout.map((layout) => - // _.omit(layout, ['static', 'moved', 'isDraggable','isResizable', 'minW','minH','maxW','maxH']) - // ); - // saveVisualizationLayouts(panelId, visualizationParams); - console.log('click saved', postEditLayout); - mergeLayoutAndVisualizations(postEditLayout, panelVisualizations, setPanelVisualizations); - // setPanelVisualizations(visualizationParams); - // if (updateAvailabilityVizId) { - // updateAvailabilityVizId(panelVisualizations); - // } + removeMetricsList.map((value) => handleRemoveMetric(value)); + updateLayout(mergeLayoutAndMetrics(postEditLayout, panelVisualizations)); } }, [editActionType]); From 94be3cd4e58d25bc44ac48d7b74c4ecce6a6d51d Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Wed, 2 Nov 2022 09:21:24 -0700 Subject: [PATCH 3/4] removed comments and updated constants Signed-off-by: Shenoy Pratik --- .../common/constants/metrics.ts | 4 +- .../custom_panels/helpers/utils.tsx | 15 ------- .../components/metrics/helpers/utils.tsx | 40 +++---------------- .../public/components/metrics/index.tsx | 1 - 4 files changed, 9 insertions(+), 51 deletions(-) diff --git a/dashboards-observability/common/constants/metrics.ts b/dashboards-observability/common/constants/metrics.ts index 04b9034eb..49232392f 100644 --- a/dashboards-observability/common/constants/metrics.ts +++ b/dashboards-observability/common/constants/metrics.ts @@ -11,7 +11,6 @@ export const PPL_PROMETHEUS_CATALOG_REQUEST = export const REDUX_SLICE_METRICS = 'metrics'; export const resolutionOptions = [ - // { value: 'ms', text: 'milliseconds' }, { value: 's', text: 'seconds' }, { value: 'm', text: 'minutes' }, { value: 'h', text: 'hours' }, @@ -20,3 +19,6 @@ export const resolutionOptions = [ { value: 'q', text: 'quarters' }, { value: 'y', text: 'years' }, ]; + +export const DEFAULT_METRIC_HEIGHT = 2; +export const DEFAULT_METRIC_WIDTH = 12; diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index 70e16dd67..7945c3706 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -18,7 +18,6 @@ import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_pa import { VisualizationType, SavedVisualizationType } from '../../../../common/types/custom_panels'; import { Visualization } from '../../visualizations/visualization'; import { getVizContainerProps } from '../../../components/visualizations/charts/helpers'; -import { string } from 'joi'; /* * "Utils" This file contains different reused functions in operational panels @@ -405,17 +404,3 @@ export const displayVisualization = (metaData: any, data: any, type: string) => /> ); }; - -// ; diff --git a/dashboards-observability/public/components/metrics/helpers/utils.tsx b/dashboards-observability/public/components/metrics/helpers/utils.tsx index 5a5aa8cc8..1f8a4583c 100644 --- a/dashboards-observability/public/components/metrics/helpers/utils.tsx +++ b/dashboards-observability/public/components/metrics/helpers/utils.tsx @@ -2,7 +2,6 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -/* eslint-disable no-console */ import dateMath from '@elastic/datemath'; import { ShortDate } from '@elastic/eui'; @@ -11,12 +10,13 @@ import _ from 'lodash'; import { Moment } from 'moment-timezone'; import React from 'react'; import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_panels'; -import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/shared'; +import { PPL_DATE_FORMAT } from '../../../../common/constants/shared'; import PPLService from '../../../services/requests/ppl'; import { CoreStart } from '../../../../../../src/core/public'; -import { MetricData, MetricType } from '../../../../common/types/metrics'; +import { MetricType } from '../../../../common/types/metrics'; import { Layout } from 'react-grid-layout'; import { VisualizationType } from '../../../../common/types/custom_panels'; +import { DEFAULT_METRIC_HEIGHT, DEFAULT_METRIC_WIDTH } from '../../../../common/constants/metrics'; export const convertDateTime = (datetime: string, isStart = true, formatted = true) => { let returnTime: undefined | Moment; @@ -69,9 +69,6 @@ interface boxType { y2: number; } -const defaultHeight = 3; -const defaultWidth = 12; - const calculatOverlapArea = (bb1: boxType, bb2: boxType) => { const x_left = Math.max(bb1.x1, bb2.x1); const y_top = Math.max(bb1.y1, bb2.y1); @@ -83,7 +80,7 @@ const calculatOverlapArea = (bb1: boxType, bb2: boxType) => { }; const getTotalOverlapArea = (panelVisualizations: MetricType[]) => { - const newVizBox = { x1: 0, y1: 0, x2: defaultWidth, y2: defaultHeight }; + const newVizBox = { x1: 0, y1: 0, x2: DEFAULT_METRIC_WIDTH, y2: DEFAULT_METRIC_HEIGHT }; const currentVizBoxes = panelVisualizations.map((visualization) => { return { x1: visualization.x, @@ -110,7 +107,7 @@ export const getNewVizDimensions = (panelVisualizations: MetricType[]) => { // check if we can place the new visualization at default location if (getTotalOverlapArea(panelVisualizations) === 0) { - return { x: 0, y: 0, w: defaultWidth, h: defaultHeight }; + return { x: 0, y: 0, w: DEFAULT_METRIC_WIDTH, h: DEFAULT_METRIC_HEIGHT }; } // else place the new visualization at the bottom of the panel @@ -121,14 +118,13 @@ export const getNewVizDimensions = (panelVisualizations: MetricType[]) => { } }); - return { x: 0, y: maxY + maxYH, w: defaultWidth, h: defaultHeight }; + return { x: 0, y: maxY + maxYH, w: DEFAULT_METRIC_WIDTH, h: DEFAULT_METRIC_HEIGHT }; }; export const getMinSpanInterval = (start: any, end: any) => { const momentStart = dateMath.parse(start)!; const momentEnd = dateMath.parse(end, { roundUp: true })!; const diffSeconds = momentEnd.unix() - momentStart.unix(); - console.log('diffSeconds', diffSeconds); let minInterval; // // less than 1 second // if (diffSeconds <= 1) minInterval = 'ms'; @@ -170,27 +166,3 @@ export const mergeLayoutAndMetrics = ( } return newPanelVisualizations; }; - -export const updateMetricsVisualizations = (selectedMetrics: MetricData[]) => { - let metricVisualizations: MetricType[] = []; - - selectedMetrics.map((selectedMetric: any, index: number) => { - const newDimensions = getNewVizDimensions(metricVisualizations); - - const metricVisualization: MetricType = { - id: index + '', - savedVisualizationId: selectedMetric.id, - x: newDimensions.x, - y: newDimensions.y, - h: newDimensions.h, - w: newDimensions.w, - metricType: - selectedMetric.catalog === 'CUSTOM_METRICS' ? 'savedCustomMetric' : 'prometheusMetric', - }; - metricVisualizations.push(metricVisualization); - }); - - return metricVisualizations; - - // updateMetricsLayout(metricVisualizations); -}; diff --git a/dashboards-observability/public/components/metrics/index.tsx b/dashboards-observability/public/components/metrics/index.tsx index 3ff6e36d4..e44b96d92 100644 --- a/dashboards-observability/public/components/metrics/index.tsx +++ b/dashboards-observability/public/components/metrics/index.tsx @@ -63,7 +63,6 @@ export const Home = ({ http, chrome, parentBreadcrumb, renderProps, pplService } const [panelVisualizations, setPanelVisualizations] = useState([]); const onRefreshFilters = (startTime: ShortDate, endTime: ShortDate) => { - console.log('spanParam', spanValue + resolutionValue); setOnRefresh(!onRefresh); }; From e1abd459ed25de9e15f9d301df9e5b23a8bf9fd4 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Wed, 2 Nov 2022 09:27:58 -0700 Subject: [PATCH 4/4] remove excess console.log Signed-off-by: Shenoy Pratik --- .../public/components/custom_panels/helpers/utils.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index 7945c3706..483361dad 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -102,9 +102,7 @@ const queryAccumulator = ( )}' and ${timestampField} <= '${convertDateTime(endTime, false)}'`; const pplFilterQuery = panelFilterQuery === '' ? '' : ` | ${panelFilterQuery}`; const finalQuery = indexPartOfQuery + timeQueryFilter + pplFilterQuery + filterPartOfQuery; - console.log('spanParam in query accu', spanParam); if (spanParam === undefined) { - console.log('normal query'); return finalQuery; } else { return finalQuery.replace( @@ -192,7 +190,6 @@ export const getQueryResponse = ( setIsLoading(false); return; } - console.log('query here:', finalQuery); pplServiceRequestor(pplService, finalQuery, type, setVisualizationData, setIsLoading, setIsError); };