/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { find, isEmpty } from 'lodash';
import Plotly from 'plotly.js-dist';
import React, { useMemo } from 'react';
import {
AGGREGATIONS,
GROUPBY,
PLOTLY_GAUGE_COLUMN_NUMBER,
DEFAULT_GAUGE_CHART_PARAMETERS,
} from '../../../../../../common/constants/explorer';
import { IVisualizationContainerProps } from '../../../../../../common/types/explorer';
import { PLOT_MARGIN } from '../../../../../../common/constants/shared';
import { ThresholdUnitType } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds';
import { EmptyPlaceholder } from '../../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
import { getPropName } from '../../../../event_analytics/utils/utils';
import { Plt } from '../../../plotly/plot';
const {
GaugeTitleSize,
DisplayDefaultGauges,
OrientationDefault,
TickLength,
LegendPlacement,
} = DEFAULT_GAUGE_CHART_PARAMETERS;
export const Gauge = ({ visualizations, layout, config }: any) => {
const {
data: {
rawVizData: {
data: queriedVizData,
metadata: { fields },
},
userConfigs: {
dataConfig: {
span = {},
chartStyles = {},
panelOptions = {},
thresholds = [],
[GROUPBY]: dimensions = [],
[AGGREGATIONS]: series = [],
},
layoutConfig = {},
},
},
vis: { icontype },
}: IVisualizationContainerProps = visualizations;
const seriesLength = series.length;
const numberOfGauges = panelOptions.numberOfGauges || DisplayDefaultGauges;
// style parameters
const titleSize = chartStyles.titleSize || GaugeTitleSize;
const valueSize = chartStyles.valueSize;
const showThresholdMarkers = chartStyles.showThresholdMarkers || false;
const showThresholdLabels = chartStyles.showThresholdLabels || false;
const orientation = chartStyles.orientation || OrientationDefault;
const legendPlacement = chartStyles.legendPlacement || LegendPlacement;
let xaxes = dimensions;
const isEmptyPlot = !seriesLength || isEmpty(queriedVizData);
const timestampField = find(fields, (field) => field.type === 'timestamp');
if (span && span.time_field && timestampField) {
xaxes = [timestampField, ...xaxes];
}
if (isEmptyPlot) return ;
const gaugeData: Plotly.Data[] = useMemo(() => {
let calculatedGaugeData: Plotly.Data[] = [];
// case 1,2: no dimension, single/multiple metrics
if (!xaxes.length && seriesLength >= 1) {
calculatedGaugeData = series.map((seriesItem: any) => {
return {
field_name: getPropName(seriesItem),
value: queriedVizData[getPropName(seriesItem)][0],
};
});
}
// case 3: multiple dimensions and multiple metrics
if (xaxes.length && seriesLength) {
const selectedDimensionsData = xaxes
.map((dimension: any) => {
return queriedVizData[dimension.name]
? queriedVizData[dimension.name].slice(0, numberOfGauges)
: [];
})
.reduce((prev, cur) => {
return prev.map((i, j) => `${i}, ${cur[j]}`);
});
const selectedSeriesData = series.map((seriesItem: any) => {
const propValue = getPropName(seriesItem);
return queriedVizData[`${propValue}`]
? queriedVizData[`${propValue}`].slice(0, numberOfGauges)
: [];
});
selectedSeriesData.map((seriesSlice: any, seriesSliceIndex: number) => {
calculatedGaugeData = [
...calculatedGaugeData,
...seriesSlice.map((seriesSliceData: any, seriesSliceDataIndex: number) => {
return {
field_name: `${selectedDimensionsData[seriesSliceDataIndex]}, ${getPropName(
series[seriesSliceIndex]
)}`,
value: seriesSliceData,
};
}),
];
});
}
return calculatedGaugeData.map((gauge, index) => {
return {
type: 'indicator',
mode: 'gauge+number+delta',
value: gauge.value || 0,
title: {
text: gauge.field_name,
font: { size: titleSize },
align: legendPlacement,
},
...(valueSize && {
number: {
font: {
size: valueSize,
},
},
}),
domain: {
...(orientation === 'auto' || orientation === 'h'
? {
row: Math.floor(index / PLOTLY_GAUGE_COLUMN_NUMBER),
column: index % PLOTLY_GAUGE_COLUMN_NUMBER,
}
: {
column: Math.floor(index / PLOTLY_GAUGE_COLUMN_NUMBER),
row: index % PLOTLY_GAUGE_COLUMN_NUMBER,
}),
},
gauge: {
...(showThresholdMarkers &&
thresholds &&
thresholds.length && {
threshold: {
line: { color: thresholds[0]?.color || 'red', width: 4 },
thickness: 0.75,
value: thresholds[0]?.value || 0,
},
}),
// threshold labels
...(showThresholdLabels && thresholds && thresholds.length
? {
axis: {
ticktext: [gauge.value, ...thresholds.map((t: ThresholdUnitType) => t.name)],
tickvals: [gauge.value, ...thresholds.map((t: ThresholdUnitType) => t.value)],
ticklen: TickLength,
},
}
: {}),
},
};
});
}, [
xaxes,
series,
queriedVizData,
fields,
thresholds,
showThresholdMarkers,
orientation,
showThresholdLabels,
titleSize,
valueSize,
]);
const mergedLayout = useMemo(() => {
const isAtleastOneFullRow = Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) > 0;
return {
grid: {
...(orientation === 'auto' || orientation === 'h'
? {
rows: Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) + 1,
columns: isAtleastOneFullRow ? PLOTLY_GAUGE_COLUMN_NUMBER : gaugeData.length,
}
: {
columns: Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) + 1,
rows: isAtleastOneFullRow ? PLOTLY_GAUGE_COLUMN_NUMBER : gaugeData.length,
}),
pattern: 'independent',
},
...layout,
...(layoutConfig.layout && layoutConfig.layout),
title: panelOptions.title || layoutConfig.layout?.title || '',
margin: {
...PLOT_MARGIN,
t: 100,
},
};
}, [layout, gaugeData.length, layoutConfig.layout, panelOptions.title, orientation]);
const mergedConfigs = {
...config,
...(layoutConfig.config && layoutConfig.config),
};
return ;
};