@backstage-community/plugin-cost-insights 0.12.25 → 0.12.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/alerts/ProjectGrowthAlert.esm.js +24 -0
  3. package/dist/alerts/ProjectGrowthAlert.esm.js.map +1 -0
  4. package/dist/alerts/UnlabeledDataflowAlert.esm.js +25 -0
  5. package/dist/alerts/UnlabeledDataflowAlert.esm.js.map +1 -0
  6. package/dist/api/CostInsightsApi.esm.js +8 -0
  7. package/dist/api/CostInsightsApi.esm.js.map +1 -0
  8. package/dist/components/ActionItems/ActionItemCard.esm.js +46 -0
  9. package/dist/components/ActionItems/ActionItemCard.esm.js.map +1 -0
  10. package/dist/components/ActionItems/ActionItems.esm.js +90 -0
  11. package/dist/components/ActionItems/ActionItems.esm.js.map +1 -0
  12. package/dist/components/AlertInsights/AlertDialog.esm.js +123 -0
  13. package/dist/components/AlertInsights/AlertDialog.esm.js.map +1 -0
  14. package/dist/components/AlertInsights/AlertInsights.esm.js +157 -0
  15. package/dist/components/AlertInsights/AlertInsights.esm.js.map +1 -0
  16. package/dist/components/AlertInsights/AlertInsightsHeader.esm.js +17 -0
  17. package/dist/components/AlertInsights/AlertInsightsHeader.esm.js.map +1 -0
  18. package/dist/components/AlertInsights/AlertInsightsSection.esm.js +57 -0
  19. package/dist/components/AlertInsights/AlertInsightsSection.esm.js.map +1 -0
  20. package/dist/components/AlertInsights/AlertInsightsSectionHeader.esm.js +30 -0
  21. package/dist/components/AlertInsights/AlertInsightsSectionHeader.esm.js.map +1 -0
  22. package/dist/components/AlertInsights/AlertStatusSummary.esm.js +93 -0
  23. package/dist/components/AlertInsights/AlertStatusSummary.esm.js.map +1 -0
  24. package/dist/components/AlertInsights/AlertStatusSummaryButton.esm.js +35 -0
  25. package/dist/components/AlertInsights/AlertStatusSummaryButton.esm.js.map +1 -0
  26. package/dist/components/AlertInstructionsLayout/AlertInstructionsLayout.esm.js +47 -0
  27. package/dist/components/AlertInstructionsLayout/AlertInstructionsLayout.esm.js.map +1 -0
  28. package/dist/components/BarChart/BarChart.esm.js +167 -0
  29. package/dist/components/BarChart/BarChart.esm.js.map +1 -0
  30. package/dist/components/BarChart/BarChartLabel.esm.js +31 -0
  31. package/dist/components/BarChart/BarChartLabel.esm.js.map +1 -0
  32. package/dist/components/BarChart/BarChartLegend.esm.js +47 -0
  33. package/dist/components/BarChart/BarChartLegend.esm.js.map +1 -0
  34. package/dist/components/BarChart/BarChartStepper.esm.js +91 -0
  35. package/dist/components/BarChart/BarChartStepper.esm.js.map +1 -0
  36. package/dist/components/BarChart/BarChartStepperButton.esm.js +27 -0
  37. package/dist/components/BarChart/BarChartStepperButton.esm.js.map +1 -0
  38. package/dist/components/BarChart/BarChartSteps.esm.js +25 -0
  39. package/dist/components/BarChart/BarChartSteps.esm.js.map +1 -0
  40. package/dist/components/BarChart/BarChartTick.esm.js +29 -0
  41. package/dist/components/BarChart/BarChartTick.esm.js.map +1 -0
  42. package/dist/components/BarChart/BarChartTooltip.esm.js +30 -0
  43. package/dist/components/BarChart/BarChartTooltip.esm.js.map +1 -0
  44. package/dist/components/BarChart/BarChartTooltipItem.esm.js +24 -0
  45. package/dist/components/BarChart/BarChartTooltipItem.esm.js.map +1 -0
  46. package/dist/components/CopyUrlToClipboard/CopyUrlToClipboard.esm.js +44 -0
  47. package/dist/components/CopyUrlToClipboard/CopyUrlToClipboard.esm.js.map +1 -0
  48. package/dist/components/CostGrowth/CostGrowth.esm.js +55 -0
  49. package/dist/components/CostGrowth/CostGrowth.esm.js.map +1 -0
  50. package/dist/components/CostGrowth/CostGrowthIndicator.esm.js +31 -0
  51. package/dist/components/CostGrowth/CostGrowthIndicator.esm.js.map +1 -0
  52. package/dist/components/CostInsightsHeader/CostInsightsHeader.esm.js +57 -0
  53. package/dist/components/CostInsightsHeader/CostInsightsHeader.esm.js.map +1 -0
  54. package/dist/components/CostInsightsLayout/CostInsightsLayout.esm.js +35 -0
  55. package/dist/components/CostInsightsLayout/CostInsightsLayout.esm.js.map +1 -0
  56. package/dist/components/CostInsightsNavigation/CostInsightsNavigation.esm.js +87 -0
  57. package/dist/components/CostInsightsNavigation/CostInsightsNavigation.esm.js.map +1 -0
  58. package/dist/components/CostInsightsPage/CostInsightsPage.esm.js +229 -0
  59. package/dist/components/CostInsightsPage/CostInsightsPage.esm.js.map +1 -0
  60. package/dist/components/CostInsightsPage/CostInsightsPageRoot.esm.js +15 -0
  61. package/dist/components/CostInsightsPage/CostInsightsPageRoot.esm.js.map +1 -0
  62. package/dist/components/CostInsightsPage/CostInsightsThemeProvider.esm.js +24 -0
  63. package/dist/components/CostInsightsPage/CostInsightsThemeProvider.esm.js.map +1 -0
  64. package/dist/components/CostInsightsPage/index.esm.js +2 -0
  65. package/dist/components/CostInsightsPage/index.esm.js.map +1 -0
  66. package/dist/components/CostInsightsPage/selector.esm.js +15 -0
  67. package/dist/components/CostInsightsPage/selector.esm.js.map +1 -0
  68. package/dist/components/CostInsightsTabs/CostInsightsTabs.esm.js +89 -0
  69. package/dist/components/CostInsightsTabs/CostInsightsTabs.esm.js.map +1 -0
  70. package/dist/components/CostInsightsTabs/selector.esm.js +17 -0
  71. package/dist/components/CostInsightsTabs/selector.esm.js.map +1 -0
  72. package/dist/components/CostOverviewCard/CostOverviewBreakdownChart.esm.js +196 -0
  73. package/dist/components/CostOverviewCard/CostOverviewBreakdownChart.esm.js.map +1 -0
  74. package/dist/components/CostOverviewCard/CostOverviewCard.esm.js +99 -0
  75. package/dist/components/CostOverviewCard/CostOverviewCard.esm.js.map +1 -0
  76. package/dist/components/CostOverviewCard/CostOverviewChart.esm.js +157 -0
  77. package/dist/components/CostOverviewCard/CostOverviewChart.esm.js.map +1 -0
  78. package/dist/components/CostOverviewCard/CostOverviewHeader.esm.js +25 -0
  79. package/dist/components/CostOverviewCard/CostOverviewHeader.esm.js.map +1 -0
  80. package/dist/components/CostOverviewCard/CostOverviewLegend.esm.js +54 -0
  81. package/dist/components/CostOverviewCard/CostOverviewLegend.esm.js.map +1 -0
  82. package/dist/components/CostOverviewCard/selector.esm.js +22 -0
  83. package/dist/components/CostOverviewCard/selector.esm.js.map +1 -0
  84. package/dist/components/CurrencySelect/CurrencySelect.esm.js +52 -0
  85. package/dist/components/CurrencySelect/CurrencySelect.esm.js.map +1 -0
  86. package/dist/{esm/index-BPzWHt3A.esm.js → components/EntityCosts/EntityCosts.esm.js} +14 -80
  87. package/dist/{esm/index-BPzWHt3A.esm.js.map → components/EntityCosts/EntityCosts.esm.js.map} +1 -1
  88. package/dist/components/EntityCosts/index.esm.js +2 -0
  89. package/dist/components/EntityCosts/index.esm.js.map +1 -0
  90. package/dist/{esm/index-BA6m_qFS.esm.js → components/LabelDataflowInstructionsPage/LabelDataflowInstructionsPage.esm.js} +2 -79
  91. package/dist/components/LabelDataflowInstructionsPage/LabelDataflowInstructionsPage.esm.js.map +1 -0
  92. package/dist/components/LabelDataflowInstructionsPage/index.esm.js +2 -0
  93. package/dist/components/LabelDataflowInstructionsPage/index.esm.js.map +1 -0
  94. package/dist/components/LegendItem/LegendItem.esm.js +44 -0
  95. package/dist/components/LegendItem/LegendItem.esm.js.map +1 -0
  96. package/dist/components/MetricSelect/MetricSelect.esm.js +37 -0
  97. package/dist/components/MetricSelect/MetricSelect.esm.js.map +1 -0
  98. package/dist/components/PeriodSelect/PeriodSelect.esm.js +72 -0
  99. package/dist/components/PeriodSelect/PeriodSelect.esm.js.map +1 -0
  100. package/dist/components/ProductInsights/ProductInsights.esm.js +98 -0
  101. package/dist/components/ProductInsights/ProductInsights.esm.js.map +1 -0
  102. package/dist/components/ProductInsightsCard/ProductEntityDialog.esm.js +42 -0
  103. package/dist/components/ProductInsightsCard/ProductEntityDialog.esm.js.map +1 -0
  104. package/dist/components/ProductInsightsCard/ProductEntityTable.esm.js +139 -0
  105. package/dist/components/ProductInsightsCard/ProductEntityTable.esm.js.map +1 -0
  106. package/dist/components/ProductInsightsCard/ProductInsightsCard.esm.js +83 -0
  107. package/dist/components/ProductInsightsCard/ProductInsightsCard.esm.js.map +1 -0
  108. package/dist/components/ProductInsightsCard/ProductInsightsCardList.esm.js +34 -0
  109. package/dist/components/ProductInsightsCard/ProductInsightsCardList.esm.js.map +1 -0
  110. package/dist/components/ProductInsightsCard/ProductInsightsChart.esm.js +148 -0
  111. package/dist/components/ProductInsightsCard/ProductInsightsChart.esm.js.map +1 -0
  112. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.esm.js +20 -0
  113. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.esm.js.map +1 -0
  114. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.esm.js +46 -0
  115. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.esm.js.map +1 -0
  116. package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js +111 -0
  117. package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js.map +1 -0
  118. package/dist/components/ProjectGrowthInstructionsPage/index.esm.js +2 -0
  119. package/dist/components/ProjectGrowthInstructionsPage/index.esm.js.map +1 -0
  120. package/dist/components/ProjectSelect/ProjectSelect.esm.js +47 -0
  121. package/dist/components/ProjectSelect/ProjectSelect.esm.js.map +1 -0
  122. package/dist/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.esm.js +41 -0
  123. package/dist/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.esm.js.map +1 -0
  124. package/dist/components/WhyCostsMatter/WhyCostsMatter.esm.js +21 -0
  125. package/dist/components/WhyCostsMatter/WhyCostsMatter.esm.js.map +1 -0
  126. package/dist/example/alerts/KubernetesMigrationAlert.esm.js +86 -0
  127. package/dist/example/alerts/KubernetesMigrationAlert.esm.js.map +1 -0
  128. package/dist/example/client.esm.js +194 -0
  129. package/dist/example/client.esm.js.map +1 -0
  130. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.esm.js +33 -0
  131. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.esm.js.map +1 -0
  132. package/dist/{esm/index-BTIHr4n_.esm.js → example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChart.esm.js} +69 -48
  133. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChart.esm.js.map +1 -0
  134. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.esm.js +107 -0
  135. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.esm.js.map +1 -0
  136. package/dist/example/forms/KubernetesMigrationDismissForm.esm.js +57 -0
  137. package/dist/example/forms/KubernetesMigrationDismissForm.esm.js.map +1 -0
  138. package/dist/forms/AlertAcceptForm.esm.js +27 -0
  139. package/dist/forms/AlertAcceptForm.esm.js.map +1 -0
  140. package/dist/forms/AlertDismissForm.esm.js +100 -0
  141. package/dist/forms/AlertDismissForm.esm.js.map +1 -0
  142. package/dist/forms/AlertSnoozeForm.esm.js +52 -0
  143. package/dist/forms/AlertSnoozeForm.esm.js.map +1 -0
  144. package/dist/hooks/useConfig.esm.js +153 -0
  145. package/dist/hooks/useConfig.esm.js.map +1 -0
  146. package/dist/hooks/useCurrency.esm.js +26 -0
  147. package/dist/hooks/useCurrency.esm.js.map +1 -0
  148. package/dist/hooks/useFilters.esm.js +67 -0
  149. package/dist/hooks/useFilters.esm.js.map +1 -0
  150. package/dist/hooks/useGroups.esm.js +55 -0
  151. package/dist/hooks/useGroups.esm.js.map +1 -0
  152. package/dist/hooks/useLastCompleteBillingDate.esm.js +65 -0
  153. package/dist/hooks/useLastCompleteBillingDate.esm.js.map +1 -0
  154. package/dist/hooks/useLoading.esm.js +54 -0
  155. package/dist/hooks/useLoading.esm.js.map +1 -0
  156. package/dist/hooks/useScroll.esm.js +24 -0
  157. package/dist/hooks/useScroll.esm.js.map +1 -0
  158. package/dist/index.esm.js +37 -22
  159. package/dist/index.esm.js.map +1 -1
  160. package/dist/plugin.esm.js +55 -0
  161. package/dist/plugin.esm.js.map +1 -0
  162. package/dist/testUtils/filters.esm.js +8 -0
  163. package/dist/testUtils/filters.esm.js.map +1 -0
  164. package/dist/testUtils/loading.esm.js +14 -0
  165. package/dist/testUtils/loading.esm.js.map +1 -0
  166. package/dist/testUtils/mockData.esm.js +485 -0
  167. package/dist/testUtils/mockData.esm.js.map +1 -0
  168. package/dist/testUtils/products.esm.js +49 -0
  169. package/dist/testUtils/products.esm.js.map +1 -0
  170. package/dist/testUtils/providers.esm.js +41 -0
  171. package/dist/testUtils/providers.esm.js.map +1 -0
  172. package/dist/testUtils/testUtils.esm.js +142 -0
  173. package/dist/testUtils/testUtils.esm.js.map +1 -0
  174. package/dist/types/Alert.esm.js +66 -0
  175. package/dist/types/Alert.esm.js.map +1 -0
  176. package/dist/types/ChangeStatistic.esm.js +15 -0
  177. package/dist/types/ChangeStatistic.esm.js.map +1 -0
  178. package/dist/types/CurrencyType.esm.js +10 -0
  179. package/dist/types/CurrencyType.esm.js.map +1 -0
  180. package/dist/types/DateFormat.esm.js +4 -0
  181. package/dist/types/DateFormat.esm.js.map +1 -0
  182. package/dist/types/Duration.esm.js +10 -0
  183. package/dist/types/Duration.esm.js.map +1 -0
  184. package/dist/types/Icon.esm.js +12 -0
  185. package/dist/types/Icon.esm.js.map +1 -0
  186. package/dist/utils/alerts.esm.js +72 -0
  187. package/dist/utils/alerts.esm.js.map +1 -0
  188. package/dist/utils/assert.esm.js +29 -0
  189. package/dist/utils/assert.esm.js.map +1 -0
  190. package/dist/utils/change.esm.js +58 -0
  191. package/dist/utils/change.esm.js.map +1 -0
  192. package/dist/utils/charts.esm.js +9 -0
  193. package/dist/utils/charts.esm.js.map +1 -0
  194. package/dist/utils/config.esm.js +19 -0
  195. package/dist/utils/config.esm.js.map +1 -0
  196. package/dist/utils/currency.esm.js +56 -0
  197. package/dist/utils/currency.esm.js.map +1 -0
  198. package/dist/utils/duration.esm.js +55 -0
  199. package/dist/utils/duration.esm.js.map +1 -0
  200. package/dist/utils/filters.esm.js +14 -0
  201. package/dist/utils/filters.esm.js.map +1 -0
  202. package/dist/utils/formatters.esm.js +103 -0
  203. package/dist/utils/formatters.esm.js.map +1 -0
  204. package/dist/utils/grammar.esm.js +14 -0
  205. package/dist/utils/grammar.esm.js.map +1 -0
  206. package/dist/utils/graphs.esm.js +48 -0
  207. package/dist/utils/graphs.esm.js.map +1 -0
  208. package/dist/utils/history.esm.js +32 -0
  209. package/dist/utils/history.esm.js.map +1 -0
  210. package/dist/utils/loading.esm.js +51 -0
  211. package/dist/utils/loading.esm.js.map +1 -0
  212. package/dist/utils/navigation.esm.js +56 -0
  213. package/dist/utils/navigation.esm.js.map +1 -0
  214. package/dist/utils/scroll.esm.js +38 -0
  215. package/dist/utils/scroll.esm.js.map +1 -0
  216. package/dist/utils/sort.esm.js +10 -0
  217. package/dist/utils/sort.esm.js.map +1 -0
  218. package/dist/utils/styles.esm.js +562 -0
  219. package/dist/utils/styles.esm.js.map +1 -0
  220. package/dist/utils/sum.esm.js +4 -0
  221. package/dist/utils/sum.esm.js.map +1 -0
  222. package/package.json +17 -12
  223. package/dist/esm/index-BA6m_qFS.esm.js.map +0 -1
  224. package/dist/esm/index-BFM2mP6Z.esm.js +0 -4084
  225. package/dist/esm/index-BFM2mP6Z.esm.js.map +0 -1
  226. package/dist/esm/index-BTIHr4n_.esm.js.map +0 -1
  227. package/dist/esm/index-DaitkcPN.esm.js +0 -1136
  228. package/dist/esm/index-DaitkcPN.esm.js.map +0 -1
  229. package/dist/esm/selector-D7iXJwd9.esm.js +0 -594
  230. package/dist/esm/selector-D7iXJwd9.esm.js.map +0 -1
