Merge pull request #3518 from backstage/ryanv/enable-unlabeled-dataflow-skus
enable sku breakdowns for unlabeled entities
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
enable SKU breakdown for unlabeled entities
|
||||
@@ -176,7 +176,7 @@ export const ProductEntityDialog = ({
|
||||
<Table
|
||||
columns={columns}
|
||||
data={rowData}
|
||||
title={entity.id || 'Unknown'}
|
||||
title={entity.id || 'Unlabeled'}
|
||||
subtitle="Resource breakdown"
|
||||
options={{
|
||||
paging: false,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
ContentRenderer,
|
||||
TooltipProps as RechartsTooltipProps,
|
||||
@@ -33,14 +33,15 @@ import {
|
||||
BarChartLegendOptions,
|
||||
} from '../BarChart';
|
||||
import { pluralOf } from '../../utils/grammar';
|
||||
import { findAlways, notEmpty } from '../../utils/assert';
|
||||
import { findAlways, notEmpty, isUndefined } from '../../utils/assert';
|
||||
import { formatPeriod, formatPercent } from '../../utils/formatters';
|
||||
import {
|
||||
titleOf,
|
||||
tooltipItemOf,
|
||||
resourceOf,
|
||||
isInvalid,
|
||||
isActiveLabel,
|
||||
isLabeled,
|
||||
isUnlabeled,
|
||||
} from '../../utils/graphs';
|
||||
import {
|
||||
useProductInsightsChartStyles as useStyles,
|
||||
@@ -61,10 +62,14 @@ export const ProductInsightsChart = ({
|
||||
}: ProductInsightsChartProps) => {
|
||||
const classes = useStyles();
|
||||
const layoutClasses = useLayoutStyles();
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [isClickable, setClickable] = useState(true);
|
||||
const [selectLabel, setSelected] = useState<Maybe<string>>(null);
|
||||
const [activeLabel, setActive] = useState<Maybe<string>>(null);
|
||||
const [activeLabel, setActive] = useState<Maybe<string>>();
|
||||
const [selectLabel, setSelected] = useState<Maybe<string>>();
|
||||
const isSelected = useMemo(() => !isUndefined(selectLabel), [selectLabel]);
|
||||
const isClickable = useMemo(() => {
|
||||
const skus =
|
||||
entity.entities.find(e => e.id === activeLabel)?.entities ?? [];
|
||||
return skus.length > 0;
|
||||
}, [entity, activeLabel]);
|
||||
|
||||
const legendTitle = `Cost ${entity.change.ratio <= 0 ? 'Savings' : 'Growth'}`;
|
||||
const costStart = entity.aggregation[0];
|
||||
@@ -76,52 +81,25 @@ export const ProductInsightsChart = ({
|
||||
currentName: formatPeriod(duration, billingDate, true),
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
function toggleModal() {
|
||||
if (selectLabel) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleModal();
|
||||
}, [selectLabel]);
|
||||
|
||||
useEffect(() => {
|
||||
// disable click if an entity is unlabeled or if it does not have any skus
|
||||
function toggleClickableEntity() {
|
||||
if (activeLabel) {
|
||||
const hasSkus = entity.entities.find(e => e.id === activeLabel)
|
||||
?.entities.length;
|
||||
if (hasSkus) {
|
||||
setClickable(true);
|
||||
} else {
|
||||
setClickable(false);
|
||||
}
|
||||
} else {
|
||||
setClickable(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleClickableEntity();
|
||||
}, [activeLabel, entity]);
|
||||
|
||||
const onMouseMove: RechartsFunction = (
|
||||
data: Record<'activeLabel', string | undefined>,
|
||||
) => {
|
||||
if (isActiveLabel(data)) {
|
||||
if (isLabeled(data)) {
|
||||
setActive(data.activeLabel!);
|
||||
} else {
|
||||
} else if (isUnlabeled(data)) {
|
||||
setActive(null);
|
||||
} else {
|
||||
setActive(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const onClick: RechartsFunction = (data: Record<'activeLabel', string>) => {
|
||||
if (isActiveLabel(data)) {
|
||||
if (isLabeled(data)) {
|
||||
setSelected(data.activeLabel);
|
||||
} else {
|
||||
} else if (isUnlabeled(data)) {
|
||||
setSelected(null);
|
||||
} else {
|
||||
setSelected(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -213,10 +191,10 @@ export const ProductInsightsChart = ({
|
||||
options={options}
|
||||
{...barChartProps}
|
||||
/>
|
||||
{selectLabel && entity.entities.length && (
|
||||
{isSelected && entity.entities.length && (
|
||||
<ProductEntityDialog
|
||||
open={isOpen}
|
||||
onClose={() => setSelected(null)}
|
||||
open={isSelected}
|
||||
onClose={() => setSelected(undefined)}
|
||||
entity={entity.entities.find(e => e.id === selectLabel)}
|
||||
options={options}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,15 @@
|
||||
export function notEmpty<TValue>(
|
||||
value: TValue | null | undefined,
|
||||
): value is TValue {
|
||||
return value !== null && value !== undefined;
|
||||
return !isNull(value) && !isUndefined(value);
|
||||
}
|
||||
|
||||
export function isUndefined(value: any): boolean {
|
||||
return value === undefined;
|
||||
}
|
||||
|
||||
export function isNull(value: any): boolean {
|
||||
return value === null;
|
||||
}
|
||||
|
||||
// Utility for exhaustiveness checking in switch statements
|
||||
|
||||
@@ -72,8 +72,12 @@ export const isInvalid = ({ label, payload }: TooltipProps) => {
|
||||
return label === undefined || !payload || !payload.length;
|
||||
};
|
||||
|
||||
export const isActiveLabel = (
|
||||
export const isLabeled = (data?: Record<'activeLabel', string | undefined>) => {
|
||||
return data?.activeLabel && data?.activeLabel !== '';
|
||||
};
|
||||
|
||||
export const isUnlabeled = (
|
||||
data?: Record<'activeLabel', string | undefined>,
|
||||
) => {
|
||||
return data?.activeLabel && data.activeLabel !== '';
|
||||
return data?.activeLabel === '';
|
||||
};
|
||||
|
||||
@@ -563,7 +563,26 @@ export const SampleCloudDataflowInsights: Entity = {
|
||||
ratio: 0.2,
|
||||
amount: 2_000,
|
||||
},
|
||||
entities: [],
|
||||
entities: [
|
||||
{
|
||||
id: 'Sample SKU A',
|
||||
aggregation: [3_000, 4_000],
|
||||
change: {
|
||||
ratio: 0.333333,
|
||||
amount: 1_000,
|
||||
},
|
||||
entities: [],
|
||||
},
|
||||
{
|
||||
id: 'Sample SKU B',
|
||||
aggregation: [7_000, 8_000],
|
||||
change: {
|
||||
ratio: 0.14285714,
|
||||
amount: 1_000,
|
||||
},
|
||||
entities: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'entity-a',
|
||||
|
||||
Reference in New Issue
Block a user