From fe7257ff0700ffac9956435ae99da4986b61e6b1 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Tue, 1 Dec 2020 15:06:59 -0500 Subject: [PATCH] enable sku breakdowns for unlabeled entities --- .changeset/cost-insights-calm-bikes-prove.md | 5 ++ .../ProductEntityDialog.tsx | 2 +- .../ProductInsightsChart.tsx | 68 +++++++------------ plugins/cost-insights/src/utils/assert.ts | 10 ++- plugins/cost-insights/src/utils/graphs.ts | 8 ++- plugins/cost-insights/src/utils/mockData.ts | 21 +++++- 6 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 .changeset/cost-insights-calm-bikes-prove.md diff --git a/.changeset/cost-insights-calm-bikes-prove.md b/.changeset/cost-insights-calm-bikes-prove.md new file mode 100644 index 0000000000..179350fcd9 --- /dev/null +++ b/.changeset/cost-insights-calm-bikes-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +enable SKU breakdown for unlabeled entities diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx index 4cccd638cb..c01ab67ecc 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx @@ -176,7 +176,7 @@ export const ProductEntityDialog = ({ { const classes = useStyles(); const layoutClasses = useLayoutStyles(); - const [isOpen, setOpen] = useState(false); - const [isClickable, setClickable] = useState(true); - const [selectLabel, setSelected] = useState>(null); - const [activeLabel, setActive] = useState>(null); + const [activeLabel, setActive] = useState>(); + const [selectLabel, setSelected] = useState>(); + 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 && ( setSelected(null)} + open={isSelected} + onClose={() => setSelected(undefined)} entity={entity.entities.find(e => e.id === selectLabel)} options={options} /> diff --git a/plugins/cost-insights/src/utils/assert.ts b/plugins/cost-insights/src/utils/assert.ts index 8f7caef0fa..19ddabe574 100644 --- a/plugins/cost-insights/src/utils/assert.ts +++ b/plugins/cost-insights/src/utils/assert.ts @@ -17,7 +17,15 @@ export function notEmpty( 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 diff --git a/plugins/cost-insights/src/utils/graphs.ts b/plugins/cost-insights/src/utils/graphs.ts index ac06e20587..bc82b63f89 100644 --- a/plugins/cost-insights/src/utils/graphs.ts +++ b/plugins/cost-insights/src/utils/graphs.ts @@ -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 === ''; }; diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/utils/mockData.ts index f188bd7a57..98eab19b41 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/utils/mockData.ts @@ -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',