@@ -0,0 +1,196 @@
1
+ import React, { useState } from 'react';
2
+ import { DateTime } from 'luxon';
3
+ import Box from '@material-ui/core/Box';
4
+ import Divider from '@material-ui/core/Divider';
5
+ import { emphasize } from '@material-ui/core/styles/colorManipulator';
6
+ import Typography from '@material-ui/core/Typography';
7
+ import { useTheme } from '@material-ui/core/styles';
8
+ import FullScreenIcon from '@material-ui/icons/Fullscreen';
9
+ import { ResponsiveContainer, AreaChart, CartesianGrid, XAxis, YAxis, Tooltip, Area } from 'recharts';
10
+ import '../../types/Alert.esm.js';
11
+ import { DEFAULT_DATE_FORMAT } from '../../types/DateFormat.esm.js';
12
+ import { useCostOverviewStyles } from '../../utils/styles.esm.js';
13
+ import '@material-ui/core/Paper';
14
+ import '@material-ui/core/Slide';
15
+ import '@material-ui/icons/ChevronLeft';
16
+ import '@material-ui/icons/ChevronRight';
17
+ import '../BarChart/BarChartStepperButton.esm.js';
18
+ import '@material-ui/core/ButtonBase';
19
+ import { BarChartTooltip } from '../BarChart/BarChartTooltip.esm.js';
20
+ import { BarChartTooltipItem } from '../BarChart/BarChartTooltipItem.esm.js';
21
+ import { formatPeriod } from '../../utils/formatters.esm.js';
22
+ import { overviewGraphTickFormatter, formatGraphValue, isInvalid } from '../../utils/graphs.esm.js';
23
+ import { useConfig } from '../../hooks/useConfig.esm.js';
24
+ import '../../hooks/useCurrency.esm.js';
25
+ import { useFilters } from '../../hooks/useFilters.esm.js';
26
+ import '../../hooks/useGroups.esm.js';
27
+ import '../../hooks/useLoading.esm.js';
28
+ import '../../hooks/useScroll.esm.js';
29
+ import { useLastCompleteBillingDate } from '../../hooks/useLastCompleteBillingDate.esm.js';
30
+ import { BarChartLegend } from '../BarChart/BarChartLegend.esm.js';
31
+ import { mapFiltersToProps } from './selector.esm.js';
32
+ import { getPreviousPeriodTotalCost } from '../../utils/change.esm.js';
33
+ import { aggregationSum } from '../../utils/sum.esm.js';
34
+
35
+ const LOW_COST_THRESHOLD = 0.1;
36
+ const CostOverviewBreakdownChart = ({
37
+ costBreakdown,
38
+ responsive = true
39
+ }) => {
40
+ const theme = useTheme();
41
+ const classes = useCostOverviewStyles(theme);
42
+ const { baseCurrency } = useConfig();
43
+ const lastCompleteBillingDate = useLastCompleteBillingDate();
44
+ const { duration } = useFilters(mapFiltersToProps);
45
+ const [isExpanded, setExpanded] = useState(false);
46
+ if (!costBreakdown) {
47
+ return null;
48
+ }
49
+ const flattenedAggregation = costBreakdown.map((cost) => cost.aggregation).flat();
50
+ const totalCost = aggregationSum(flattenedAggregation);
51
+ const previousPeriodTotal = getPreviousPeriodTotalCost(
52
+ flattenedAggregation,
53
+ duration,
54
+ lastCompleteBillingDate
55
+ );
56
+ const currentPeriodTotal = totalCost - previousPeriodTotal;
57
+ const canExpand = costBreakdown.length >= 8;
58
+ const otherCategoryIds = [];
59
+ const breakdownsByDate = costBreakdown.reduce(
60
+ (breakdownByDate, breakdown) => {
61
+ const breakdownTotal = aggregationSum(breakdown.aggregation);
62
+ const isOtherCategory = canExpand && breakdownTotal < totalCost * LOW_COST_THRESHOLD;
63
+ const updatedBreakdownByDate = { ...breakdownByDate };
64
+ if (isOtherCategory) {
65
+ otherCategoryIds.push(breakdown.id);
66
+ }
67
+ breakdown.aggregation.forEach((curAggregation) => {
68
+ const costsForDate = updatedBreakdownByDate[curAggregation.date] || {};
69
+ updatedBreakdownByDate[curAggregation.date] = {
70
+ ...costsForDate,
71
+ [breakdown.id]: (costsForDate[breakdown.id] || 0) + curAggregation.amount
72
+ };
73
+ });
74
+ return updatedBreakdownByDate;
75
+ },
76
+ {}
77
+ );
78
+ const chartData = Object.keys(breakdownsByDate).map((date) => {
79
+ const costsForDate = Object.keys(breakdownsByDate[date]).reduce(
80
+ (dateCosts, breakdown) => {
81
+ const cost = breakdownsByDate[date][breakdown];
82
+ const breakdownCost = !isExpanded && otherCategoryIds.includes(breakdown) ? { Other: (dateCosts.Other || 0) + cost } : { [breakdown]: cost };
83
+ return { ...dateCosts, ...breakdownCost };
84
+ },
85
+ {}
86
+ );
87
+ return {
88
+ ...costsForDate,
89
+ date: Date.parse(date)
90
+ };
91
+ }).sort((a, b) => a.date - b.date);
92
+ const sortedBreakdowns = costBreakdown.sort(
93
+ (a, b) => aggregationSum(a.aggregation) - aggregationSum(b.aggregation)
94
+ );
95
+ const renderAreas = () => {
96
+ const separatedBreakdowns = sortedBreakdowns.filter(
97
+ (breakdown) => breakdown.id !== "Other" && !otherCategoryIds.includes(breakdown.id)
98
+ ).map((breakdown) => breakdown.id);
99
+ const breakdownsToDisplay = isExpanded ? sortedBreakdowns.map((breakdown) => breakdown.id) : ["Other", ...separatedBreakdowns];
100
+ return breakdownsToDisplay.map((breakdown, i) => {
101
+ const color = theme.palette.dataViz[(breakdownsToDisplay.length - 1 - i) % (theme.palette.dataViz.length - 1)];
102
+ return /* @__PURE__ */ React.createElement(
103
+ Area,
104
+ {
105
+ key: breakdown,
106
+ dataKey: breakdown,
107
+ isAnimationActive: false,
108
+ stackId: "1",
109
+ stroke: color,
110
+ fill: color,
111
+ onClick: () => setExpanded(true),
112
+ style: {
113
+ cursor: breakdown === "Other" && !isExpanded ? "pointer" : void 0
114
+ }
115
+ }
116
+ );
117
+ });
118
+ };
119
+ const tooltipRenderer = ({ label, payload = [] }) => {
120
+ if (isInvalid({ label, payload })) return null;
121
+ const date = typeof label === "number" ? DateTime.fromMillis(label) : DateTime.fromISO(label);
122
+ const dateTitle = date.toUTC().toFormat(DEFAULT_DATE_FORMAT);
123
+ const formatGraphValueWith = formatGraphValue(baseCurrency);
124
+ const items = payload.map((p, i) => ({
125
+ label: p.dataKey,
126
+ value: formatGraphValueWith(Number(p.value), i),
127
+ fill: p.color
128
+ }));
129
+ const expandText = /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
130
+ Divider,
131
+ {
132
+ style: {
133
+ backgroundColor: emphasize(theme.palette.divider, 1),
134
+ margin: "10px 0"
135
+ }
136
+ }
137
+ ), /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "space-between", alignItems: "center" }, /* @__PURE__ */ React.createElement(FullScreenIcon, null), /* @__PURE__ */ React.createElement(Typography, null, "Click to expand")));
138
+ return /* @__PURE__ */ React.createElement(BarChartTooltip, { title: dateTitle }, items.reverse().map((item, index) => /* @__PURE__ */ React.createElement(BarChartTooltipItem, { key: `${item.label}-${index}`, item })), canExpand && !isExpanded ? expandText : null);
139
+ };
140
+ const options = {
141
+ previousName: formatPeriod(duration, lastCompleteBillingDate, false),
142
+ currentName: formatPeriod(duration, lastCompleteBillingDate, true),
143
+ hideMarker: true
144
+ };
145
+ return /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "row" }, /* @__PURE__ */ React.createElement(
146
+ BarChartLegend,
147
+ {
148
+ costStart: previousPeriodTotal,
149
+ costEnd: currentPeriodTotal,
150
+ options
151
+ }
152
+ )), /* @__PURE__ */ React.createElement(
153
+ ResponsiveContainer,
154
+ {
155
+ width: responsive ? "100%" : classes.container.width,
156
+ height: classes.container.height
157
+ },
158
+ /* @__PURE__ */ React.createElement(
159
+ AreaChart,
160
+ {
161
+ data: chartData,
162
+ margin: {
163
+ top: 16,
164
+ right: 30,
165
+ bottom: 40
166
+ }
167
+ },
168
+ /* @__PURE__ */ React.createElement(CartesianGrid, { stroke: classes.cartesianGrid.stroke }),
169
+ /* @__PURE__ */ React.createElement(
170
+ XAxis,
171
+ {
172
+ dataKey: "date",
173
+ domain: ["dataMin", "dataMax"],
174
+ tickFormatter: overviewGraphTickFormatter,
175
+ tickCount: 6,
176
+ type: "number",
177
+ stroke: classes.axis.fill
178
+ }
179
+ ),
180
+ /* @__PURE__ */ React.createElement(
181
+ YAxis,
182
+ {
183
+ domain: [() => 0, "dataMax"],
184
+ tick: { fill: classes.axis.fill },
185
+ tickFormatter: formatGraphValue(baseCurrency),
186
+ width: classes.yAxis.width
187
+ }
188
+ ),
189
+ renderAreas(),
190
+ /* @__PURE__ */ React.createElement(Tooltip, { content: tooltipRenderer, animationDuration: 100 })
191
+ )
192
+ ));
193
+ };
194
+
195
+ export { CostOverviewBreakdownChart };
196
+ //# sourceMappingURL=CostOverviewBreakdownChart.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostOverviewBreakdownChart.esm.js","sources":["../../../src/components/CostOverviewCard/CostOverviewBreakdownChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport Divider from '@material-ui/core/Divider';\nimport { emphasize } from '@material-ui/core/styles/colorManipulator';\nimport Typography from '@material-ui/core/Typography';\nimport { useTheme } from '@material-ui/core/styles';\nimport { default as FullScreenIcon } from '@material-ui/icons/Fullscreen';\nimport {\n Area,\n AreaChart,\n CartesianGrid,\n ResponsiveContainer,\n Tooltip as RechartsTooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { DEFAULT_DATE_FORMAT, CostInsightsTheme } from '../../types';\nimport { Cost } from '@backstage-community/plugin-cost-insights-common';\nimport {\n BarChartLegend,\n BarChartTooltip as Tooltip,\n BarChartTooltipItem as TooltipItem,\n} from '../BarChart';\nimport {\n formatGraphValue,\n isInvalid,\n overviewGraphTickFormatter,\n} from '../../utils/graphs';\nimport { useCostOverviewStyles as useStyles } from '../../utils/styles';\nimport { useConfig, useFilters, useLastCompleteBillingDate } from '../../hooks';\nimport { mapFiltersToProps } from './selector';\nimport { getPreviousPeriodTotalCost } from '../../utils/change';\nimport { formatPeriod } from '../../utils/formatters';\nimport { aggregationSum } from '../../utils/sum';\nimport { BarChartLegendOptions } from '../BarChart';\nimport { TooltipRenderer } from '../../types';\n\nexport type CostOverviewBreakdownChartProps = {\n costBreakdown: Cost[];\n responsive?: boolean;\n};\n\nconst LOW_COST_THRESHOLD = 0.1;\n\nexport const CostOverviewBreakdownChart = ({\n costBreakdown,\n responsive = true,\n}: CostOverviewBreakdownChartProps) => {\n const theme = useTheme<CostInsightsTheme>();\n const classes = useStyles(theme);\n const { baseCurrency } = useConfig();\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const { duration } = useFilters(mapFiltersToProps);\n const [isExpanded, setExpanded] = useState(false);\n\n if (!costBreakdown) {\n return null;\n }\n\n const flattenedAggregation = costBreakdown\n .map(cost => cost.aggregation)\n .flat();\n\n const totalCost = aggregationSum(flattenedAggregation);\n\n const previousPeriodTotal = getPreviousPeriodTotalCost(\n flattenedAggregation,\n duration,\n lastCompleteBillingDate,\n );\n const currentPeriodTotal = totalCost - previousPeriodTotal;\n const canExpand = costBreakdown.length >= 8;\n const otherCategoryIds: string[] = [];\n\n const breakdownsByDate = costBreakdown.reduce(\n (breakdownByDate, breakdown) => {\n const breakdownTotal = aggregationSum(breakdown.aggregation);\n // Group breakdown items with less than 10% of the total cost into \"Other\" category if needed\n const isOtherCategory =\n canExpand && breakdownTotal < totalCost * LOW_COST_THRESHOLD;\n\n const updatedBreakdownByDate = { ...breakdownByDate };\n if (isOtherCategory) {\n otherCategoryIds.push(breakdown.id);\n }\n breakdown.aggregation.forEach(curAggregation => {\n const costsForDate = updatedBreakdownByDate[curAggregation.date] || {};\n\n updatedBreakdownByDate[curAggregation.date] = {\n ...costsForDate,\n [breakdown.id]:\n (costsForDate[breakdown.id] || 0) + curAggregation.amount,\n };\n });\n\n return updatedBreakdownByDate;\n },\n {} as Record<string, Record<string, number>>,\n );\n\n const chartData: Record<string, number>[] = Object.keys(breakdownsByDate)\n .map(date => {\n const costsForDate = Object.keys(breakdownsByDate[date]).reduce(\n (dateCosts, breakdown) => {\n // Group costs for items that belong to 'Other' in the chart.\n const cost = breakdownsByDate[date][breakdown];\n const breakdownCost =\n !isExpanded && otherCategoryIds.includes(breakdown)\n ? { Other: (dateCosts.Other || 0) + cost }\n : { [breakdown]: cost };\n return { ...dateCosts, ...breakdownCost };\n },\n {} as Record<string, number>,\n );\n return {\n ...costsForDate,\n date: Date.parse(date),\n };\n })\n .sort((a, b) => a.date - b.date);\n\n const sortedBreakdowns = costBreakdown.sort(\n (a, b) => aggregationSum(a.aggregation) - aggregationSum(b.aggregation),\n );\n\n const renderAreas = () => {\n const separatedBreakdowns = sortedBreakdowns\n // Check that the breakdown is a separate group and hasn't been added to 'Other'\n .filter(\n breakdown =>\n breakdown.id !== 'Other' && !otherCategoryIds.includes(breakdown.id),\n )\n .map(breakdown => breakdown.id);\n // Keep 'Other' category at the bottom of the stack\n const breakdownsToDisplay = isExpanded\n ? sortedBreakdowns.map(breakdown => breakdown.id)\n : ['Other', ...separatedBreakdowns];\n\n return breakdownsToDisplay.map((breakdown, i) => {\n // Logic to handle case where there are more items than data viz colors.\n const color =\n theme.palette.dataViz[\n (breakdownsToDisplay.length - 1 - i) %\n (theme.palette.dataViz.length - 1)\n ];\n return (\n <Area\n key={breakdown}\n dataKey={breakdown}\n isAnimationActive={false}\n stackId=\"1\"\n stroke={color}\n fill={color}\n onClick={() => setExpanded(true)}\n style={{\n cursor:\n breakdown === 'Other' && !isExpanded ? 'pointer' : undefined,\n }}\n />\n );\n });\n };\n\n const tooltipRenderer: TooltipRenderer = ({ label, payload = [] }) => {\n if (isInvalid({ label, payload })) return null;\n\n const date =\n typeof label === 'number'\n ? DateTime.fromMillis(label)\n : DateTime.fromISO(label!);\n const dateTitle = date.toUTC().toFormat(DEFAULT_DATE_FORMAT);\n const formatGraphValueWith = formatGraphValue(baseCurrency);\n const items = payload.map((p, i) => ({\n label: p.dataKey as string,\n value: formatGraphValueWith(Number(p.value), i),\n fill: p.color!,\n }));\n const expandText = (\n <Box>\n <Divider\n style={{\n backgroundColor: emphasize(theme.palette.divider, 1),\n margin: '10px 0',\n }}\n />\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\">\n <FullScreenIcon />\n <Typography>Click to expand</Typography>\n </Box>\n </Box>\n );\n return (\n <Tooltip title={dateTitle}>\n {items.reverse().map((item, index) => (\n <TooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n {canExpand && !isExpanded ? expandText : null}\n </Tooltip>\n );\n };\n\n const options: Partial<BarChartLegendOptions> = {\n previousName: formatPeriod(duration, lastCompleteBillingDate, false),\n currentName: formatPeriod(duration, lastCompleteBillingDate, true),\n hideMarker: true,\n };\n\n return (\n <Box display=\"flex\" flexDirection=\"column\">\n <Box display=\"flex\" flexDirection=\"row\">\n <BarChartLegend\n costStart={previousPeriodTotal}\n costEnd={currentPeriodTotal}\n options={options}\n />\n </Box>\n <ResponsiveContainer\n width={responsive ? '100%' : classes.container.width}\n height={classes.container.height}\n >\n <AreaChart\n data={chartData}\n margin={{\n top: 16,\n right: 30,\n bottom: 40,\n }}\n >\n <CartesianGrid stroke={classes.cartesianGrid.stroke} />\n <XAxis\n dataKey=\"date\"\n domain={['dataMin', 'dataMax']}\n tickFormatter={overviewGraphTickFormatter}\n tickCount={6}\n type=\"number\"\n stroke={classes.axis.fill}\n />\n <YAxis\n domain={[() => 0, 'dataMax']}\n tick={{ fill: classes.axis.fill }}\n tickFormatter={formatGraphValue(baseCurrency)}\n width={classes.yAxis.width}\n />\n {renderAreas()}\n <RechartsTooltip content={tooltipRenderer} animationDuration={100} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n );\n};\n"],"names":["useStyles","Tooltip","TooltipItem","RechartsTooltip"],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import Box from '@material-ui/core/Box';
3
+ import { capitalize } from '@material-ui/core/utils';
4
+ import Card from '@material-ui/core/Card';
5
+ import CardContent from '@material-ui/core/CardContent';
6
+ import Divider from '@material-ui/core/Divider';
7
+ import Tab from '@material-ui/core/Tab';
8
+ import Tabs from '@material-ui/core/Tabs';
9
+ import { useTheme } from '@material-ui/core/styles';
10
+ import { CostOverviewChart } from './CostOverviewChart.esm.js';
11
+ import { CostOverviewBreakdownChart } from './CostOverviewBreakdownChart.esm.js';
12
+ import { CostOverviewHeader } from './CostOverviewHeader.esm.js';
13
+ import { MetricSelect } from '../MetricSelect/MetricSelect.esm.js';
14
+ import { PeriodSelect } from '../PeriodSelect/PeriodSelect.esm.js';
15
+ import { useConfig } from '../../hooks/useConfig.esm.js';
16
+ import '../../hooks/useCurrency.esm.js';
17
+ import { useFilters } from '../../hooks/useFilters.esm.js';
18
+ import '../../hooks/useGroups.esm.js';
19
+ import '../../hooks/useLoading.esm.js';
20
+ import '../../hooks/useScroll.esm.js';
21
+ import '../../hooks/useLastCompleteBillingDate.esm.js';
22
+ import { mapFiltersToProps } from './selector.esm.js';
23
+ import { DefaultNavigation } from '../../utils/navigation.esm.js';
24
+ import { findAlways } from '../../utils/assert.esm.js';
25
+ import { useOverviewTabsStyles } from '../../utils/styles.esm.js';
26
+ import { ScrollAnchor } from '../../utils/scroll.esm.js';
27
+
28
+ const CostOverviewCard = ({
29
+ dailyCostData,
30
+ metricData
31
+ }) => {
32
+ const theme = useTheme();
33
+ const styles = useOverviewTabsStyles(theme);
34
+ const config = useConfig();
35
+ const [tabIndex, setTabIndex] = useState(0);
36
+ const { setDuration, setProject, setMetric, ...filters } = useFilters(mapFiltersToProps);
37
+ useEffect(() => {
38
+ const lastIndex = Object.keys(dailyCostData.groupedCosts ?? {}).length;
39
+ if (tabIndex > lastIndex) {
40
+ setTabIndex(0);
41
+ }
42
+ }, [dailyCostData, tabIndex, setTabIndex]);
43
+ const metric = filters.metric ? findAlways(config.metrics, (m) => m.kind === filters.metric) : null;
44
+ const breakdownTabs = Object.keys(dailyCostData.groupedCosts ?? {}).map(
45
+ (key) => ({
46
+ id: key,
47
+ label: `Breakdown by ${key}`,
48
+ title: `Cloud Cost By ${capitalize(key)}`
49
+ })
50
+ );
51
+ const tabs = [
52
+ { id: "overview", label: "Total cost", title: "Cloud Cost" }
53
+ ].concat(breakdownTabs);
54
+ const safeTabIndex = tabIndex > tabs.length - 1 ? 0 : tabIndex;
55
+ const OverviewTabs = () => {
56
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
57
+ Tabs,
58
+ {
59
+ indicatorColor: "primary",
60
+ onChange: (_, index) => setTabIndex(index),
61
+ value: safeTabIndex
62
+ },
63
+ tabs.map((tab, index) => /* @__PURE__ */ React.createElement(
64
+ Tab,
65
+ {
66
+ className: styles.default,
67
+ label: tab.label,
68
+ key: tab.id,
69
+ value: index,
70
+ classes: { selected: styles.selected }
71
+ }
72
+ ))
73
+ ));
74
+ };
75
+ const showMetricSelect = metricData && config.metrics.length && safeTabIndex === 0;
76
+ return /* @__PURE__ */ React.createElement(Card, { style: { position: "relative", overflow: "visible" } }, /* @__PURE__ */ React.createElement(ScrollAnchor, { id: DefaultNavigation.CostOverviewCard }), /* @__PURE__ */ React.createElement(CardContent, null, dailyCostData.groupedCosts && /* @__PURE__ */ React.createElement(OverviewTabs, null), /* @__PURE__ */ React.createElement(CostOverviewHeader, { title: tabs[safeTabIndex].title }, /* @__PURE__ */ React.createElement(PeriodSelect, { duration: filters.duration, onSelect: setDuration })), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(Box, { ml: 2, my: 1, display: "flex", flexDirection: "column" }, safeTabIndex === 0 ? /* @__PURE__ */ React.createElement(
77
+ CostOverviewChart,
78
+ {
79
+ dailyCostData,
80
+ metric,
81
+ metricData
82
+ }
83
+ ) : /* @__PURE__ */ React.createElement(
84
+ CostOverviewBreakdownChart,
85
+ {
86
+ costBreakdown: dailyCostData.groupedCosts[tabs[safeTabIndex].id]
87
+ }
88
+ )), /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "flex-end", alignItems: "center" }, showMetricSelect && /* @__PURE__ */ React.createElement(
89
+ MetricSelect,
90
+ {
91
+ metric: filters.metric,
92
+ metrics: config.metrics,
93
+ onSelect: setMetric
94
+ }
95
+ ))));
96
+ };
97
+
98
+ export { CostOverviewCard };
99
+ //# sourceMappingURL=CostOverviewCard.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostOverviewCard.esm.js","sources":["../../../src/components/CostOverviewCard/CostOverviewCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport Box from '@material-ui/core/Box';\nimport { capitalize } from '@material-ui/core/utils';\nimport Card from '@material-ui/core/Card';\nimport CardContent from '@material-ui/core/CardContent';\nimport Divider from '@material-ui/core/Divider';\nimport Tab from '@material-ui/core/Tab';\nimport Tabs from '@material-ui/core/Tabs';\nimport { useTheme } from '@material-ui/core/styles';\nimport { CostOverviewChart } from './CostOverviewChart';\nimport { CostOverviewBreakdownChart } from './CostOverviewBreakdownChart';\nimport { CostOverviewHeader } from './CostOverviewHeader';\nimport { MetricSelect } from '../MetricSelect';\nimport { PeriodSelect } from '../PeriodSelect';\nimport { useConfig, useFilters } from '../../hooks';\nimport { mapFiltersToProps } from './selector';\nimport { DefaultNavigation } from '../../utils/navigation';\nimport { findAlways } from '../../utils/assert';\nimport { CostInsightsTheme } from '../../types';\nimport {\n Cost,\n Maybe,\n MetricData,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useOverviewTabsStyles } from '../../utils/styles';\nimport { ScrollAnchor } from '../../utils/scroll';\n\nexport type CostOverviewCardProps = {\n dailyCostData: Cost;\n metricData: Maybe<MetricData>;\n};\n\nexport const CostOverviewCard = ({\n dailyCostData,\n metricData,\n}: CostOverviewCardProps) => {\n const theme = useTheme<CostInsightsTheme>();\n const styles = useOverviewTabsStyles(theme);\n const config = useConfig();\n const [tabIndex, setTabIndex] = useState(0);\n const { setDuration, setProject, setMetric, ...filters } =\n useFilters(mapFiltersToProps);\n\n // Reset tabIndex if breakdowns available change\n useEffect(() => {\n // Intentionally off-by-one to account for the overview tab\n const lastIndex = Object.keys(dailyCostData.groupedCosts ?? {}).length;\n if (tabIndex > lastIndex) {\n setTabIndex(0);\n }\n }, [dailyCostData, tabIndex, setTabIndex]);\n\n const metric = filters.metric\n ? findAlways(config.metrics, m => m.kind === filters.metric)\n : null;\n\n const breakdownTabs = Object.keys(dailyCostData.groupedCosts ?? {}).map(\n key => ({\n id: key,\n label: `Breakdown by ${key}`,\n title: `Cloud Cost By ${capitalize(key)}`,\n }),\n );\n const tabs = [\n { id: 'overview', label: 'Total cost', title: 'Cloud Cost' },\n ].concat(breakdownTabs);\n // tabIndex can temporarily be invalid while the useEffect above processes\n const safeTabIndex = tabIndex > tabs.length - 1 ? 0 : tabIndex;\n\n const OverviewTabs = () => {\n return (\n <>\n <Tabs\n indicatorColor=\"primary\"\n onChange={(_, index) => setTabIndex(index)}\n value={safeTabIndex}\n >\n {tabs.map((tab, index) => (\n <Tab\n className={styles.default}\n label={tab.label}\n key={tab.id}\n value={index}\n classes={{ selected: styles.selected }}\n />\n ))}\n </Tabs>\n </>\n );\n };\n\n // Metrics can only be selected on the total cost graph\n const showMetricSelect =\n metricData && config.metrics.length && safeTabIndex === 0;\n\n return (\n <Card style={{ position: 'relative', overflow: 'visible' }}>\n <ScrollAnchor id={DefaultNavigation.CostOverviewCard} />\n <CardContent>\n {dailyCostData.groupedCosts && <OverviewTabs />}\n <CostOverviewHeader title={tabs[safeTabIndex].title}>\n <PeriodSelect duration={filters.duration} onSelect={setDuration} />\n </CostOverviewHeader>\n <Divider />\n <Box ml={2} my={1} display=\"flex\" flexDirection=\"column\">\n {safeTabIndex === 0 ? (\n <CostOverviewChart\n dailyCostData={dailyCostData}\n metric={metric}\n metricData={metricData}\n />\n ) : (\n <CostOverviewBreakdownChart\n costBreakdown={dailyCostData.groupedCosts![tabs[safeTabIndex].id]}\n />\n )}\n </Box>\n <Box display=\"flex\" justifyContent=\"flex-end\" alignItems=\"center\">\n {showMetricSelect && (\n <MetricSelect\n metric={filters.metric}\n metrics={config.metrics}\n onSelect={setMetric}\n />\n )}\n </Box>\n </CardContent>\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ import React from 'react';
2
+ import { DateTime } from 'luxon';
3
+ import Box from '@material-ui/core/Box';
4
+ import { useTheme } from '@material-ui/core/styles';
5
+ import { ResponsiveContainer, ComposedChart, CartesianGrid, XAxis, YAxis, Area, Line, Tooltip } from 'recharts';
6
+ import '../../types/Alert.esm.js';
7
+ import { DEFAULT_DATE_FORMAT } from '../../types/DateFormat.esm.js';
8
+ import '@material-ui/core/Typography';
9
+ import { useCostOverviewStyles } from '../../utils/styles.esm.js';
10
+ import '@material-ui/core/Paper';
11
+ import '@material-ui/core/Slide';
12
+ import '@material-ui/icons/ChevronLeft';
13
+ import '@material-ui/icons/ChevronRight';
14
+ import '../BarChart/BarChartStepperButton.esm.js';
15
+ import '@material-ui/core/ButtonBase';
16
+ import { BarChartTooltip } from '../BarChart/BarChartTooltip.esm.js';
17
+ import { BarChartTooltipItem } from '../BarChart/BarChartTooltipItem.esm.js';
18
+ import 'pluralize';
19
+ import '../../utils/duration.esm.js';
20
+ import { aggregationSort } from '../../utils/sort.esm.js';
21
+ import { overviewGraphTickFormatter, isInvalid, formatGraphValue } from '../../utils/graphs.esm.js';
22
+ import { useConfig } from '../../hooks/useConfig.esm.js';
23
+ import '../../hooks/useCurrency.esm.js';
24
+ import '../../hooks/useFilters.esm.js';
25
+ import '../../hooks/useGroups.esm.js';
26
+ import '../../hooks/useLoading.esm.js';
27
+ import '../../hooks/useScroll.esm.js';
28
+ import '../../hooks/useLastCompleteBillingDate.esm.js';
29
+ import '@material-ui/core/Tooltip';
30
+ import '@material-ui/icons/Lens';
31
+ import '@material-ui/icons/HelpOutlineOutlined';
32
+ import { groupByDate, trendFrom } from '../../utils/charts.esm.js';
33
+ import { CostOverviewLegend } from './CostOverviewLegend.esm.js';
34
+
35
+ const CostOverviewChart = ({
36
+ dailyCostData,
37
+ metric,
38
+ metricData,
39
+ responsive = true
40
+ }) => {
41
+ const theme = useTheme();
42
+ const styles = useCostOverviewStyles(theme);
43
+ const { baseCurrency, hideTrendLine } = useConfig();
44
+ const data = {
45
+ dailyCost: {
46
+ dataKey: "dailyCost",
47
+ name: `Daily Cost`,
48
+ format: "currency",
49
+ data: dailyCostData
50
+ },
51
+ metric: {
52
+ dataKey: metric?.kind ?? "Unknown",
53
+ name: metric?.name ?? "Unknown",
54
+ format: metricData?.format ?? "number",
55
+ data: metricData
56
+ }
57
+ };
58
+ const metricsByDate = data.metric.data ? data.metric.data.aggregation.reduce(groupByDate, {}) : {};
59
+ const chartData = data.dailyCost.data.aggregation.slice().sort(aggregationSort).map((entry) => ({
60
+ date: Date.parse(entry.date),
61
+ trend: trendFrom(data.dailyCost.data.trendline, Date.parse(entry.date)),
62
+ dailyCost: entry.amount,
63
+ ...metric && data.metric.data ? { [data.metric.dataKey]: metricsByDate[`${entry.date}`] } : {}
64
+ }));
65
+ const tooltipRenderer = ({ label, payload = [] }) => {
66
+ if (isInvalid({ label, payload })) return null;
67
+ const dataKeys = [data.dailyCost.dataKey, data.metric.dataKey];
68
+ const date = typeof label === "number" ? DateTime.fromMillis(label) : DateTime.fromISO(label);
69
+ const title = date.toUTC().toFormat(DEFAULT_DATE_FORMAT);
70
+ const formatGraphValueWith = formatGraphValue(baseCurrency);
71
+ const items = payload.filter((p) => dataKeys.includes(p.dataKey)).map((p, i) => ({
72
+ label: p.dataKey === data.dailyCost.dataKey ? data.dailyCost.name : data.metric.name,
73
+ value: p.dataKey === data.dailyCost.dataKey ? formatGraphValueWith(Number(p.value), i, data.dailyCost.format) : formatGraphValueWith(Number(p.value), i, data.metric.format),
74
+ fill: p.dataKey === data.dailyCost.dataKey ? theme.palette.blue : theme.palette.magenta
75
+ }));
76
+ return /* @__PURE__ */ React.createElement(BarChartTooltip, { title }, items.map((item, index) => /* @__PURE__ */ React.createElement(BarChartTooltipItem, { key: `${item.label}-${index}`, item })));
77
+ };
78
+ const localizedTickFormatter = formatGraphValue(baseCurrency);
79
+ return /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "column" }, /* @__PURE__ */ React.createElement(
80
+ CostOverviewLegend,
81
+ {
82
+ dailyCostData,
83
+ metric,
84
+ metricData
85
+ }
86
+ ), /* @__PURE__ */ React.createElement(
87
+ ResponsiveContainer,
88
+ {
89
+ width: responsive ? "100%" : styles.container.width,
90
+ height: styles.container.height,
91
+ className: "cost-overview-chart"
92
+ },
93
+ /* @__PURE__ */ React.createElement(ComposedChart, { margin: styles.chart.margin, data: chartData }, /* @__PURE__ */ React.createElement(CartesianGrid, { stroke: styles.cartesianGrid.stroke }), /* @__PURE__ */ React.createElement(
94
+ XAxis,
95
+ {
96
+ dataKey: "date",
97
+ domain: ["dataMin", "dataMax"],
98
+ tickFormatter: overviewGraphTickFormatter,
99
+ tickCount: 6,
100
+ type: "number",
101
+ stroke: styles.axis.fill
102
+ }
103
+ ), /* @__PURE__ */ React.createElement(
104
+ YAxis,
105
+ {
106
+ domain: [() => 0, "dataMax"],
107
+ tick: { fill: styles.axis.fill },
108
+ tickFormatter: localizedTickFormatter,
109
+ width: styles.yAxis.width,
110
+ yAxisId: data.dailyCost.dataKey
111
+ }
112
+ ), metric && /* @__PURE__ */ React.createElement(
113
+ YAxis,
114
+ {
115
+ domain: ["auto", "auto"],
116
+ width: styles.yAxis.width,
117
+ yAxisId: data.metric.dataKey,
118
+ tickFormatter: (value, index) => localizedTickFormatter(value, index, data.metric.format),
119
+ orientation: "right"
120
+ }
121
+ ), /* @__PURE__ */ React.createElement(
122
+ Area,
123
+ {
124
+ dataKey: data.dailyCost.dataKey,
125
+ isAnimationActive: false,
126
+ fill: theme.palette.blue,
127
+ fillOpacity: 0.4,
128
+ stroke: "none",
129
+ yAxisId: data.dailyCost.dataKey
130
+ }
131
+ ), !hideTrendLine && /* @__PURE__ */ React.createElement(
132
+ Line,
133
+ {
134
+ activeDot: false,
135
+ dataKey: "trend",
136
+ dot: false,
137
+ isAnimationActive: false,
138
+ strokeWidth: 2,
139
+ stroke: theme.palette.blue,
140
+ yAxisId: data.dailyCost.dataKey
141
+ }
142
+ ), metric && /* @__PURE__ */ React.createElement(
143
+ Line,
144
+ {
145
+ dataKey: data.metric.dataKey,
146
+ dot: false,
147
+ isAnimationActive: false,
148
+ strokeWidth: 2,
149
+ stroke: theme.palette.magenta,
150
+ yAxisId: data.metric.dataKey
151
+ }
152
+ ), /* @__PURE__ */ React.createElement(Tooltip, { content: tooltipRenderer, animationDuration: 100 }))
153
+ ));
154
+ };
155
+
156
+ export { CostOverviewChart };
157
+ //# sourceMappingURL=CostOverviewChart.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostOverviewChart.esm.js","sources":["../../../src/components/CostOverviewCard/CostOverviewChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport { useTheme } from '@material-ui/core/styles';\nimport {\n ComposedChart,\n XAxis,\n YAxis,\n Tooltip as RechartsTooltip,\n CartesianGrid,\n Area,\n Line,\n ResponsiveContainer,\n} from 'recharts';\nimport { ChartData, DEFAULT_DATE_FORMAT, CostInsightsTheme } from '../../types';\nimport {\n Cost,\n Maybe,\n Metric,\n MetricData,\n} from '@backstage-community/plugin-cost-insights-common';\nimport {\n BarChartTooltip as Tooltip,\n BarChartTooltipItem as TooltipItem,\n} from '../BarChart';\nimport {\n overviewGraphTickFormatter,\n formatGraphValue,\n isInvalid,\n} from '../../utils/graphs';\nimport { useCostOverviewStyles as useStyles } from '../../utils/styles';\nimport { groupByDate, trendFrom } from '../../utils/charts';\nimport { aggregationSort } from '../../utils/sort';\nimport { CostOverviewLegend } from './CostOverviewLegend';\nimport { TooltipRenderer } from '../../types';\nimport { useConfig } from '../../hooks';\n\ntype CostOverviewChartProps = {\n metric: Maybe<Metric>;\n metricData: Maybe<MetricData>;\n dailyCostData: Cost;\n responsive?: boolean;\n};\n\nexport const CostOverviewChart = ({\n dailyCostData,\n metric,\n metricData,\n responsive = true,\n}: CostOverviewChartProps) => {\n const theme = useTheme<CostInsightsTheme>();\n const styles = useStyles(theme);\n const { baseCurrency, hideTrendLine } = useConfig();\n\n const data = {\n dailyCost: {\n dataKey: 'dailyCost',\n name: `Daily Cost`,\n format: 'currency',\n data: dailyCostData,\n },\n metric: {\n dataKey: metric?.kind ?? 'Unknown',\n name: metric?.name ?? 'Unknown',\n format: metricData?.format ?? 'number',\n data: metricData,\n },\n };\n\n const metricsByDate = data.metric.data\n ? data.metric.data.aggregation.reduce(groupByDate, {})\n : {};\n\n const chartData: ChartData[] = data.dailyCost.data.aggregation\n .slice()\n .sort(aggregationSort)\n .map(entry => ({\n date: Date.parse(entry.date),\n trend: trendFrom(data.dailyCost.data.trendline!, Date.parse(entry.date)),\n dailyCost: entry.amount,\n ...(metric && data.metric.data\n ? { [data.metric.dataKey]: metricsByDate[`${entry.date}`] }\n : {}),\n }));\n\n const tooltipRenderer: TooltipRenderer = ({ label, payload = [] }) => {\n if (isInvalid({ label, payload })) return null;\n\n const dataKeys = [data.dailyCost.dataKey, data.metric.dataKey];\n const date =\n typeof label === 'number'\n ? DateTime.fromMillis(label)\n : DateTime.fromISO(label!);\n const title = date.toUTC().toFormat(DEFAULT_DATE_FORMAT);\n const formatGraphValueWith = formatGraphValue(baseCurrency);\n const items = payload\n .filter(p => dataKeys.includes(p.dataKey as string))\n .map((p, i) => ({\n label:\n p.dataKey === data.dailyCost.dataKey\n ? data.dailyCost.name\n : data.metric.name,\n value:\n p.dataKey === data.dailyCost.dataKey\n ? formatGraphValueWith(Number(p.value), i, data.dailyCost.format)\n : formatGraphValueWith(Number(p.value), i, data.metric.format),\n fill:\n p.dataKey === data.dailyCost.dataKey\n ? theme.palette.blue\n : theme.palette.magenta,\n }));\n\n return (\n <Tooltip title={title}>\n {items.map((item, index) => (\n <TooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n </Tooltip>\n );\n };\n\n const localizedTickFormatter = formatGraphValue(baseCurrency);\n\n return (\n <Box display=\"flex\" flexDirection=\"column\">\n <CostOverviewLegend\n dailyCostData={dailyCostData}\n metric={metric}\n metricData={metricData}\n />\n <ResponsiveContainer\n width={responsive ? '100%' : styles.container.width}\n height={styles.container.height}\n className=\"cost-overview-chart\"\n >\n <ComposedChart margin={styles.chart.margin} data={chartData}>\n <CartesianGrid stroke={styles.cartesianGrid.stroke} />\n <XAxis\n dataKey=\"date\"\n domain={['dataMin', 'dataMax']}\n tickFormatter={overviewGraphTickFormatter}\n tickCount={6}\n type=\"number\"\n stroke={styles.axis.fill}\n />\n <YAxis\n domain={[() => 0, 'dataMax']}\n tick={{ fill: styles.axis.fill }}\n tickFormatter={localizedTickFormatter}\n width={styles.yAxis.width}\n yAxisId={data.dailyCost.dataKey}\n />\n {metric && (\n <YAxis\n domain={['auto', 'auto']}\n width={styles.yAxis.width}\n yAxisId={data.metric.dataKey}\n tickFormatter={(value: any, index: number) =>\n localizedTickFormatter(value, index, data.metric.format)\n }\n orientation=\"right\"\n />\n )}\n <Area\n dataKey={data.dailyCost.dataKey}\n isAnimationActive={false}\n fill={theme.palette.blue}\n fillOpacity={0.4}\n stroke=\"none\"\n yAxisId={data.dailyCost.dataKey}\n />\n {!hideTrendLine && (\n <Line\n activeDot={false}\n dataKey=\"trend\"\n dot={false}\n isAnimationActive={false}\n strokeWidth={2}\n stroke={theme.palette.blue}\n yAxisId={data.dailyCost.dataKey}\n />\n )}\n {metric && (\n <Line\n dataKey={data.metric.dataKey}\n dot={false}\n isAnimationActive={false}\n strokeWidth={2}\n stroke={theme.palette.magenta}\n yAxisId={data.metric.dataKey}\n />\n )}\n <RechartsTooltip content={tooltipRenderer} animationDuration={100} />\n </ComposedChart>\n </ResponsiveContainer>\n </Box>\n );\n};\n"],"names":["useStyles","Tooltip","TooltipItem","RechartsTooltip"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import Box from '@material-ui/core/Box';
3
+ import Typography from '@material-ui/core/Typography';
4
+
5
+ const CostOverviewHeader = ({
6
+ title,
7
+ subtitle,
8
+ children
9
+ }) => /* @__PURE__ */ React.createElement(
10
+ Box,
11
+ {
12
+ mt: 2,
13
+ ml: 1,
14
+ mb: 1,
15
+ display: "flex",
16
+ flexDirection: "row",
17
+ justifyContent: "space-between",
18
+ alignItems: "center"
19
+ },
20
+ /* @__PURE__ */ React.createElement(Box, { minHeight: 40, paddingRight: 5 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5", gutterBottom: true }, title), !!subtitle && /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", color: "textSecondary", component: "div" }, subtitle)),
21
+ /* @__PURE__ */ React.createElement(Box, { minHeight: 40, maxHeight: 60, display: "flex" }, children)
22
+ );
23
+
24
+ export { CostOverviewHeader };
25
+ //# sourceMappingURL=CostOverviewHeader.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostOverviewHeader.esm.js","sources":["../../../src/components/CostOverviewCard/CostOverviewHeader.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { PropsWithChildren } from 'react';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\n\ntype CostOverviewHeaderProps = {\n title: string;\n subtitle?: string;\n};\n\nexport const CostOverviewHeader = ({\n title,\n subtitle,\n children,\n}: PropsWithChildren<CostOverviewHeaderProps>) => (\n <Box\n mt={2}\n ml={1}\n mb={1}\n display=\"flex\"\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n >\n <Box minHeight={40} paddingRight={5}>\n <Typography variant=\"h5\" gutterBottom>\n {title}\n </Typography>\n {!!subtitle && (\n <Typography variant=\"subtitle2\" color=\"textSecondary\" component=\"div\">\n {subtitle}\n </Typography>\n )}\n </Box>\n <Box minHeight={40} maxHeight={60} display=\"flex\">\n {children}\n </Box>\n </Box>\n);\n"],"names":[],"mappings":";;;;AAwBO,MAAM,qBAAqB,CAAC;AAAA,EACjC,KAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AACF,CACE,qBAAA,KAAA,CAAA,aAAA;AAAA,EAAC,GAAA;AAAA,EAAA;AAAA,IACC,EAAI,EAAA,CAAA;AAAA,IACJ,EAAI,EAAA,CAAA;AAAA,IACJ,EAAI,EAAA,CAAA;AAAA,IACJ,OAAQ,EAAA,MAAA;AAAA,IACR,aAAc,EAAA,KAAA;AAAA,IACd,cAAe,EAAA,eAAA;AAAA,IACf,UAAW,EAAA,QAAA;AAAA,GAAA;AAAA,kBAEX,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,SAAA,EAAW,EAAI,EAAA,YAAA,EAAc,CAChC,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,IAAK,EAAA,YAAA,EAAY,IAClC,EAAA,EAAA,KACH,GACC,CAAC,CAAC,QACD,oBAAA,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,WAAY,EAAA,KAAA,EAAM,eAAgB,EAAA,SAAA,EAAU,KAC7D,EAAA,EAAA,QACH,CAEJ,CAAA;AAAA,kBACA,KAAA,CAAA,aAAA,CAAC,OAAI,SAAW,EAAA,EAAA,EAAI,WAAW,EAAI,EAAA,OAAA,EAAQ,UACxC,QACH,CAAA;AACF;;;;"}
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import Box from '@material-ui/core/Box';
3
+ import { useTheme } from '@material-ui/core/styles';
4
+ import { LegendItem } from '../LegendItem/LegendItem.esm.js';
5
+ import '../../hooks/useConfig.esm.js';
6
+ import '../../hooks/useCurrency.esm.js';
7
+ import { useFilters } from '../../hooks/useFilters.esm.js';
8
+ import '../../hooks/useGroups.esm.js';
9
+ import '../../hooks/useLoading.esm.js';
10
+ import '../../hooks/useScroll.esm.js';
11
+ import { useLastCompleteBillingDate } from '../../hooks/useLastCompleteBillingDate.esm.js';
12
+ import { getComparedChange, choose } from '../../utils/change.esm.js';
13
+ import { mapFiltersToProps } from './selector.esm.js';
14
+ import { formatChange } from '../../utils/formatters.esm.js';
15
+ import { CostGrowth } from '../CostGrowth/CostGrowth.esm.js';
16
+ import 'classnames';
17
+ import '@material-ui/core/Typography';
18
+ import '@material-ui/icons/ArrowDropUp';
19
+ import '@material-ui/icons/ArrowDropDown';
20
+ import '../../types/Alert.esm.js';
21
+ import '../../utils/styles.esm.js';
22
+
23
+ const CostOverviewLegend = ({
24
+ dailyCostData,
25
+ metric,
26
+ metricData
27
+ }) => {
28
+ const theme = useTheme();
29
+ const { duration } = useFilters(mapFiltersToProps);
30
+ const lastCompleteBillingDate = useLastCompleteBillingDate();
31
+ const comparedChange = metricData ? getComparedChange(
32
+ dailyCostData,
33
+ metricData,
34
+ duration,
35
+ lastCompleteBillingDate
36
+ ) : null;
37
+ return /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "row" }, dailyCostData.change && /* @__PURE__ */ React.createElement(Box, { mr: 2 }, /* @__PURE__ */ React.createElement(LegendItem, { title: "Cost Trend", markerColor: theme.palette.blue }, formatChange(dailyCostData.change))), metricData && metric && comparedChange && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { mr: 2 }, /* @__PURE__ */ React.createElement(
38
+ LegendItem,
39
+ {
40
+ title: `${metric.name} Trend`,
41
+ markerColor: theme.palette.magenta
42
+ },
43
+ formatChange(metricData.change)
44
+ )), /* @__PURE__ */ React.createElement(
45
+ LegendItem,
46
+ {
47
+ title: choose(["Your Savings", "Your Excess"], comparedChange)
48
+ },
49
+ /* @__PURE__ */ React.createElement(CostGrowth, { change: comparedChange, duration })
50
+ )));
51
+ };
52
+
53
+ export { CostOverviewLegend };
54
+ //# sourceMappingURL=CostOverviewLegend.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostOverviewLegend.esm.js","sources":["../../../src/components/CostOverviewCard/CostOverviewLegend.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren } from 'react';\nimport Box from '@material-ui/core/Box';\nimport { useTheme } from '@material-ui/core/styles';\nimport { LegendItem } from '../LegendItem';\nimport { CostInsightsTheme } from '../../types';\nimport {\n MetricData,\n Maybe,\n Cost,\n Metric,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useLastCompleteBillingDate, useFilters } from '../../hooks';\nimport { getComparedChange, choose } from '../../utils/change';\nimport { mapFiltersToProps } from './selector';\nimport { formatChange } from '../../utils/formatters';\nimport { CostGrowth } from '../CostGrowth';\n\ntype CostOverviewLegendProps = {\n metric: Maybe<Metric>;\n metricData: Maybe<MetricData>;\n dailyCostData: Cost;\n};\n\nexport const CostOverviewLegend = ({\n dailyCostData,\n metric,\n metricData,\n}: PropsWithChildren<CostOverviewLegendProps>) => {\n const theme = useTheme<CostInsightsTheme>();\n const { duration } = useFilters(mapFiltersToProps);\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n\n const comparedChange = metricData\n ? getComparedChange(\n dailyCostData,\n metricData,\n duration,\n lastCompleteBillingDate,\n )\n : null;\n\n return (\n <Box display=\"flex\" flexDirection=\"row\">\n {dailyCostData.change && (\n <Box mr={2}>\n <LegendItem title=\"Cost Trend\" markerColor={theme.palette.blue}>\n {formatChange(dailyCostData.change)}\n </LegendItem>\n </Box>\n )}\n {metricData && metric && comparedChange && (\n <>\n <Box mr={2}>\n <LegendItem\n title={`${metric.name} Trend`}\n markerColor={theme.palette.magenta}\n >\n {formatChange(metricData.change)}\n </LegendItem>\n </Box>\n <LegendItem\n title={choose(['Your Savings', 'Your Excess'], comparedChange)}\n >\n <CostGrowth change={comparedChange} duration={duration} />\n </LegendItem>\n </>\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuCO,MAAM,qBAAqB,CAAC;AAAA,EACjC,aAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AACF,CAAkD,KAAA;AAChD,EAAA,MAAM,QAAQ,QAA4B,EAAA,CAAA;AAC1C,EAAA,MAAM,EAAE,QAAA,EAAa,GAAA,UAAA,CAAW,iBAAiB,CAAA,CAAA;AACjD,EAAA,MAAM,0BAA0B,0BAA2B,EAAA,CAAA;AAE3D,EAAA,MAAM,iBAAiB,UACnB,GAAA,iBAAA;AAAA,IACE,aAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,uBAAA;AAAA,GAEF,GAAA,IAAA,CAAA;AAEJ,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,OAAQ,EAAA,MAAA,EAAO,eAAc,KAC/B,EAAA,EAAA,aAAA,CAAc,MACb,oBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,EAAA,EAAI,CACP,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,KAAM,EAAA,YAAA,EAAa,WAAa,EAAA,KAAA,CAAM,OAAQ,CAAA,IAAA,EAAA,EACvD,YAAa,CAAA,aAAA,CAAc,MAAM,CACpC,CACF,CAED,EAAA,UAAA,IAAc,UAAU,cACvB,oBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBACG,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,IAAI,CACP,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAG,EAAA,MAAA,CAAO,IAAI,CAAA,MAAA,CAAA;AAAA,MACrB,WAAA,EAAa,MAAM,OAAQ,CAAA,OAAA;AAAA,KAAA;AAAA,IAE1B,YAAA,CAAa,WAAW,MAAM,CAAA;AAAA,GAEnC,CACA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,OAAO,MAAO,CAAA,CAAC,cAAgB,EAAA,aAAa,GAAG,cAAc,CAAA;AAAA,KAAA;AAAA,oBAE5D,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,MAAQ,EAAA,cAAA,EAAgB,QAAoB,EAAA,CAAA;AAAA,GAE5D,CAEJ,CAAA,CAAA;AAEJ;;;;"}