@backstage-community/plugin-cost-insights 0.26.0 → 0.28.0

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 (158) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/alerts/ProjectGrowthAlert.esm.js.map +1 -1
  3. package/dist/alerts/UnlabeledDataflowAlert.esm.js.map +1 -1
  4. package/dist/alpha/plugin.esm.js +2 -1
  5. package/dist/alpha/plugin.esm.js.map +1 -1
  6. package/dist/alpha/router.esm.js +1 -0
  7. package/dist/alpha/router.esm.js.map +1 -1
  8. package/dist/alpha.d.ts +31 -10
  9. package/dist/api/CostInsightsApi.esm.js.map +1 -1
  10. package/dist/components/ActionItems/ActionItemCard.esm.js.map +1 -1
  11. package/dist/components/ActionItems/ActionItems.esm.js.map +1 -1
  12. package/dist/components/AlertInsights/AlertDialog.esm.js.map +1 -1
  13. package/dist/components/AlertInsights/AlertInsights.esm.js.map +1 -1
  14. package/dist/components/AlertInsights/AlertInsightsHeader.esm.js.map +1 -1
  15. package/dist/components/AlertInsights/AlertInsightsSection.esm.js.map +1 -1
  16. package/dist/components/AlertInsights/AlertInsightsSectionHeader.esm.js.map +1 -1
  17. package/dist/components/AlertInsights/AlertStatusSummary.esm.js.map +1 -1
  18. package/dist/components/AlertInsights/AlertStatusSummaryButton.esm.js.map +1 -1
  19. package/dist/components/AlertInstructionsLayout/AlertInstructionsLayout.esm.js.map +1 -1
  20. package/dist/components/BarChart/BarChart.esm.js.map +1 -1
  21. package/dist/components/BarChart/BarChartLabel.esm.js.map +1 -1
  22. package/dist/components/BarChart/BarChartLegend.esm.js.map +1 -1
  23. package/dist/components/BarChart/BarChartStepper.esm.js.map +1 -1
  24. package/dist/components/BarChart/BarChartStepperButton.esm.js.map +1 -1
  25. package/dist/components/BarChart/BarChartSteps.esm.js.map +1 -1
  26. package/dist/components/BarChart/BarChartTick.esm.js.map +1 -1
  27. package/dist/components/BarChart/BarChartTooltip.esm.js.map +1 -1
  28. package/dist/components/BarChart/BarChartTooltipItem.esm.js.map +1 -1
  29. package/dist/components/CopyUrlToClipboard/CopyUrlToClipboard.esm.js.map +1 -1
  30. package/dist/components/CostGrowth/CostGrowth.esm.js +11 -2
  31. package/dist/components/CostGrowth/CostGrowth.esm.js.map +1 -1
  32. package/dist/components/CostGrowth/CostGrowthIndicator.esm.js.map +1 -1
  33. package/dist/components/CostInsightsHeader/CostInsightsHeader.esm.js.map +1 -1
  34. package/dist/components/CostInsightsLayout/CostInsightsLayout.esm.js.map +1 -1
  35. package/dist/components/CostInsightsNavigation/CostInsightsNavigation.esm.js.map +1 -1
  36. package/dist/components/CostInsightsPage/CostInsightsPage.esm.js +5 -1
  37. package/dist/components/CostInsightsPage/CostInsightsPage.esm.js.map +1 -1
  38. package/dist/components/CostInsightsPage/CostInsightsPageRoot.esm.js.map +1 -1
  39. package/dist/components/CostInsightsPage/CostInsightsThemeProvider.esm.js.map +1 -1
  40. package/dist/components/CostInsightsPage/index.esm.js +5 -1
  41. package/dist/components/CostInsightsPage/index.esm.js.map +1 -1
  42. package/dist/components/CostInsightsPage/selector.esm.js +1 -1
  43. package/dist/components/CostInsightsPage/selector.esm.js.map +1 -1
  44. package/dist/components/CostInsightsTabs/CostInsightsTabs.esm.js.map +1 -1
  45. package/dist/components/CostInsightsTabs/selector.esm.js.map +1 -1
  46. package/dist/components/CostOverviewCard/CostOverviewBreakdownChart.esm.js +20 -6
  47. package/dist/components/CostOverviewCard/CostOverviewBreakdownChart.esm.js.map +1 -1
  48. package/dist/components/CostOverviewCard/CostOverviewCard.esm.js +17 -4
  49. package/dist/components/CostOverviewCard/CostOverviewCard.esm.js.map +1 -1
  50. package/dist/components/CostOverviewCard/CostOverviewChart.esm.js +1 -1
  51. package/dist/components/CostOverviewCard/CostOverviewChart.esm.js.map +1 -1
  52. package/dist/components/CostOverviewCard/CostOverviewHeader.esm.js.map +1 -1
  53. package/dist/components/CostOverviewCard/CostOverviewLegend.esm.js +11 -3
  54. package/dist/components/CostOverviewCard/CostOverviewLegend.esm.js.map +1 -1
  55. package/dist/components/CostOverviewCard/selector.esm.js +3 -2
  56. package/dist/components/CostOverviewCard/selector.esm.js.map +1 -1
  57. package/dist/components/CurrencySelect/CurrencySelect.esm.js.map +1 -1
  58. package/dist/components/EntityCosts/EntityCosts.esm.js +5 -1
  59. package/dist/components/EntityCosts/EntityCosts.esm.js.map +1 -1
  60. package/dist/components/EntityCosts/index.esm.js +5 -1
  61. package/dist/components/EntityCosts/index.esm.js.map +1 -1
  62. package/dist/components/LabelDataflowInstructionsPage/LabelDataflowInstructionsPage.esm.js.map +1 -1
  63. package/dist/components/LabelDataflowInstructionsPage/index.esm.js +5 -1
  64. package/dist/components/LabelDataflowInstructionsPage/index.esm.js.map +1 -1
  65. package/dist/components/LegendItem/LegendItem.esm.js.map +1 -1
  66. package/dist/components/MetricSelect/MetricSelect.esm.js.map +1 -1
  67. package/dist/components/PeriodSelect/DateRangePicker.esm.js +218 -0
  68. package/dist/components/PeriodSelect/DateRangePicker.esm.js.map +1 -0
  69. package/dist/components/ProductInsights/ProductInsights.esm.js +9 -2
  70. package/dist/components/ProductInsights/ProductInsights.esm.js.map +1 -1
  71. package/dist/components/ProductInsightsCard/ProductEntityDialog.esm.js.map +1 -1
  72. package/dist/components/ProductInsightsCard/ProductEntityTable.esm.js.map +1 -1
  73. package/dist/components/ProductInsightsCard/ProductInsightsCard.esm.js +32 -8
  74. package/dist/components/ProductInsightsCard/ProductInsightsCard.esm.js.map +1 -1
  75. package/dist/components/ProductInsightsCard/ProductInsightsCardList.esm.js.map +1 -1
  76. package/dist/components/ProductInsightsCard/ProductInsightsChart.esm.js +24 -4
  77. package/dist/components/ProductInsightsCard/ProductInsightsChart.esm.js.map +1 -1
  78. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.esm.js.map +1 -1
  79. package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.esm.js.map +1 -1
  80. package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js +1 -1
  81. package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js.map +1 -1
  82. package/dist/components/ProjectGrowthInstructionsPage/index.esm.js +5 -1
  83. package/dist/components/ProjectGrowthInstructionsPage/index.esm.js.map +1 -1
  84. package/dist/components/ProjectSelect/ProjectSelect.esm.js.map +1 -1
  85. package/dist/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.esm.js.map +1 -1
  86. package/dist/components/WhyCostsMatter/WhyCostsMatter.esm.js.map +1 -1
  87. package/dist/example/alerts/KubernetesMigrationAlert.esm.js.map +1 -1
  88. package/dist/example/client.esm.js +1 -1
  89. package/dist/example/client.esm.js.map +1 -1
  90. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.esm.js.map +1 -1
  91. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChart.esm.js +1 -0
  92. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChart.esm.js.map +1 -1
  93. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.esm.js +1 -0
  94. package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.esm.js.map +1 -1
  95. package/dist/example/forms/KubernetesMigrationDismissForm.esm.js.map +1 -1
  96. package/dist/forms/AlertAcceptForm.esm.js.map +1 -1
  97. package/dist/forms/AlertDismissForm.esm.js.map +1 -1
  98. package/dist/forms/AlertSnoozeForm.esm.js.map +1 -1
  99. package/dist/hooks/useConfig.esm.js +2 -2
  100. package/dist/hooks/useConfig.esm.js.map +1 -1
  101. package/dist/hooks/useCurrency.esm.js +1 -1
  102. package/dist/hooks/useCurrency.esm.js.map +1 -1
  103. package/dist/hooks/useFilters.esm.js +1 -1
  104. package/dist/hooks/useFilters.esm.js.map +1 -1
  105. package/dist/hooks/useGroups.esm.js +1 -1
  106. package/dist/hooks/useGroups.esm.js.map +1 -1
  107. package/dist/hooks/useLastCompleteBillingDate.esm.js +1 -1
  108. package/dist/hooks/useLastCompleteBillingDate.esm.js.map +1 -1
  109. package/dist/hooks/useLoading.esm.js +1 -1
  110. package/dist/hooks/useLoading.esm.js.map +1 -1
  111. package/dist/hooks/useScroll.esm.js +1 -1
  112. package/dist/hooks/useScroll.esm.js.map +1 -1
  113. package/dist/index.d.ts +16 -2
  114. package/dist/index.esm.js +1 -0
  115. package/dist/index.esm.js.map +1 -1
  116. package/dist/plugin.esm.js +1 -1
  117. package/dist/plugin.esm.js.map +1 -1
  118. package/dist/routes.esm.js.map +1 -1
  119. package/dist/testUtils/filters.esm.js.map +1 -1
  120. package/dist/testUtils/loading.esm.js.map +1 -1
  121. package/dist/testUtils/mockData.esm.js.map +1 -1
  122. package/dist/testUtils/products.esm.js.map +1 -1
  123. package/dist/testUtils/providers.esm.js.map +1 -1
  124. package/dist/testUtils/testUtils.esm.js +30 -11
  125. package/dist/testUtils/testUtils.esm.js.map +1 -1
  126. package/dist/types/Alert.esm.js.map +1 -1
  127. package/dist/types/ChangeStatistic.esm.js.map +1 -1
  128. package/dist/types/CurrencyType.esm.js.map +1 -1
  129. package/dist/types/DateFormat.esm.js.map +1 -1
  130. package/dist/types/Duration.esm.js +1 -0
  131. package/dist/types/Duration.esm.js.map +1 -1
  132. package/dist/types/Icon.esm.js.map +1 -1
  133. package/dist/utils/alerts.esm.js.map +1 -1
  134. package/dist/utils/assert.esm.js.map +1 -1
  135. package/dist/utils/change.esm.js +20 -6
  136. package/dist/utils/change.esm.js.map +1 -1
  137. package/dist/utils/charts.esm.js.map +1 -1
  138. package/dist/utils/config.esm.js.map +1 -1
  139. package/dist/utils/currency.esm.js +6 -1
  140. package/dist/utils/currency.esm.js.map +1 -1
  141. package/dist/utils/duration.esm.js +41 -5
  142. package/dist/utils/duration.esm.js.map +1 -1
  143. package/dist/utils/filters.esm.js.map +1 -1
  144. package/dist/utils/formatters.esm.js +25 -4
  145. package/dist/utils/formatters.esm.js.map +1 -1
  146. package/dist/utils/grammar.esm.js.map +1 -1
  147. package/dist/utils/graphs.esm.js +1 -1
  148. package/dist/utils/graphs.esm.js.map +1 -1
  149. package/dist/utils/history.esm.js.map +1 -1
  150. package/dist/utils/loading.esm.js.map +1 -1
  151. package/dist/utils/navigation.esm.js.map +1 -1
  152. package/dist/utils/scroll.esm.js.map +1 -1
  153. package/dist/utils/sort.esm.js.map +1 -1
  154. package/dist/utils/styles.esm.js.map +1 -1
  155. package/dist/utils/sum.esm.js.map +1 -1
  156. package/package.json +15 -13
  157. package/dist/components/PeriodSelect/PeriodSelect.esm.js +0 -72
  158. package/dist/components/PeriodSelect/PeriodSelect.esm.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"assert.esm.js","sources":["../../src/utils/assert.ts"],"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\nexport function notEmpty<TValue>(\n value: TValue | null | undefined,\n): value is TValue {\n return !isNull(value) && !isUndefined(value);\n}\n\nexport function isUndefined(value: any): value is undefined {\n return value === undefined;\n}\n\nexport function isNull(value: any): boolean {\n return value === null;\n}\n\n// Utility for exhaustiveness checking in switch statements\nexport function assertNever(x: never): never {\n throw new Error(`Exhaustiveness check failed: ${x}`);\n}\n\nexport function assertAlways<T>(argument: T | undefined): T {\n if (argument === undefined) {\n throw new TypeError(\n 'Expected to always find a value but received undefined',\n );\n }\n return argument;\n}\n\n// Utility for working with static lists; asserts a value will always be found or\n// throws an error\nexport function findAlways<T>(\n collection: T[],\n callback: (el: T) => boolean,\n): T {\n return assertAlways(collection.find(callback));\n}\n\nexport function findAnyKey<T>(\n record: Record<string, T> | undefined,\n): string | undefined {\n return Object.keys(record ?? {}).find(_ => true);\n}\n"],"names":[],"mappings":"AAgBO,SAAS,SACd,KACiB,EAAA;AACjB,EAAA,OAAO,CAAC,MAAO,CAAA,KAAK,CAAK,IAAA,CAAC,YAAY,KAAK,CAAA;AAC7C;AAEO,SAAS,YAAY,KAAgC,EAAA;AAC1D,EAAA,OAAO,KAAU,KAAA,KAAA,CAAA;AACnB;AAEO,SAAS,OAAO,KAAqB,EAAA;AAC1C,EAAA,OAAO,KAAU,KAAA,IAAA;AACnB;AAGO,SAAS,YAAY,CAAiB,EAAA;AAC3C,EAAA,MAAM,IAAI,KAAA,CAAM,CAAgC,6BAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AACrD;AAEO,SAAS,aAAgB,QAA4B,EAAA;AAC1D,EAAA,IAAI,aAAa,KAAW,CAAA,EAAA;AAC1B,IAAA,MAAM,IAAI,SAAA;AAAA,MACR;AAAA,KACF;AAAA;AAEF,EAAO,OAAA,QAAA;AACT;AAIgB,SAAA,UAAA,CACd,YACA,QACG,EAAA;AACH,EAAA,OAAO,YAAa,CAAA,UAAA,CAAW,IAAK,CAAA,QAAQ,CAAC,CAAA;AAC/C;AAEO,SAAS,WACd,MACoB,EAAA;AACpB,EAAO,OAAA,MAAA,CAAO,KAAK,MAAU,IAAA,EAAE,CAAE,CAAA,IAAA,CAAK,OAAK,IAAI,CAAA;AACjD;;;;"}
1
+ {"version":3,"file":"assert.esm.js","sources":["../../src/utils/assert.ts"],"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\nexport function notEmpty<TValue>(\n value: TValue | null | undefined,\n): value is TValue {\n return !isNull(value) && !isUndefined(value);\n}\n\nexport function isUndefined(value: any): value is undefined {\n return value === undefined;\n}\n\nexport function isNull(value: any): boolean {\n return value === null;\n}\n\n// Utility for exhaustiveness checking in switch statements\nexport function assertNever(x: never): never {\n throw new Error(`Exhaustiveness check failed: ${x}`);\n}\n\nexport function assertAlways<T>(argument: T | undefined): T {\n if (argument === undefined) {\n throw new TypeError(\n 'Expected to always find a value but received undefined',\n );\n }\n return argument;\n}\n\n// Utility for working with static lists; asserts a value will always be found or\n// throws an error\nexport function findAlways<T>(\n collection: T[],\n callback: (el: T) => boolean,\n): T {\n return assertAlways(collection.find(callback));\n}\n\nexport function findAnyKey<T>(\n record: Record<string, T> | undefined,\n): string | undefined {\n return Object.keys(record ?? {}).find(_ => true);\n}\n"],"names":[],"mappings":"AAgBO,SAAS,SACd,KAAA,EACiB;AACjB,EAAA,OAAO,CAAC,MAAA,CAAO,KAAK,CAAA,IAAK,CAAC,YAAY,KAAK,CAAA;AAC7C;AAEO,SAAS,YAAY,KAAA,EAAgC;AAC1D,EAAA,OAAO,KAAA,KAAU,MAAA;AACnB;AAEO,SAAS,OAAO,KAAA,EAAqB;AAC1C,EAAA,OAAO,KAAA,KAAU,IAAA;AACnB;AAGO,SAAS,YAAY,CAAA,EAAiB;AAC3C,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,CAAC,CAAA,CAAE,CAAA;AACrD;AAEO,SAAS,aAAgB,QAAA,EAA4B;AAC1D,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,MAAM,IAAI,SAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAIO,SAAS,UAAA,CACd,YACA,QAAA,EACG;AACH,EAAA,OAAO,YAAA,CAAa,UAAA,CAAW,IAAA,CAAK,QAAQ,CAAC,CAAA;AAC/C;AAEO,SAAS,WACd,MAAA,EACoB;AACpB,EAAA,OAAO,MAAA,CAAO,KAAK,MAAA,IAAU,EAAE,CAAA,CAAE,IAAA,CAAK,OAAK,IAAI,CAAA;AACjD;;;;"}
@@ -1,6 +1,7 @@
1
1
  import '../types/Alert.esm.js';
2
2
  import { ChangeThreshold, GrowthType } from '../types/ChangeStatistic.esm.js';
3
- import { Duration, DateTime } from 'luxon';
3
+ import { Duration } from '../types/Duration.esm.js';
4
+ import { Duration as Duration$1, DateTime } from 'luxon';
4
5
  import { inclusiveStartDateOf } from './duration.esm.js';
5
6
  import { notEmpty } from './assert.esm.js';
6
7
 
@@ -23,13 +24,14 @@ function growthOf(change, engineerThreshold) {
23
24
  }
24
25
  return GrowthType.Negligible;
25
26
  }
26
- function getComparedChange(dailyCost, metricData, duration, lastCompleteBillingDate) {
27
+ function getComparedChange(dailyCost, metricData, duration, lastCompleteBillingDate, customDateRange) {
27
28
  const dailyCostRatio = dailyCost.change?.ratio;
28
29
  const metricDataRatio = metricData.change?.ratio;
29
30
  const previousPeriodTotal = getPreviousPeriodTotalCost(
30
31
  dailyCost.aggregation,
31
32
  duration,
32
- lastCompleteBillingDate
33
+ lastCompleteBillingDate,
34
+ customDateRange
33
35
  );
34
36
  if (!notEmpty(dailyCostRatio) || !notEmpty(metricDataRatio)) {
35
37
  return {
@@ -41,9 +43,21 @@ function getComparedChange(dailyCost, metricData, duration, lastCompleteBillingD
41
43
  amount: previousPeriodTotal * (dailyCostRatio - metricDataRatio)
42
44
  };
43
45
  }
44
- function getPreviousPeriodTotalCost(aggregation, duration, inclusiveEndDate) {
45
- const luxonDuration = Duration.fromISO(duration);
46
- const startDate = inclusiveStartDateOf(duration, inclusiveEndDate);
46
+ function getPreviousPeriodTotalCost(aggregation, duration, inclusiveEndDate, customDateRange, comparisonMode = false) {
47
+ if (duration === Duration.CUSTOM && !comparisonMode) {
48
+ return 0;
49
+ }
50
+ const luxonDuration = duration === Duration.CUSTOM && customDateRange ? Duration$1.fromMillis(
51
+ DateTime.fromISO(customDateRange.end).diff(
52
+ DateTime.fromISO(customDateRange.start)
53
+ ).milliseconds
54
+ ) : Duration$1.fromISO(duration);
55
+ const startDate = inclusiveStartDateOf(
56
+ duration,
57
+ inclusiveEndDate,
58
+ customDateRange,
59
+ comparisonMode
60
+ );
47
61
  const nextPeriodStart = DateTime.fromISO(startDate).plus(luxonDuration);
48
62
  return aggregation.reduce((acc, costByDate) => {
49
63
  return DateTime.fromISO(costByDate.date) < nextPeriodStart ? acc + costByDate.amount : acc;
@@ -1 +1 @@
1
- {"version":3,"file":"change.esm.js","sources":["../../src/utils/change.ts"],"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 { ChangeThreshold, GrowthType, Duration } from '../types';\nimport {\n Cost,\n ChangeStatistic,\n MetricData,\n DateAggregation,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { DateTime, Duration as LuxonDuration } from 'luxon';\nimport { inclusiveStartDateOf } from './duration';\nimport { notEmpty } from './assert';\n\n// Used for displaying status colors\nexport function growthOf(\n change: ChangeStatistic,\n engineerThreshold: number,\n): GrowthType {\n const exceedsEngineerThreshold = Math.abs(change.amount) >= engineerThreshold;\n\n if (notEmpty(change.ratio)) {\n if (exceedsEngineerThreshold && change.ratio >= ChangeThreshold.upper) {\n return GrowthType.Excess;\n }\n\n if (exceedsEngineerThreshold && change.ratio <= ChangeThreshold.lower) {\n return GrowthType.Savings;\n }\n } else {\n if (exceedsEngineerThreshold && change.amount > 0) {\n return GrowthType.Excess;\n }\n if (exceedsEngineerThreshold && change.amount < 0) {\n return GrowthType.Savings;\n }\n }\n\n return GrowthType.Negligible;\n}\n\n// Used by <CostOverviewCard /> for displaying engineer totals\nexport function getComparedChange(\n dailyCost: Cost,\n metricData: MetricData,\n duration: Duration,\n lastCompleteBillingDate: string, // YYYY-MM-DD,\n): ChangeStatistic {\n const dailyCostRatio = dailyCost.change?.ratio;\n const metricDataRatio = metricData.change?.ratio;\n const previousPeriodTotal = getPreviousPeriodTotalCost(\n dailyCost.aggregation,\n duration,\n lastCompleteBillingDate,\n );\n\n // if either ratio cannot be calculated, no compared ratio can be calculated\n if (!notEmpty(dailyCostRatio) || !notEmpty(metricDataRatio)) {\n return {\n amount: previousPeriodTotal,\n };\n }\n\n return {\n ratio: dailyCostRatio - metricDataRatio,\n amount: previousPeriodTotal * (dailyCostRatio - metricDataRatio),\n };\n}\n\nexport function getPreviousPeriodTotalCost(\n aggregation: DateAggregation[],\n duration: Duration,\n inclusiveEndDate: string,\n): number {\n const luxonDuration = LuxonDuration.fromISO(duration);\n const startDate = inclusiveStartDateOf(duration, inclusiveEndDate);\n const nextPeriodStart = DateTime.fromISO(startDate).plus(luxonDuration);\n // Add up costs that incurred before the start of the next period.\n return aggregation.reduce((acc, costByDate) => {\n return DateTime.fromISO(costByDate.date) < nextPeriodStart\n ? acc + costByDate.amount\n : acc;\n }, 0);\n}\n\nexport function choose<T>(\n [savings, excess]: [T, T],\n change: ChangeStatistic,\n): T {\n const isSavings = (change.ratio ?? change.amount) <= 0;\n return isSavings ? savings : excess;\n}\n"],"names":["LuxonDuration"],"mappings":";;;;;;AA4BgB,SAAA,QAAA,CACd,QACA,iBACY,EAAA;AACZ,EAAA,MAAM,wBAA2B,GAAA,IAAA,CAAK,GAAI,CAAA,MAAA,CAAO,MAAM,CAAK,IAAA,iBAAA;AAE5D,EAAI,IAAA,QAAA,CAAS,MAAO,CAAA,KAAK,CAAG,EAAA;AAC1B,IAAA,IAAI,wBAA4B,IAAA,MAAA,CAAO,KAAS,IAAA,eAAA,CAAgB,KAAO,EAAA;AACrE,MAAA,OAAO,UAAW,CAAA,MAAA;AAAA;AAGpB,IAAA,IAAI,wBAA4B,IAAA,MAAA,CAAO,KAAS,IAAA,eAAA,CAAgB,KAAO,EAAA;AACrE,MAAA,OAAO,UAAW,CAAA,OAAA;AAAA;AACpB,GACK,MAAA;AACL,IAAI,IAAA,wBAAA,IAA4B,MAAO,CAAA,MAAA,GAAS,CAAG,EAAA;AACjD,MAAA,OAAO,UAAW,CAAA,MAAA;AAAA;AAEpB,IAAI,IAAA,wBAAA,IAA4B,MAAO,CAAA,MAAA,GAAS,CAAG,EAAA;AACjD,MAAA,OAAO,UAAW,CAAA,OAAA;AAAA;AACpB;AAGF,EAAA,OAAO,UAAW,CAAA,UAAA;AACpB;AAGO,SAAS,iBACd,CAAA,SAAA,EACA,UACA,EAAA,QAAA,EACA,uBACiB,EAAA;AACjB,EAAM,MAAA,cAAA,GAAiB,UAAU,MAAQ,EAAA,KAAA;AACzC,EAAM,MAAA,eAAA,GAAkB,WAAW,MAAQ,EAAA,KAAA;AAC3C,EAAA,MAAM,mBAAsB,GAAA,0BAAA;AAAA,IAC1B,SAAU,CAAA,WAAA;AAAA,IACV,QAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,QAAS,CAAA,cAAc,KAAK,CAAC,QAAA,CAAS,eAAe,CAAG,EAAA;AAC3D,IAAO,OAAA;AAAA,MACL,MAAQ,EAAA;AAAA,KACV;AAAA;AAGF,EAAO,OAAA;AAAA,IACL,OAAO,cAAiB,GAAA,eAAA;AAAA,IACxB,MAAA,EAAQ,uBAAuB,cAAiB,GAAA,eAAA;AAAA,GAClD;AACF;AAEgB,SAAA,0BAAA,CACd,WACA,EAAA,QAAA,EACA,gBACQ,EAAA;AACR,EAAM,MAAA,aAAA,GAAgBA,QAAc,CAAA,OAAA,CAAQ,QAAQ,CAAA;AACpD,EAAM,MAAA,SAAA,GAAY,oBAAqB,CAAA,QAAA,EAAU,gBAAgB,CAAA;AACjE,EAAA,MAAM,kBAAkB,QAAS,CAAA,OAAA,CAAQ,SAAS,CAAA,CAAE,KAAK,aAAa,CAAA;AAEtE,EAAA,OAAO,WAAY,CAAA,MAAA,CAAO,CAAC,GAAA,EAAK,UAAe,KAAA;AAC7C,IAAO,OAAA,QAAA,CAAS,QAAQ,UAAW,CAAA,IAAI,IAAI,eACvC,GAAA,GAAA,GAAM,WAAW,MACjB,GAAA,GAAA;AAAA,KACH,CAAC,CAAA;AACN;AAEO,SAAS,MACd,CAAA,CAAC,OAAS,EAAA,MAAM,GAChB,MACG,EAAA;AACH,EAAA,MAAM,SAAa,GAAA,CAAA,MAAA,CAAO,KAAS,IAAA,MAAA,CAAO,MAAW,KAAA,CAAA;AACrD,EAAA,OAAO,YAAY,OAAU,GAAA,MAAA;AAC/B;;;;"}
1
+ {"version":3,"file":"change.esm.js","sources":["../../src/utils/change.ts"],"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 { ChangeThreshold, GrowthType, Duration } from '../types';\nimport {\n Cost,\n ChangeStatistic,\n MetricData,\n DateAggregation,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { DateTime, Duration as LuxonDuration } from 'luxon';\nimport { inclusiveStartDateOf } from './duration';\nimport { notEmpty } from './assert';\n\n// Used for displaying status colors\nexport function growthOf(\n change: ChangeStatistic,\n engineerThreshold: number,\n): GrowthType {\n const exceedsEngineerThreshold = Math.abs(change.amount) >= engineerThreshold;\n\n if (notEmpty(change.ratio)) {\n if (exceedsEngineerThreshold && change.ratio >= ChangeThreshold.upper) {\n return GrowthType.Excess;\n }\n\n if (exceedsEngineerThreshold && change.ratio <= ChangeThreshold.lower) {\n return GrowthType.Savings;\n }\n } else {\n if (exceedsEngineerThreshold && change.amount > 0) {\n return GrowthType.Excess;\n }\n if (exceedsEngineerThreshold && change.amount < 0) {\n return GrowthType.Savings;\n }\n }\n\n return GrowthType.Negligible;\n}\n\n// Used by <CostOverviewCard /> for displaying engineer totals\nexport function getComparedChange(\n dailyCost: Cost,\n metricData: MetricData,\n duration: Duration,\n lastCompleteBillingDate: string, // YYYY-MM-DD,\n customDateRange?: { start: string; end: string },\n): ChangeStatistic {\n const dailyCostRatio = dailyCost.change?.ratio;\n const metricDataRatio = metricData.change?.ratio;\n const previousPeriodTotal = getPreviousPeriodTotalCost(\n dailyCost.aggregation,\n duration,\n lastCompleteBillingDate,\n customDateRange,\n );\n\n // if either ratio cannot be calculated, no compared ratio can be calculated\n if (!notEmpty(dailyCostRatio) || !notEmpty(metricDataRatio)) {\n return {\n amount: previousPeriodTotal,\n };\n }\n\n return {\n ratio: dailyCostRatio - metricDataRatio,\n amount: previousPeriodTotal * (dailyCostRatio - metricDataRatio),\n };\n}\n\nexport function getPreviousPeriodTotalCost(\n aggregation: DateAggregation[],\n duration: Duration,\n inclusiveEndDate: string,\n customDateRange?: { start: string; end: string },\n comparisonMode: boolean = false,\n): number {\n // For custom date ranges in non-comparison mode, we don't show a previous period\n if (duration === Duration.CUSTOM && !comparisonMode) {\n return 0;\n }\n\n const luxonDuration =\n duration === Duration.CUSTOM && customDateRange\n ? LuxonDuration.fromMillis(\n DateTime.fromISO(customDateRange.end).diff(\n DateTime.fromISO(customDateRange.start),\n ).milliseconds,\n )\n : LuxonDuration.fromISO(duration);\n const startDate = inclusiveStartDateOf(\n duration,\n inclusiveEndDate,\n customDateRange,\n comparisonMode,\n );\n const nextPeriodStart = DateTime.fromISO(startDate).plus(luxonDuration);\n // Add up costs that incurred before the start of the next period.\n return aggregation.reduce((acc, costByDate) => {\n return DateTime.fromISO(costByDate.date) < nextPeriodStart\n ? acc + costByDate.amount\n : acc;\n }, 0);\n}\n\nexport function choose<T>(\n [savings, excess]: [T, T],\n change: ChangeStatistic,\n): T {\n const isSavings = (change.ratio ?? change.amount) <= 0;\n return isSavings ? savings : excess;\n}\n"],"names":["LuxonDuration"],"mappings":";;;;;;;AA4BO,SAAS,QAAA,CACd,QACA,iBAAA,EACY;AACZ,EAAA,MAAM,wBAAA,GAA2B,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,IAAK,iBAAA;AAE5D,EAAA,IAAI,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA,EAAG;AAC1B,IAAA,IAAI,wBAAA,IAA4B,MAAA,CAAO,KAAA,IAAS,eAAA,CAAgB,KAAA,EAAO;AACrE,MAAA,OAAO,UAAA,CAAW,MAAA;AAAA,IACpB;AAEA,IAAA,IAAI,wBAAA,IAA4B,MAAA,CAAO,KAAA,IAAS,eAAA,CAAgB,KAAA,EAAO;AACrE,MAAA,OAAO,UAAA,CAAW,OAAA;AAAA,IACpB;AAAA,EACF,CAAA,MAAO;AACL,IAAA,IAAI,wBAAA,IAA4B,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AACjD,MAAA,OAAO,UAAA,CAAW,MAAA;AAAA,IACpB;AACA,IAAA,IAAI,wBAAA,IAA4B,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AACjD,MAAA,OAAO,UAAA,CAAW,OAAA;AAAA,IACpB;AAAA,EACF;AAEA,EAAA,OAAO,UAAA,CAAW,UAAA;AACpB;AAGO,SAAS,iBAAA,CACd,SAAA,EACA,UAAA,EACA,QAAA,EACA,yBACA,eAAA,EACiB;AACjB,EAAA,MAAM,cAAA,GAAiB,UAAU,MAAA,EAAQ,KAAA;AACzC,EAAA,MAAM,eAAA,GAAkB,WAAW,MAAA,EAAQ,KAAA;AAC3C,EAAA,MAAM,mBAAA,GAAsB,0BAAA;AAAA,IAC1B,SAAA,CAAU,WAAA;AAAA,IACV,QAAA;AAAA,IACA,uBAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,QAAA,CAAS,cAAc,KAAK,CAAC,QAAA,CAAS,eAAe,CAAA,EAAG;AAC3D,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ;AAAA,KACV;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAO,cAAA,GAAiB,eAAA;AAAA,IACxB,MAAA,EAAQ,uBAAuB,cAAA,GAAiB,eAAA;AAAA,GAClD;AACF;AAEO,SAAS,2BACd,WAAA,EACA,QAAA,EACA,gBAAA,EACA,eAAA,EACA,iBAA0B,KAAA,EAClB;AAER,EAAA,IAAI,QAAA,KAAa,QAAA,CAAS,MAAA,IAAU,CAAC,cAAA,EAAgB;AACnD,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,MAAM,aAAA,GACJ,QAAA,KAAa,QAAA,CAAS,MAAA,IAAU,kBAC5BA,UAAA,CAAc,UAAA;AAAA,IACZ,QAAA,CAAS,OAAA,CAAQ,eAAA,CAAgB,GAAG,CAAA,CAAE,IAAA;AAAA,MACpC,QAAA,CAAS,OAAA,CAAQ,eAAA,CAAgB,KAAK;AAAA,KACxC,CAAE;AAAA,GACJ,GACAA,UAAA,CAAc,OAAA,CAAQ,QAAQ,CAAA;AACpC,EAAA,MAAM,SAAA,GAAY,oBAAA;AAAA,IAChB,QAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,kBAAkB,QAAA,CAAS,OAAA,CAAQ,SAAS,CAAA,CAAE,KAAK,aAAa,CAAA;AAEtE,EAAA,OAAO,WAAA,CAAY,MAAA,CAAO,CAAC,GAAA,EAAK,UAAA,KAAe;AAC7C,IAAA,OAAO,QAAA,CAAS,QAAQ,UAAA,CAAW,IAAI,IAAI,eAAA,GACvC,GAAA,GAAM,WAAW,MAAA,GACjB,GAAA;AAAA,EACN,GAAG,CAAC,CAAA;AACN;AAEO,SAAS,MAAA,CACd,CAAC,OAAA,EAAS,MAAM,GAChB,MAAA,EACG;AACH,EAAA,MAAM,SAAA,GAAA,CAAa,MAAA,CAAO,KAAA,IAAS,MAAA,CAAO,MAAA,KAAW,CAAA;AACrD,EAAA,OAAO,YAAY,OAAA,GAAU,MAAA;AAC/B;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"charts.esm.js","sources":["../../src/utils/charts.ts"],"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 {\n DateAggregation,\n Trendline,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ChartData } from '../types';\n\nexport function trendFrom(trendline: Trendline, date: number): number {\n return trendline.slope * (date / 1000) + trendline.intercept;\n}\n\nexport function groupByDate(\n acc: Record<string, number>,\n entry: DateAggregation,\n): Record<string, number> {\n return { ...acc, [entry.date]: entry.amount };\n}\n\nexport function toMaxCost(acc: ChartData, entry: ChartData): ChartData {\n return acc.dailyCost > entry.dailyCost ? acc : entry;\n}\n\nexport function toDataMax(metric: string, data: ChartData[]): number {\n return (\n (data.reduce(toMaxCost).dailyCost / Math.abs(data[0].trend)) *\n data[0][metric]\n );\n}\n"],"names":[],"mappings":"AAsBgB,SAAA,SAAA,CAAU,WAAsB,IAAsB,EAAA;AACpE,EAAA,OAAO,SAAU,CAAA,KAAA,IAAS,IAAO,GAAA,GAAA,CAAA,GAAQ,SAAU,CAAA,SAAA;AACrD;AAEgB,SAAA,WAAA,CACd,KACA,KACwB,EAAA;AACxB,EAAO,OAAA,EAAE,GAAG,GAAK,EAAA,CAAC,MAAM,IAAI,GAAG,MAAM,MAAO,EAAA;AAC9C;;;;"}
1
+ {"version":3,"file":"charts.esm.js","sources":["../../src/utils/charts.ts"],"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 {\n DateAggregation,\n Trendline,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ChartData } from '../types';\n\nexport function trendFrom(trendline: Trendline, date: number): number {\n return trendline.slope * (date / 1000) + trendline.intercept;\n}\n\nexport function groupByDate(\n acc: Record<string, number>,\n entry: DateAggregation,\n): Record<string, number> {\n return { ...acc, [entry.date]: entry.amount };\n}\n\nexport function toMaxCost(acc: ChartData, entry: ChartData): ChartData {\n return acc.dailyCost > entry.dailyCost ? acc : entry;\n}\n\nexport function toDataMax(metric: string, data: ChartData[]): number {\n return (\n (data.reduce(toMaxCost).dailyCost / Math.abs(data[0].trend)) *\n data[0][metric]\n );\n}\n"],"names":[],"mappings":"AAsBO,SAAS,SAAA,CAAU,WAAsB,IAAA,EAAsB;AACpE,EAAA,OAAO,SAAA,CAAU,KAAA,IAAS,IAAA,GAAO,GAAA,CAAA,GAAQ,SAAA,CAAU,SAAA;AACrD;AAEO,SAAS,WAAA,CACd,KACA,KAAA,EACwB;AACxB,EAAA,OAAO,EAAE,GAAG,GAAA,EAAK,CAAC,MAAM,IAAI,GAAG,MAAM,MAAA,EAAO;AAC9C;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.esm.js","sources":["../../src/utils/config.ts"],"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 { Currency } from '../types';\nimport { Metric } from '@backstage-community/plugin-cost-insights-common';\n\nexport function validateMetrics(metrics: Metric[]) {\n const defaults = metrics.filter(metric => metric.default);\n if (defaults.length > 1) {\n throw new Error(\n `Only one default metric can be set at a time. Found ${defaults.length}`,\n );\n }\n}\n\nexport function validateCurrencies(currencies: Currency[]) {\n const withoutKinds = currencies.filter(currency => currency.kind === null);\n if (withoutKinds.length > 1) {\n throw new Error(\n `Only one currency can be without kind. Found ${withoutKinds.length}`,\n );\n }\n}\n"],"names":[],"mappings":"AAmBO,SAAS,gBAAgB,OAAmB,EAAA;AACjD,EAAA,MAAM,QAAW,GAAA,OAAA,CAAQ,MAAO,CAAA,CAAA,MAAA,KAAU,OAAO,OAAO,CAAA;AACxD,EAAI,IAAA,QAAA,CAAS,SAAS,CAAG,EAAA;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,oDAAA,EAAuD,SAAS,MAAM,CAAA;AAAA,KACxE;AAAA;AAEJ;AAEO,SAAS,mBAAmB,UAAwB,EAAA;AACzD,EAAA,MAAM,eAAe,UAAW,CAAA,MAAA,CAAO,CAAY,QAAA,KAAA,QAAA,CAAS,SAAS,IAAI,CAAA;AACzE,EAAI,IAAA,YAAA,CAAa,SAAS,CAAG,EAAA;AAC3B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6CAAA,EAAgD,aAAa,MAAM,CAAA;AAAA,KACrE;AAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"config.esm.js","sources":["../../src/utils/config.ts"],"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 { Currency } from '../types';\nimport { Metric } from '@backstage-community/plugin-cost-insights-common';\n\nexport function validateMetrics(metrics: Metric[]) {\n const defaults = metrics.filter(metric => metric.default);\n if (defaults.length > 1) {\n throw new Error(\n `Only one default metric can be set at a time. Found ${defaults.length}`,\n );\n }\n}\n\nexport function validateCurrencies(currencies: Currency[]) {\n const withoutKinds = currencies.filter(currency => currency.kind === null);\n if (withoutKinds.length > 1) {\n throw new Error(\n `Only one currency can be without kind. Found ${withoutKinds.length}`,\n );\n }\n}\n"],"names":[],"mappings":"AAmBO,SAAS,gBAAgB,OAAA,EAAmB;AACjD,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,MAAA,CAAO,CAAA,MAAA,KAAU,OAAO,OAAO,CAAA;AACxD,EAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,oDAAA,EAAuD,SAAS,MAAM,CAAA;AAAA,KACxE;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,UAAA,EAAwB;AACzD,EAAA,MAAM,eAAe,UAAA,CAAW,MAAA,CAAO,CAAA,QAAA,KAAY,QAAA,CAAS,SAAS,IAAI,CAAA;AACzE,EAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6CAAA,EAAgD,aAAa,MAAM,CAAA;AAAA,KACrE;AAAA,EACF;AACF;;;;"}
@@ -3,7 +3,7 @@ import { CurrencyType } from '../types/CurrencyType.esm.js';
3
3
  import { Duration } from '../types/Duration.esm.js';
4
4
  import { assertNever } from './assert.esm.js';
5
5
 
6
- const rateOf = (cost, duration) => {
6
+ const rateOf = (cost, duration, customDays) => {
7
7
  switch (duration) {
8
8
  case Duration.P30D:
9
9
  return cost / 12;
@@ -11,6 +11,11 @@ const rateOf = (cost, duration) => {
11
11
  case Duration.P90D:
12
12
  case Duration.P3M:
13
13
  return cost / 4;
14
+ case Duration.CUSTOM:
15
+ if (customDays) {
16
+ return cost / 365 * customDays;
17
+ }
18
+ return cost;
14
19
  default:
15
20
  return assertNever(duration);
16
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"currency.esm.js","sources":["../../src/utils/currency.ts"],"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 { Currency, CurrencyType, Duration } from '../types';\nimport { assertNever } from './assert';\n\nexport const rateOf = (cost: number, duration: Duration) => {\n switch (duration) {\n case Duration.P30D:\n return cost / 12;\n case Duration.P7D:\n case Duration.P90D:\n case Duration.P3M:\n return cost / 4;\n default:\n return assertNever(duration);\n }\n};\n\nexport const defaultCurrencies: Currency[] = [\n {\n kind: null,\n label: 'Engineers 🛠',\n unit: 'engineer',\n },\n {\n kind: CurrencyType.USD,\n label: 'US Dollars 💵',\n unit: 'dollar',\n prefix: '$',\n rate: 1,\n },\n {\n kind: CurrencyType.CarbonOffsetTons,\n label: 'Carbon Offset Tons ♻️⚖️s',\n unit: 'carbon offset ton',\n rate: 3.5,\n },\n {\n kind: CurrencyType.Beers,\n label: 'Beers 🍺',\n unit: 'beer',\n rate: 4.5,\n },\n {\n kind: CurrencyType.IceCream,\n label: 'Pints of Ice Cream 🍦',\n unit: 'ice cream pint',\n rate: 5.5,\n },\n];\n\nexport const createCurrencyFormat = (\n currency: string = 'USD',\n locale: string = 'en-US',\n) =>\n new Intl.NumberFormat(locale, {\n currency,\n style: 'currency',\n });\n"],"names":[],"mappings":";;;;;AAkBa,MAAA,MAAA,GAAS,CAAC,IAAA,EAAc,QAAuB,KAAA;AAC1D,EAAA,QAAQ,QAAU;AAAA,IAChB,KAAK,QAAS,CAAA,IAAA;AACZ,MAAA,OAAO,IAAO,GAAA,EAAA;AAAA,IAChB,KAAK,QAAS,CAAA,GAAA;AAAA,IACd,KAAK,QAAS,CAAA,IAAA;AAAA,IACd,KAAK,QAAS,CAAA,GAAA;AACZ,MAAA,OAAO,IAAO,GAAA,CAAA;AAAA,IAChB;AACE,MAAA,OAAO,YAAY,QAAQ,CAAA;AAAA;AAEjC;AAEO,MAAM,iBAAgC,GAAA;AAAA,EAC3C;AAAA,IACE,IAAM,EAAA,IAAA;AAAA,IACN,KAAO,EAAA,qBAAA;AAAA,IACP,IAAM,EAAA;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAa,CAAA,GAAA;AAAA,IACnB,KAAO,EAAA,sBAAA;AAAA,IACP,IAAM,EAAA,QAAA;AAAA,IACN,MAAQ,EAAA,GAAA;AAAA,IACR,IAAM,EAAA;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAa,CAAA,gBAAA;AAAA,IACnB,KAAO,EAAA,8CAAA;AAAA,IACP,IAAM,EAAA,mBAAA;AAAA,IACN,IAAM,EAAA;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAa,CAAA,KAAA;AAAA,IACnB,KAAO,EAAA,iBAAA;AAAA,IACP,IAAM,EAAA,MAAA;AAAA,IACN,IAAM,EAAA;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAa,CAAA,QAAA;AAAA,IACnB,KAAO,EAAA,8BAAA;AAAA,IACP,IAAM,EAAA,gBAAA;AAAA,IACN,IAAM,EAAA;AAAA;AAEV;AAEa,MAAA,oBAAA,GAAuB,CAClC,QAAmB,GAAA,KAAA,EACnB,SAAiB,OAEjB,KAAA,IAAI,IAAK,CAAA,YAAA,CAAa,MAAQ,EAAA;AAAA,EAC5B,QAAA;AAAA,EACA,KAAO,EAAA;AACT,CAAC;;;;"}
1
+ {"version":3,"file":"currency.esm.js","sources":["../../src/utils/currency.ts"],"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 { Currency, CurrencyType, Duration } from '../types';\nimport { assertNever } from './assert';\n\nexport const rateOf = (\n cost: number,\n duration: Duration,\n customDays?: number,\n) => {\n switch (duration) {\n case Duration.P30D:\n return cost / 12;\n case Duration.P7D:\n case Duration.P90D:\n case Duration.P3M:\n return cost / 4;\n case Duration.CUSTOM:\n // For custom durations, calculate rate based on number of days\n // Assuming cost is annual, calculate proportional rate\n if (customDays) {\n // Annual cost divided by days in year, multiplied by custom period days\n return (cost / 365) * customDays;\n }\n // Fallback: return the cost as-is (rate of 1)\n return cost;\n default:\n return assertNever(duration);\n }\n};\n\nexport const defaultCurrencies: Currency[] = [\n {\n kind: null,\n label: 'Engineers 🛠',\n unit: 'engineer',\n },\n {\n kind: CurrencyType.USD,\n label: 'US Dollars 💵',\n unit: 'dollar',\n prefix: '$',\n rate: 1,\n },\n {\n kind: CurrencyType.CarbonOffsetTons,\n label: 'Carbon Offset Tons ♻️⚖️s',\n unit: 'carbon offset ton',\n rate: 3.5,\n },\n {\n kind: CurrencyType.Beers,\n label: 'Beers 🍺',\n unit: 'beer',\n rate: 4.5,\n },\n {\n kind: CurrencyType.IceCream,\n label: 'Pints of Ice Cream 🍦',\n unit: 'ice cream pint',\n rate: 5.5,\n },\n];\n\nexport const createCurrencyFormat = (\n currency: string = 'USD',\n locale: string = 'en-US',\n) =>\n new Intl.NumberFormat(locale, {\n currency,\n style: 'currency',\n });\n"],"names":[],"mappings":";;;;;AAkBO,MAAM,MAAA,GAAS,CACpB,IAAA,EACA,QAAA,EACA,UAAA,KACG;AACH,EAAA,QAAQ,QAAA;AAAU,IAChB,KAAK,QAAA,CAAS,IAAA;AACZ,MAAA,OAAO,IAAA,GAAO,EAAA;AAAA,IAChB,KAAK,QAAA,CAAS,GAAA;AAAA,IACd,KAAK,QAAA,CAAS,IAAA;AAAA,IACd,KAAK,QAAA,CAAS,GAAA;AACZ,MAAA,OAAO,IAAA,GAAO,CAAA;AAAA,IAChB,KAAK,QAAA,CAAS,MAAA;AAGZ,MAAA,IAAI,UAAA,EAAY;AAEd,QAAA,OAAQ,OAAO,GAAA,GAAO,UAAA;AAAA,MACxB;AAEA,MAAA,OAAO,IAAA;AAAA,IACT;AACE,MAAA,OAAO,YAAY,QAAQ,CAAA;AAAA;AAEjC;AAEO,MAAM,iBAAA,GAAgC;AAAA,EAC3C;AAAA,IACE,IAAA,EAAM,IAAA;AAAA,IACN,KAAA,EAAO,qBAAA;AAAA,IACP,IAAA,EAAM;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAA,CAAa,GAAA;AAAA,IACnB,KAAA,EAAO,sBAAA;AAAA,IACP,IAAA,EAAM,QAAA;AAAA,IACN,MAAA,EAAQ,GAAA;AAAA,IACR,IAAA,EAAM;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAA,CAAa,gBAAA;AAAA,IACnB,KAAA,EAAO,8CAAA;AAAA,IACP,IAAA,EAAM,mBAAA;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAA,CAAa,KAAA;AAAA,IACnB,KAAA,EAAO,iBAAA;AAAA,IACP,IAAA,EAAM,MAAA;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA;AAAA,IACE,MAAM,YAAA,CAAa,QAAA;AAAA,IACnB,KAAA,EAAO,8BAAA;AAAA,IACP,IAAA,EAAM,gBAAA;AAAA,IACN,IAAA,EAAM;AAAA;AAEV;AAEO,MAAM,oBAAA,GAAuB,CAClC,QAAA,GAAmB,KAAA,EACnB,SAAiB,OAAA,KAEjB,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ;AAAA,EAC5B,QAAA;AAAA,EACA,KAAA,EAAO;AACT,CAAC;;;;"}
@@ -5,7 +5,16 @@ import { Duration } from '../types/Duration.esm.js';
5
5
  import { assertNever } from './assert.esm.js';
6
6
 
7
7
  const DEFAULT_DURATION = Duration.P30D;
8
- function inclusiveStartDateOf(duration, inclusiveEndDate) {
8
+ function inclusiveStartDateOf(duration, inclusiveEndDate, customDateRange, comparisonMode = false) {
9
+ if (duration === Duration.CUSTOM && customDateRange) {
10
+ if (comparisonMode) {
11
+ const startDate = DateTime.fromISO(customDateRange.start);
12
+ const endDate = DateTime.fromISO(customDateRange.end);
13
+ const daysDiff = Math.round(endDate.diff(startDate, "days").days) + 1;
14
+ return startDate.minus({ days: daysDiff }).toFormat(DEFAULT_DATE_FORMAT);
15
+ }
16
+ return customDateRange.start;
17
+ }
9
18
  switch (duration) {
10
19
  case Duration.P7D:
11
20
  case Duration.P30D:
@@ -17,11 +26,16 @@ function inclusiveStartDateOf(duration, inclusiveEndDate) {
17
26
  return DateTime.fromISO(inclusiveEndDate).startOf("quarter").minus(
18
27
  Duration$1.fromISO(duration).plus(Duration$1.fromISO(duration))
19
28
  ).toFormat(DEFAULT_DATE_FORMAT);
29
+ case Duration.CUSTOM:
30
+ throw new Error("CUSTOM duration requires customDateRange parameter");
20
31
  default:
21
32
  return assertNever(duration);
22
33
  }
23
34
  }
24
- function exclusiveEndDateOf(duration, inclusiveEndDate) {
35
+ function exclusiveEndDateOf(duration, inclusiveEndDate, customDateRange) {
36
+ if (duration === Duration.CUSTOM && customDateRange) {
37
+ return DateTime.fromISO(customDateRange.end).plus({ days: 1 }).toFormat(DEFAULT_DATE_FORMAT);
38
+ }
25
39
  switch (duration) {
26
40
  case Duration.P7D:
27
41
  case Duration.P30D:
@@ -29,14 +43,36 @@ function exclusiveEndDateOf(duration, inclusiveEndDate) {
29
43
  return DateTime.fromISO(inclusiveEndDate).plus({ days: 1 }).toFormat(DEFAULT_DATE_FORMAT);
30
44
  case Duration.P3M:
31
45
  return DateTime.fromISO(quarterEndDate(inclusiveEndDate)).plus({ days: 1 }).toFormat(DEFAULT_DATE_FORMAT);
46
+ case Duration.CUSTOM:
47
+ throw new Error("CUSTOM duration requires customDateRange parameter");
32
48
  default:
33
49
  return assertNever(duration);
34
50
  }
35
51
  }
36
- function inclusiveEndDateOf(duration, inclusiveEndDate) {
37
- return DateTime.fromISO(exclusiveEndDateOf(duration, inclusiveEndDate)).minus({ days: 1 }).toFormat(DEFAULT_DATE_FORMAT);
52
+ function inclusiveEndDateOf(duration, inclusiveEndDate, customDateRange) {
53
+ return DateTime.fromISO(
54
+ exclusiveEndDateOf(duration, inclusiveEndDate, customDateRange)
55
+ ).minus({ days: 1 }).toFormat(DEFAULT_DATE_FORMAT);
38
56
  }
39
- function intervalsOf(duration, inclusiveEndDate, repeating = 2) {
57
+ function intervalsOf(duration, inclusiveEndDate, repeating = 2, customDateRange, comparisonMode = false) {
58
+ if (duration === Duration.CUSTOM && customDateRange) {
59
+ const startDate = DateTime.fromISO(customDateRange.start);
60
+ const endDate = DateTime.fromISO(customDateRange.end);
61
+ const totalDays = Math.round(endDate.diff(startDate, "days").days) + 1;
62
+ if (comparisonMode) {
63
+ const intervalDays = Math.floor(totalDays / 2);
64
+ return `R${repeating}/P${intervalDays}D/${exclusiveEndDateOf(
65
+ duration,
66
+ inclusiveEndDate,
67
+ customDateRange
68
+ )}`;
69
+ }
70
+ return `R${repeating}/P${totalDays}D/${exclusiveEndDateOf(
71
+ duration,
72
+ inclusiveEndDate,
73
+ customDateRange
74
+ )}`;
75
+ }
40
76
  return `R${repeating}/${duration}/${exclusiveEndDateOf(
41
77
  duration,
42
78
  inclusiveEndDate
@@ -1 +1 @@
1
- {"version":3,"file":"duration.esm.js","sources":["../../src/utils/duration.ts"],"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 { DateTime, Duration as LuxonDuration } from 'luxon';\nimport { Duration, DEFAULT_DATE_FORMAT } from '../types';\nimport { assertNever } from './assert';\n\nexport const DEFAULT_DURATION = Duration.P30D;\n\n/**\n * Derive the start date of a given period, assuming two repeating intervals.\n *\n * @param duration - see comment on Duration enum\n * @param inclusiveEndDate - from CostInsightsApi.getLastCompleteBillingDate\n */\nexport function inclusiveStartDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n): string {\n switch (duration) {\n case Duration.P7D:\n case Duration.P30D:\n case Duration.P90D:\n return DateTime.fromISO(inclusiveEndDate)\n .minus(\n LuxonDuration.fromISO(duration).plus(LuxonDuration.fromISO(duration)),\n )\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.P3M:\n return DateTime.fromISO(inclusiveEndDate)\n .startOf('quarter')\n .minus(\n LuxonDuration.fromISO(duration).plus(LuxonDuration.fromISO(duration)),\n )\n .toFormat(DEFAULT_DATE_FORMAT);\n default:\n return assertNever(duration);\n }\n}\n\nexport function exclusiveEndDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n): string {\n switch (duration) {\n case Duration.P7D:\n case Duration.P30D:\n case Duration.P90D:\n return DateTime.fromISO(inclusiveEndDate)\n .plus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.P3M:\n return DateTime.fromISO(quarterEndDate(inclusiveEndDate))\n .plus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n default:\n return assertNever(duration);\n }\n}\n\nexport function inclusiveEndDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n): string {\n return DateTime.fromISO(exclusiveEndDateOf(duration, inclusiveEndDate))\n .minus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n}\n\n// https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals\nexport function intervalsOf(\n duration: Duration,\n inclusiveEndDate: string,\n repeating: number = 2,\n) {\n return `R${repeating}/${duration}/${exclusiveEndDateOf(\n duration,\n inclusiveEndDate,\n )}`;\n}\n\nexport function quarterEndDate(inclusiveEndDate: string): string {\n const endDate = DateTime.fromISO(inclusiveEndDate);\n const endOfQuarter = endDate.endOf('quarter').toFormat(DEFAULT_DATE_FORMAT);\n if (endOfQuarter === inclusiveEndDate) {\n return endDate.toFormat(DEFAULT_DATE_FORMAT);\n }\n return endDate\n .startOf('quarter')\n .minus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n}\n"],"names":["LuxonDuration"],"mappings":";;;;;;AAoBO,MAAM,mBAAmB,QAAS,CAAA;AAQzB,SAAA,oBAAA,CACd,UACA,gBACQ,EAAA;AACR,EAAA,QAAQ,QAAU;AAAA,IAChB,KAAK,QAAS,CAAA,GAAA;AAAA,IACd,KAAK,QAAS,CAAA,IAAA;AAAA,IACd,KAAK,QAAS,CAAA,IAAA;AACZ,MAAO,OAAA,QAAA,CAAS,OAAQ,CAAA,gBAAgB,CACrC,CAAA,KAAA;AAAA,QACCA,UAAA,CAAc,QAAQ,QAAQ,CAAA,CAAE,KAAKA,UAAc,CAAA,OAAA,CAAQ,QAAQ,CAAC;AAAA,OACtE,CACC,SAAS,mBAAmB,CAAA;AAAA,IACjC,KAAK,QAAS,CAAA,GAAA;AACZ,MAAA,OAAO,SAAS,OAAQ,CAAA,gBAAgB,CACrC,CAAA,OAAA,CAAQ,SAAS,CACjB,CAAA,KAAA;AAAA,QACCA,UAAA,CAAc,QAAQ,QAAQ,CAAA,CAAE,KAAKA,UAAc,CAAA,OAAA,CAAQ,QAAQ,CAAC;AAAA,OACtE,CACC,SAAS,mBAAmB,CAAA;AAAA,IACjC;AACE,MAAA,OAAO,YAAY,QAAQ,CAAA;AAAA;AAEjC;AAEgB,SAAA,kBAAA,CACd,UACA,gBACQ,EAAA;AACR,EAAA,QAAQ,QAAU;AAAA,IAChB,KAAK,QAAS,CAAA,GAAA;AAAA,IACd,KAAK,QAAS,CAAA,IAAA;AAAA,IACd,KAAK,QAAS,CAAA,IAAA;AACZ,MAAO,OAAA,QAAA,CAAS,OAAQ,CAAA,gBAAgB,CACrC,CAAA,IAAA,CAAK,EAAE,IAAA,EAAM,CAAE,EAAC,CAChB,CAAA,QAAA,CAAS,mBAAmB,CAAA;AAAA,IACjC,KAAK,QAAS,CAAA,GAAA;AACZ,MAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,cAAe,CAAA,gBAAgB,CAAC,CAAA,CACrD,IAAK,CAAA,EAAE,IAAM,EAAA,CAAA,EAAG,CAAA,CAChB,SAAS,mBAAmB,CAAA;AAAA,IACjC;AACE,MAAA,OAAO,YAAY,QAAQ,CAAA;AAAA;AAEjC;AAEgB,SAAA,kBAAA,CACd,UACA,gBACQ,EAAA;AACR,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,kBAAmB,CAAA,QAAA,EAAU,gBAAgB,CAAC,CAAA,CACnE,KAAM,CAAA,EAAE,IAAM,EAAA,CAAA,EAAG,CAAA,CACjB,SAAS,mBAAmB,CAAA;AACjC;AAGO,SAAS,WACd,CAAA,QAAA,EACA,gBACA,EAAA,SAAA,GAAoB,CACpB,EAAA;AACA,EAAA,OAAO,CAAI,CAAA,EAAA,SAAS,CAAI,CAAA,EAAA,QAAQ,CAAI,CAAA,EAAA,kBAAA;AAAA,IAClC,QAAA;AAAA,IACA;AAAA,GACD,CAAA,CAAA;AACH;AAEO,SAAS,eAAe,gBAAkC,EAAA;AAC/D,EAAM,MAAA,OAAA,GAAU,QAAS,CAAA,OAAA,CAAQ,gBAAgB,CAAA;AACjD,EAAA,MAAM,eAAe,OAAQ,CAAA,KAAA,CAAM,SAAS,CAAA,CAAE,SAAS,mBAAmB,CAAA;AAC1E,EAAA,IAAI,iBAAiB,gBAAkB,EAAA;AACrC,IAAO,OAAA,OAAA,CAAQ,SAAS,mBAAmB,CAAA;AAAA;AAE7C,EAAO,OAAA,OAAA,CACJ,OAAQ,CAAA,SAAS,CACjB,CAAA,KAAA,CAAM,EAAE,IAAA,EAAM,CAAE,EAAC,CACjB,CAAA,QAAA,CAAS,mBAAmB,CAAA;AACjC;;;;"}
1
+ {"version":3,"file":"duration.esm.js","sources":["../../src/utils/duration.ts"],"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 { DateTime, Duration as LuxonDuration } from 'luxon';\nimport { Duration, DEFAULT_DATE_FORMAT } from '../types';\nimport { assertNever } from './assert';\n\nexport const DEFAULT_DURATION = Duration.P30D;\n\n/**\n * Derive the start date of a given period, assuming two repeating intervals.\n *\n * @param duration - see comment on Duration enum\n * @param inclusiveEndDate - from CostInsightsApi.getLastCompleteBillingDate\n * @param customDateRange - optional custom date range for Duration.CUSTOM\n * @param comparisonMode - if true, creates comparison period for custom ranges (default: false)\n */\nexport function inclusiveStartDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n customDateRange?: { start: string; end: string },\n comparisonMode: boolean = false,\n): string {\n if (duration === Duration.CUSTOM && customDateRange) {\n if (comparisonMode) {\n // For comparison mode, calculate a comparison period of equal length before the selected range\n const startDate = DateTime.fromISO(customDateRange.start);\n const endDate = DateTime.fromISO(customDateRange.end);\n // Include both start and end dates (add 1 day for inclusive calculation)\n const daysDiff = Math.round(endDate.diff(startDate, 'days').days) + 1;\n // Return start of comparison period (twice the range length before the end)\n return startDate.minus({ days: daysDiff }).toFormat(DEFAULT_DATE_FORMAT);\n }\n // For non-comparison mode, return the start date as-is\n return customDateRange.start;\n }\n\n switch (duration) {\n case Duration.P7D:\n case Duration.P30D:\n case Duration.P90D:\n return DateTime.fromISO(inclusiveEndDate)\n .minus(\n LuxonDuration.fromISO(duration).plus(LuxonDuration.fromISO(duration)),\n )\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.P3M:\n return DateTime.fromISO(inclusiveEndDate)\n .startOf('quarter')\n .minus(\n LuxonDuration.fromISO(duration).plus(LuxonDuration.fromISO(duration)),\n )\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.CUSTOM:\n throw new Error('CUSTOM duration requires customDateRange parameter');\n default:\n return assertNever(duration);\n }\n}\n\nexport function exclusiveEndDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n customDateRange?: { start: string; end: string },\n): string {\n if (duration === Duration.CUSTOM && customDateRange) {\n // For custom ranges, add 1 day to the end date for exclusive end\n return DateTime.fromISO(customDateRange.end)\n .plus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n }\n\n switch (duration) {\n case Duration.P7D:\n case Duration.P30D:\n case Duration.P90D:\n return DateTime.fromISO(inclusiveEndDate)\n .plus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.P3M:\n return DateTime.fromISO(quarterEndDate(inclusiveEndDate))\n .plus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n case Duration.CUSTOM:\n throw new Error('CUSTOM duration requires customDateRange parameter');\n default:\n return assertNever(duration);\n }\n}\n\nexport function inclusiveEndDateOf(\n duration: Duration,\n inclusiveEndDate: string,\n customDateRange?: { start: string; end: string },\n): string {\n return DateTime.fromISO(\n exclusiveEndDateOf(duration, inclusiveEndDate, customDateRange),\n )\n .minus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n}\n\n// https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals\nexport function intervalsOf(\n duration: Duration,\n inclusiveEndDate: string,\n repeating: number = 2,\n customDateRange?: { start: string; end: string },\n comparisonMode: boolean = false,\n) {\n if (duration === Duration.CUSTOM && customDateRange) {\n const startDate = DateTime.fromISO(customDateRange.start);\n const endDate = DateTime.fromISO(customDateRange.end);\n // Add 1 to include both start and end dates\n const totalDays = Math.round(endDate.diff(startDate, 'days').days) + 1;\n\n if (comparisonMode) {\n // For comparison mode, split the range in half to create two comparison periods\n const intervalDays = Math.floor(totalDays / 2);\n return `R${repeating}/P${intervalDays}D/${exclusiveEndDateOf(\n duration,\n inclusiveEndDate,\n customDateRange,\n )}`;\n }\n\n // For non-comparison mode, use the full date range as a single interval\n return `R${repeating}/P${totalDays}D/${exclusiveEndDateOf(\n duration,\n inclusiveEndDate,\n customDateRange,\n )}`;\n }\n\n return `R${repeating}/${duration}/${exclusiveEndDateOf(\n duration,\n inclusiveEndDate,\n )}`;\n}\n\nexport function quarterEndDate(inclusiveEndDate: string): string {\n const endDate = DateTime.fromISO(inclusiveEndDate);\n const endOfQuarter = endDate.endOf('quarter').toFormat(DEFAULT_DATE_FORMAT);\n if (endOfQuarter === inclusiveEndDate) {\n return endDate.toFormat(DEFAULT_DATE_FORMAT);\n }\n return endDate\n .startOf('quarter')\n .minus({ days: 1 })\n .toFormat(DEFAULT_DATE_FORMAT);\n}\n"],"names":["LuxonDuration"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"filters.esm.js","sources":["../../src/utils/filters.ts"],"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 { Duration, PageFilters } from '../types';\nimport { Group } from '@backstage-community/plugin-cost-insights-common';\n\nexport function getDefaultPageFilters(groups: Group[]): PageFilters {\n return {\n group: groups.length ? groups[0].id : null,\n project: null,\n duration: Duration.P90D,\n metric: null,\n };\n}\n"],"names":[],"mappings":";;;AAmBO,SAAS,sBAAsB,MAA8B,EAAA;AAClE,EAAO,OAAA;AAAA,IACL,OAAO,MAAO,CAAA,MAAA,GAAS,MAAO,CAAA,CAAC,EAAE,EAAK,GAAA,IAAA;AAAA,IACtC,OAAS,EAAA,IAAA;AAAA,IACT,UAAU,QAAS,CAAA,IAAA;AAAA,IACnB,MAAQ,EAAA;AAAA,GACV;AACF;;;;"}
1
+ {"version":3,"file":"filters.esm.js","sources":["../../src/utils/filters.ts"],"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 { Duration, PageFilters } from '../types';\nimport { Group } from '@backstage-community/plugin-cost-insights-common';\n\nexport function getDefaultPageFilters(groups: Group[]): PageFilters {\n return {\n group: groups.length ? groups[0].id : null,\n project: null,\n duration: Duration.P90D,\n metric: null,\n };\n}\n"],"names":[],"mappings":";;;AAmBO,SAAS,sBAAsB,MAAA,EAA8B;AAClE,EAAA,OAAO;AAAA,IACL,OAAO,MAAA,CAAO,MAAA,GAAS,MAAA,CAAO,CAAC,EAAE,EAAA,GAAK,IAAA;AAAA,IACtC,OAAA,EAAS,IAAA;AAAA,IACT,UAAU,QAAA,CAAS,IAAA;AAAA,IACnB,MAAA,EAAQ;AAAA,GACV;AACF;;;;"}
@@ -79,21 +79,42 @@ function formatLastTwoLookaheadQuarters(inclusiveEndDate) {
79
79
  ).toFormat("'Q'q yyyy");
80
80
  return `${start} vs ${end}`;
81
81
  }
82
- const formatRelativePeriod = (duration, date, isEndDate) => {
82
+ const formatRelativePeriod = (duration, date, isEndDate, customDays) => {
83
+ if (duration === Duration.CUSTOM && customDays !== void 0) {
84
+ return isEndDate ? `Last ${customDays} Days` : `First ${customDays} Days`;
85
+ }
83
86
  const periodStart = isEndDate ? inclusiveStartDateOf(duration, date) : date;
84
87
  const periodEnd = isEndDate ? date : inclusiveEndDateOf(duration, date);
85
- const days = Duration$1.fromISO(duration).days;
88
+ const days = customDays ?? Duration$1.fromISO(duration).days;
86
89
  if (![periodStart, periodEnd].includes(date)) {
87
90
  throw new Error(`Invalid relative date ${date} for duration ${duration}`);
88
91
  }
89
92
  return date === periodStart ? `First ${days} Days` : `Last ${days} Days`;
90
93
  };
91
- function formatPeriod(duration, date, isEndDate) {
94
+ function formatPeriod(duration, date, isEndDate, customDateRange, comparisonMode = false) {
92
95
  switch (duration) {
93
96
  case Duration.P3M:
94
97
  return quarterOf(
95
- isEndDate ? inclusiveEndDateOf(duration, date) : inclusiveStartDateOf(duration, date)
98
+ isEndDate ? inclusiveEndDateOf(duration, date, customDateRange) : inclusiveStartDateOf(duration, date, customDateRange)
96
99
  );
100
+ case Duration.CUSTOM:
101
+ if (customDateRange && !comparisonMode) {
102
+ const startDate = DateTime.fromISO(customDateRange.start);
103
+ const endDate = DateTime.fromISO(customDateRange.end);
104
+ return `${startDate.toFormat("MMM dd")} - ${endDate.toFormat(
105
+ "MMM dd, yyyy"
106
+ )}`;
107
+ }
108
+ if (customDateRange && comparisonMode) {
109
+ const startDate = DateTime.fromISO(customDateRange.start);
110
+ const endDate = DateTime.fromISO(customDateRange.end);
111
+ const totalDays = Math.round(endDate.diff(startDate, "days").days) + 1;
112
+ const firstPeriodDays = Math.floor(totalDays / 2);
113
+ const lastPeriodDays = totalDays - firstPeriodDays;
114
+ const periodDays = isEndDate ? lastPeriodDays : firstPeriodDays;
115
+ return formatRelativePeriod(duration, date, isEndDate, periodDays);
116
+ }
117
+ return DateTime.fromISO(date).toFormat("MMM dd, yyyy");
97
118
  default:
98
119
  return formatRelativePeriod(duration, date, isEndDate);
99
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"formatters.esm.js","sources":["../../src/utils/formatters.ts"],"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 { DateTime, Duration as LuxonDuration } from 'luxon';\nimport pluralize from 'pluralize';\nimport { Duration } from '../types';\nimport { ChangeStatistic } from '@backstage-community/plugin-cost-insights-common';\nimport { inclusiveEndDateOf, inclusiveStartDateOf } from './duration';\nimport { notEmpty } from './assert';\n\nexport type Period = {\n periodStart: string;\n periodEnd: string;\n};\n\nexport const currencyFormatter = (currency: Intl.NumberFormat) => {\n const options = currency.resolvedOptions();\n\n return new Intl.NumberFormat(options.locale, {\n style: 'currency',\n currency: options.currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n });\n};\n\nexport const lengthyCurrencyFormatter = (currency: Intl.NumberFormat) => {\n const options = currency.resolvedOptions();\n\n return new Intl.NumberFormat(options.locale, {\n style: 'currency',\n currency: options.currency,\n minimumFractionDigits: 0,\n minimumSignificantDigits: 2,\n maximumSignificantDigits: 2,\n });\n};\n\nexport const numberFormatter = new Intl.NumberFormat('en-US', {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n});\n\nexport const monthFormatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'UTC',\n month: 'long',\n year: 'numeric',\n});\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'UTC',\n day: 'numeric',\n month: 'short',\n});\n\nexport const monthOf = (date: string): string => {\n return monthFormatter.format(Date.parse(date));\n};\n\nexport const quarterOf = (date: string): string => {\n // Supports formatting yyyy-LL-dd and yyyy-'Q'q returned in alerts\n const d = DateTime.fromISO(date).isValid\n ? DateTime.fromISO(date)\n : DateTime.fromFormat(date, \"yyyy-'Q'q\");\n return d.toFormat(\"'Q'q yyyy\");\n};\n\nexport function formatCurrency(amount: number, currency?: string): string {\n const n = Math.round(amount);\n const numString = numberFormatter.format(n);\n\n return currency ? `${numString} ${pluralize(currency, n)}` : numString;\n}\n\nexport function formatChange(\n change: ChangeStatistic,\n options?: { absolute: boolean },\n): string {\n if (notEmpty(change.ratio)) {\n return formatPercent(\n options?.absolute ? Math.abs(change.ratio) : change.ratio,\n );\n }\n if (options?.absolute) {\n return '∞';\n }\n return change.amount >= 0 ? '∞' : '-∞';\n}\n\nexport function formatPercent(n: number): string {\n // Number.toFixed shows scientific notation for extreme numbers\n if (isNaN(n) || Math.abs(n) < 0.01) {\n return '0%';\n }\n\n if (Math.abs(n) > 10) {\n return `>${n < 0 ? '-' : ''}1000%`;\n }\n\n return `${(n * 100).toFixed(0)}%`;\n}\n\nexport function formatLastTwoLookaheadQuarters(inclusiveEndDate: string) {\n const start = DateTime.fromISO(\n inclusiveStartDateOf(Duration.P3M, inclusiveEndDate),\n ).toFormat(\"'Q'q yyyy\");\n const end = DateTime.fromISO(\n inclusiveEndDateOf(Duration.P3M, inclusiveEndDate),\n ).toFormat(\"'Q'q yyyy\");\n return `${start} vs ${end}`;\n}\n\nconst formatRelativePeriod = (\n duration: Duration,\n date: string,\n isEndDate: boolean,\n): string => {\n const periodStart = isEndDate ? inclusiveStartDateOf(duration, date) : date;\n const periodEnd = isEndDate ? date : inclusiveEndDateOf(duration, date);\n const days = LuxonDuration.fromISO(duration).days;\n if (![periodStart, periodEnd].includes(date)) {\n throw new Error(`Invalid relative date ${date} for duration ${duration}`);\n }\n return date === periodStart ? `First ${days} Days` : `Last ${days} Days`;\n};\n\nexport function formatPeriod(\n duration: Duration,\n date: string,\n isEndDate: boolean,\n) {\n switch (duration) {\n case Duration.P3M:\n return quarterOf(\n isEndDate\n ? inclusiveEndDateOf(duration, date)\n : inclusiveStartDateOf(duration, date),\n );\n default:\n return formatRelativePeriod(duration, date, isEndDate);\n }\n}\n"],"names":["LuxonDuration"],"mappings":"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"}
1
+ {"version":3,"file":"formatters.esm.js","sources":["../../src/utils/formatters.ts"],"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 { DateTime, Duration as LuxonDuration } from 'luxon';\nimport pluralize from 'pluralize';\nimport { Duration } from '../types';\nimport { ChangeStatistic } from '@backstage-community/plugin-cost-insights-common';\nimport { inclusiveEndDateOf, inclusiveStartDateOf } from './duration';\nimport { notEmpty } from './assert';\n\nexport type Period = {\n periodStart: string;\n periodEnd: string;\n};\n\nexport const currencyFormatter = (currency: Intl.NumberFormat) => {\n const options = currency.resolvedOptions();\n\n return new Intl.NumberFormat(options.locale, {\n style: 'currency',\n currency: options.currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n });\n};\n\nexport const lengthyCurrencyFormatter = (currency: Intl.NumberFormat) => {\n const options = currency.resolvedOptions();\n\n return new Intl.NumberFormat(options.locale, {\n style: 'currency',\n currency: options.currency,\n minimumFractionDigits: 0,\n minimumSignificantDigits: 2,\n maximumSignificantDigits: 2,\n });\n};\n\nexport const numberFormatter = new Intl.NumberFormat('en-US', {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n});\n\nexport const monthFormatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'UTC',\n month: 'long',\n year: 'numeric',\n});\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'UTC',\n day: 'numeric',\n month: 'short',\n});\n\nexport const monthOf = (date: string): string => {\n return monthFormatter.format(Date.parse(date));\n};\n\nexport const quarterOf = (date: string): string => {\n // Supports formatting yyyy-LL-dd and yyyy-'Q'q returned in alerts\n const d = DateTime.fromISO(date).isValid\n ? DateTime.fromISO(date)\n : DateTime.fromFormat(date, \"yyyy-'Q'q\");\n return d.toFormat(\"'Q'q yyyy\");\n};\n\nexport function formatCurrency(amount: number, currency?: string): string {\n const n = Math.round(amount);\n const numString = numberFormatter.format(n);\n\n return currency ? `${numString} ${pluralize(currency, n)}` : numString;\n}\n\nexport function formatChange(\n change: ChangeStatistic,\n options?: { absolute: boolean },\n): string {\n if (notEmpty(change.ratio)) {\n return formatPercent(\n options?.absolute ? Math.abs(change.ratio) : change.ratio,\n );\n }\n if (options?.absolute) {\n return '∞';\n }\n return change.amount >= 0 ? '∞' : '-∞';\n}\n\nexport function formatPercent(n: number): string {\n // Number.toFixed shows scientific notation for extreme numbers\n if (isNaN(n) || Math.abs(n) < 0.01) {\n return '0%';\n }\n\n if (Math.abs(n) > 10) {\n return `>${n < 0 ? '-' : ''}1000%`;\n }\n\n return `${(n * 100).toFixed(0)}%`;\n}\n\nexport function formatLastTwoLookaheadQuarters(inclusiveEndDate: string) {\n const start = DateTime.fromISO(\n inclusiveStartDateOf(Duration.P3M, inclusiveEndDate),\n ).toFormat(\"'Q'q yyyy\");\n const end = DateTime.fromISO(\n inclusiveEndDateOf(Duration.P3M, inclusiveEndDate),\n ).toFormat(\"'Q'q yyyy\");\n return `${start} vs ${end}`;\n}\n\nconst formatRelativePeriod = (\n duration: Duration,\n date: string,\n isEndDate: boolean,\n customDays?: number,\n): string => {\n // For custom durations, we already know the days, so skip validation\n if (duration === Duration.CUSTOM && customDays !== undefined) {\n return isEndDate ? `Last ${customDays} Days` : `First ${customDays} Days`;\n }\n\n const periodStart = isEndDate ? inclusiveStartDateOf(duration, date) : date;\n const periodEnd = isEndDate ? date : inclusiveEndDateOf(duration, date);\n const days = customDays ?? LuxonDuration.fromISO(duration).days;\n if (![periodStart, periodEnd].includes(date)) {\n throw new Error(`Invalid relative date ${date} for duration ${duration}`);\n }\n return date === periodStart ? `First ${days} Days` : `Last ${days} Days`;\n};\n\nexport function formatPeriod(\n duration: Duration,\n date: string,\n isEndDate: boolean,\n customDateRange?: { start: string; end: string },\n comparisonMode: boolean = false,\n) {\n switch (duration) {\n case Duration.P3M:\n return quarterOf(\n isEndDate\n ? inclusiveEndDateOf(duration, date, customDateRange)\n : inclusiveStartDateOf(duration, date, customDateRange),\n );\n case Duration.CUSTOM:\n // For custom durations in non-comparison mode, display the full date range\n if (customDateRange && !comparisonMode) {\n const startDate = DateTime.fromISO(customDateRange.start);\n const endDate = DateTime.fromISO(customDateRange.end);\n return `${startDate.toFormat('MMM dd')} - ${endDate.toFormat(\n 'MMM dd, yyyy',\n )}`;\n }\n // For comparison mode, display as \"First X Days\" vs \"Last X Days\"\n if (customDateRange && comparisonMode) {\n const startDate = DateTime.fromISO(customDateRange.start);\n const endDate = DateTime.fromISO(customDateRange.end);\n const totalDays = Math.round(endDate.diff(startDate, 'days').days) + 1;\n const firstPeriodDays = Math.floor(totalDays / 2);\n const lastPeriodDays = totalDays - firstPeriodDays;\n const periodDays = isEndDate ? lastPeriodDays : firstPeriodDays;\n return formatRelativePeriod(duration, date, isEndDate, periodDays);\n }\n return DateTime.fromISO(date).toFormat('MMM dd, yyyy');\n default:\n return formatRelativePeriod(duration, date, isEndDate);\n }\n}\n"],"names":["LuxonDuration"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"grammar.esm.js","sources":["../../src/utils/grammar.ts"],"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\nconst vowels = {\n a: 'A',\n e: 'E',\n i: 'I',\n o: 'O',\n u: 'U',\n};\n\nexport const indefiniteArticleOf = (\n articles: [string, string],\n word: string,\n) => {\n const firstChar = word.charAt(0).toLocaleLowerCase('en-US');\n return firstChar in vowels\n ? `${articles[1]} ${word}`\n : `${articles[0]} ${word}`;\n};\n"],"names":[],"mappings":"AAgBA,MAAM,MAAS,GAAA;AAAA,EACb,CAAG,EAAA,GAAA;AAAA,EACH,CAAG,EAAA,GAAA;AAAA,EACH,CAAG,EAAA,GAAA;AAAA,EACH,CAAG,EAAA,GAAA;AAAA,EACH,CAAG,EAAA;AACL,CAAA;AAEa,MAAA,mBAAA,GAAsB,CACjC,QAAA,EACA,IACG,KAAA;AACH,EAAA,MAAM,YAAY,IAAK,CAAA,MAAA,CAAO,CAAC,CAAA,CAAE,kBAAkB,OAAO,CAAA;AAC1D,EAAA,OAAO,SAAa,IAAA,MAAA,GAChB,CAAG,EAAA,QAAA,CAAS,CAAC,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GACtB,CAAG,EAAA,QAAA,CAAS,CAAC,CAAC,IAAI,IAAI,CAAA,CAAA;AAC5B;;;;"}
1
+ {"version":3,"file":"grammar.esm.js","sources":["../../src/utils/grammar.ts"],"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\nconst vowels = {\n a: 'A',\n e: 'E',\n i: 'I',\n o: 'O',\n u: 'U',\n};\n\nexport const indefiniteArticleOf = (\n articles: [string, string],\n word: string,\n) => {\n const firstChar = word.charAt(0).toLocaleLowerCase('en-US');\n return firstChar in vowels\n ? `${articles[1]} ${word}`\n : `${articles[0]} ${word}`;\n};\n"],"names":[],"mappings":"AAgBA,MAAM,MAAA,GAAS;AAAA,EACb,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG,GAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAEO,MAAM,mBAAA,GAAsB,CACjC,QAAA,EACA,IAAA,KACG;AACH,EAAA,MAAM,YAAY,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,CAAE,kBAAkB,OAAO,CAAA;AAC1D,EAAA,OAAO,SAAA,IAAa,MAAA,GAChB,CAAA,EAAG,QAAA,CAAS,CAAC,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GACtB,CAAA,EAAG,QAAA,CAAS,CAAC,CAAC,IAAI,IAAI,CAAA,CAAA;AAC5B;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { DataKey } from '../types/Alert.esm.js';
2
- import { lengthyCurrencyFormatter, currencyFormatter, dateFormatter } from './formatters.esm.js';
2
+ import { dateFormatter, lengthyCurrencyFormatter, currencyFormatter } from './formatters.esm.js';
3
3
 
4
4
  const formatGraphValue = (baseCurrency) => (value, _index, format) => {
5
5
  if (format === "number") {
@@ -1 +1 @@
1
- {"version":3,"file":"graphs.esm.js","sources":["../../src/utils/graphs.ts"],"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 { TooltipProps } from 'recharts';\nimport { Payload } from 'recharts/types/component/DefaultTooltipContent';\nimport { AlertCost, DataKey, ResourceData } from '../types';\nimport { Entity } from '@backstage-community/plugin-cost-insights-common';\nimport {\n currencyFormatter,\n dateFormatter,\n lengthyCurrencyFormatter,\n} from './formatters';\n\nexport const formatGraphValue =\n (baseCurrency: Intl.NumberFormat) =>\n (value: number, _index: number, format?: string) => {\n if (format === 'number') {\n return value.toLocaleString();\n }\n\n if (value < 1) {\n return lengthyCurrencyFormatter(baseCurrency).format(value);\n }\n\n return currencyFormatter(baseCurrency).format(value);\n };\n\nexport const overviewGraphTickFormatter = (millis: string | number) =>\n typeof millis === 'number' ? dateFormatter.format(millis) : millis;\n\nexport const tooltipItemOf =\n (baseCurrency: Intl.NumberFormat) => (payload: Payload<string, string>) => {\n const value =\n payload.value && !isNaN(Number(payload.value))\n ? baseCurrency.format(Number(payload.value))\n : payload.value;\n const fill = payload.color as string;\n\n switch (payload.dataKey) {\n case DataKey.Current:\n case DataKey.Previous:\n return {\n label: payload.name,\n value: value,\n fill: fill,\n };\n default:\n return null;\n }\n };\n\nexport const resourceOf = (entity: Entity | AlertCost): ResourceData => ({\n name: entity.id,\n previous: entity.aggregation[0],\n current: entity.aggregation[1],\n});\n\nexport const titleOf = (label?: string | number) => {\n return label ? String(label) : 'Unlabeled';\n};\n\nexport const isInvalid = ({ label, payload }: TooltipProps<string, string>) => {\n // null labels are empty strings, which are valid\n return label === undefined || !payload || !payload.length;\n};\n\nexport const isLabeled = (data?: Record<'activeLabel', string | undefined>) => {\n return data?.activeLabel && data?.activeLabel !== '';\n};\n\nexport const isUnlabeled = (\n data?: Record<'activeLabel', string | undefined>,\n) => {\n return data?.activeLabel === '';\n};\n"],"names":[],"mappings":";;;AA0BO,MAAM,mBACX,CAAC,YAAA,KACD,CAAC,KAAA,EAAe,QAAgB,MAAoB,KAAA;AAClD,EAAA,IAAI,WAAW,QAAU,EAAA;AACvB,IAAA,OAAO,MAAM,cAAe,EAAA;AAAA;AAG9B,EAAA,IAAI,QAAQ,CAAG,EAAA;AACb,IAAA,OAAO,wBAAyB,CAAA,YAAY,CAAE,CAAA,MAAA,CAAO,KAAK,CAAA;AAAA;AAG5D,EAAA,OAAO,iBAAkB,CAAA,YAAY,CAAE,CAAA,MAAA,CAAO,KAAK,CAAA;AACrD;AAEW,MAAA,0BAAA,GAA6B,CAAC,MACzC,KAAA,OAAO,WAAW,QAAW,GAAA,aAAA,CAAc,MAAO,CAAA,MAAM,CAAI,GAAA;AAEvD,MAAM,aACX,GAAA,CAAC,YAAoC,KAAA,CAAC,OAAqC,KAAA;AACzE,EAAA,MAAM,QACJ,OAAQ,CAAA,KAAA,IAAS,CAAC,KAAA,CAAM,OAAO,OAAQ,CAAA,KAAK,CAAC,CAAA,GACzC,aAAa,MAAO,CAAA,MAAA,CAAO,QAAQ,KAAK,CAAC,IACzC,OAAQ,CAAA,KAAA;AACd,EAAA,MAAM,OAAO,OAAQ,CAAA,KAAA;AAErB,EAAA,QAAQ,QAAQ,OAAS;AAAA,IACvB,KAAK,OAAQ,CAAA,OAAA;AAAA,IACb,KAAK,OAAQ,CAAA,QAAA;AACX,MAAO,OAAA;AAAA,QACL,OAAO,OAAQ,CAAA,IAAA;AAAA,QACf,KAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACE,MAAO,OAAA,IAAA;AAAA;AAEb;AAEW,MAAA,UAAA,GAAa,CAAC,MAA8C,MAAA;AAAA,EACvE,MAAM,MAAO,CAAA,EAAA;AAAA,EACb,QAAA,EAAU,MAAO,CAAA,WAAA,CAAY,CAAC,CAAA;AAAA,EAC9B,OAAA,EAAS,MAAO,CAAA,WAAA,CAAY,CAAC;AAC/B,CAAA;AAEa,MAAA,OAAA,GAAU,CAAC,KAA4B,KAAA;AAClD,EAAO,OAAA,KAAA,GAAQ,MAAO,CAAA,KAAK,CAAI,GAAA,WAAA;AACjC;AAEO,MAAM,SAAY,GAAA,CAAC,EAAE,KAAA,EAAO,SAA4C,KAAA;AAE7E,EAAA,OAAO,KAAU,KAAA,KAAA,CAAA,IAAa,CAAC,OAAA,IAAW,CAAC,OAAQ,CAAA,MAAA;AACrD;AAEa,MAAA,SAAA,GAAY,CAAC,IAAqD,KAAA;AAC7E,EAAO,OAAA,IAAA,EAAM,WAAe,IAAA,IAAA,EAAM,WAAgB,KAAA,EAAA;AACpD;AAEa,MAAA,WAAA,GAAc,CACzB,IACG,KAAA;AACH,EAAA,OAAO,MAAM,WAAgB,KAAA,EAAA;AAC/B;;;;"}
1
+ {"version":3,"file":"graphs.esm.js","sources":["../../src/utils/graphs.ts"],"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 { TooltipProps } from 'recharts';\nimport { Payload } from 'recharts/types/component/DefaultTooltipContent';\nimport { AlertCost, DataKey, ResourceData } from '../types';\nimport { Entity } from '@backstage-community/plugin-cost-insights-common';\nimport {\n currencyFormatter,\n dateFormatter,\n lengthyCurrencyFormatter,\n} from './formatters';\n\nexport const formatGraphValue =\n (baseCurrency: Intl.NumberFormat) =>\n (value: number, _index: number, format?: string) => {\n if (format === 'number') {\n return value.toLocaleString();\n }\n\n if (value < 1) {\n return lengthyCurrencyFormatter(baseCurrency).format(value);\n }\n\n return currencyFormatter(baseCurrency).format(value);\n };\n\nexport const overviewGraphTickFormatter = (millis: string | number) =>\n typeof millis === 'number' ? dateFormatter.format(millis) : millis;\n\nexport const tooltipItemOf =\n (baseCurrency: Intl.NumberFormat) => (payload: Payload<string, string>) => {\n const value =\n payload.value && !isNaN(Number(payload.value))\n ? baseCurrency.format(Number(payload.value))\n : payload.value;\n const fill = payload.color as string;\n\n switch (payload.dataKey) {\n case DataKey.Current:\n case DataKey.Previous:\n return {\n label: payload.name,\n value: value,\n fill: fill,\n };\n default:\n return null;\n }\n };\n\nexport const resourceOf = (entity: Entity | AlertCost): ResourceData => ({\n name: entity.id,\n previous: entity.aggregation[0],\n current: entity.aggregation[1],\n});\n\nexport const titleOf = (label?: string | number) => {\n return label ? String(label) : 'Unlabeled';\n};\n\nexport const isInvalid = ({ label, payload }: TooltipProps<string, string>) => {\n // null labels are empty strings, which are valid\n return label === undefined || !payload || !payload.length;\n};\n\nexport const isLabeled = (data?: Record<'activeLabel', string | undefined>) => {\n return data?.activeLabel && data?.activeLabel !== '';\n};\n\nexport const isUnlabeled = (\n data?: Record<'activeLabel', string | undefined>,\n) => {\n return data?.activeLabel === '';\n};\n"],"names":[],"mappings":";;;AA0BO,MAAM,mBACX,CAAC,YAAA,KACD,CAAC,KAAA,EAAe,QAAgB,MAAA,KAAoB;AAClD,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,OAAO,MAAM,cAAA,EAAe;AAAA,EAC9B;AAEA,EAAA,IAAI,QAAQ,CAAA,EAAG;AACb,IAAA,OAAO,wBAAA,CAAyB,YAAY,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAAA,EAC5D;AAEA,EAAA,OAAO,iBAAA,CAAkB,YAAY,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AACrD;AAEK,MAAM,0BAAA,GAA6B,CAAC,MAAA,KACzC,OAAO,WAAW,QAAA,GAAW,aAAA,CAAc,MAAA,CAAO,MAAM,CAAA,GAAI;AAEvD,MAAM,aAAA,GACX,CAAC,YAAA,KAAoC,CAAC,OAAA,KAAqC;AACzE,EAAA,MAAM,QACJ,OAAA,CAAQ,KAAA,IAAS,CAAC,KAAA,CAAM,OAAO,OAAA,CAAQ,KAAK,CAAC,CAAA,GACzC,aAAa,MAAA,CAAO,MAAA,CAAO,QAAQ,KAAK,CAAC,IACzC,OAAA,CAAQ,KAAA;AACd,EAAA,MAAM,OAAO,OAAA,CAAQ,KAAA;AAErB,EAAA,QAAQ,QAAQ,OAAA;AAAS,IACvB,KAAK,OAAA,CAAQ,OAAA;AAAA,IACb,KAAK,OAAA,CAAQ,QAAA;AACX,MAAA,OAAO;AAAA,QACL,OAAO,OAAA,CAAQ,IAAA;AAAA,QACf,KAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAEK,MAAM,UAAA,GAAa,CAAC,MAAA,MAA8C;AAAA,EACvE,MAAM,MAAA,CAAO,EAAA;AAAA,EACb,QAAA,EAAU,MAAA,CAAO,WAAA,CAAY,CAAC,CAAA;AAAA,EAC9B,OAAA,EAAS,MAAA,CAAO,WAAA,CAAY,CAAC;AAC/B,CAAA;AAEO,MAAM,OAAA,GAAU,CAAC,KAAA,KAA4B;AAClD,EAAA,OAAO,KAAA,GAAQ,MAAA,CAAO,KAAK,CAAA,GAAI,WAAA;AACjC;AAEO,MAAM,SAAA,GAAY,CAAC,EAAE,KAAA,EAAO,SAAQ,KAAoC;AAE7E,EAAA,OAAO,KAAA,KAAU,MAAA,IAAa,CAAC,OAAA,IAAW,CAAC,OAAA,CAAQ,MAAA;AACrD;AAEO,MAAM,SAAA,GAAY,CAAC,IAAA,KAAqD;AAC7E,EAAA,OAAO,IAAA,EAAM,WAAA,IAAe,IAAA,EAAM,WAAA,KAAgB,EAAA;AACpD;AAEO,MAAM,WAAA,GAAc,CACzB,IAAA,KACG;AACH,EAAA,OAAO,MAAM,WAAA,KAAgB,EAAA;AAC/B;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"history.esm.js","sources":["../../src/utils/history.ts"],"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 qs from 'qs';\nimport * as yup from 'yup';\nimport { Duration, PageFilters } from '../types';\nimport { Group } from '@backstage-community/plugin-cost-insights-common';\nimport { getDefaultPageFilters } from '../utils/filters';\nimport { ConfigContextProps } from '../hooks/useConfig';\n\nconst schema = yup\n .object()\n .shape({\n group: yup.string().nullable(),\n project: yup.string().nullable(),\n })\n .required();\n\nexport const stringify = (queryParams: Partial<PageFilters>) =>\n qs.stringify(queryParams, { strictNullHandling: true });\n\nexport const parse = (queryString: string): Partial<PageFilters> =>\n qs.parse(queryString, { ignoreQueryPrefix: true, strictNullHandling: true });\n\nexport const validate = (queryString: string): Promise<PageFilters> => {\n return schema.validate(parse(queryString), {\n stripUnknown: true,\n strict: true,\n }) as Promise<PageFilters>;\n};\n\nexport const getInitialPageState = (\n groups: Group[],\n queryParams: Partial<PageFilters> = {},\n) => {\n return {\n ...getDefaultPageFilters(groups),\n ...(queryParams.project ? { project: queryParams.project } : {}),\n ...(queryParams.group ? { group: queryParams.group } : {}),\n };\n};\n\nexport const getInitialProductState = (config: ConfigContextProps) =>\n config.products.map(product => ({\n productType: product.kind,\n duration: Duration.P30D,\n }));\n"],"names":[],"mappings":";;;;;;AAuBA,MAAM,MAAS,GAAA,GAAA,CACZ,MAAO,EAAA,CACP,KAAM,CAAA;AAAA,EACL,KAAO,EAAA,GAAA,CAAI,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,EAC7B,OAAS,EAAA,GAAA,CAAI,MAAO,EAAA,CAAE,QAAS;AACjC,CAAC,EACA,QAAS,EAAA;AAEC,MAAA,SAAA,GAAY,CAAC,WACxB,KAAA,EAAA,CAAG,UAAU,WAAa,EAAA,EAAE,kBAAoB,EAAA,IAAA,EAAM;AAE3C,MAAA,KAAA,GAAQ,CAAC,WAAA,KACpB,EAAG,CAAA,KAAA,CAAM,WAAa,EAAA,EAAE,iBAAmB,EAAA,IAAA,EAAM,kBAAoB,EAAA,IAAA,EAAM;AAEhE,MAAA,QAAA,GAAW,CAAC,WAA8C,KAAA;AACrE,EAAA,OAAO,MAAO,CAAA,QAAA,CAAS,KAAM,CAAA,WAAW,CAAG,EAAA;AAAA,IACzC,YAAc,EAAA,IAAA;AAAA,IACd,MAAQ,EAAA;AAAA,GACT,CAAA;AACH;AAEO,MAAM,mBAAsB,GAAA,CACjC,MACA,EAAA,WAAA,GAAoC,EACjC,KAAA;AACH,EAAO,OAAA;AAAA,IACL,GAAG,sBAAsB,MAAM,CAAA;AAAA,IAC/B,GAAI,YAAY,OAAU,GAAA,EAAE,SAAS,WAAY,CAAA,OAAA,KAAY,EAAC;AAAA,IAC9D,GAAI,YAAY,KAAQ,GAAA,EAAE,OAAO,WAAY,CAAA,KAAA,KAAU;AAAC,GAC1D;AACF;AAEO,MAAM,yBAAyB,CAAC,MAAA,KACrC,MAAO,CAAA,QAAA,CAAS,IAAI,CAAY,OAAA,MAAA;AAAA,EAC9B,aAAa,OAAQ,CAAA,IAAA;AAAA,EACrB,UAAU,QAAS,CAAA;AACrB,CAAE,CAAA;;;;"}
1
+ {"version":3,"file":"history.esm.js","sources":["../../src/utils/history.ts"],"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 qs from 'qs';\nimport * as yup from 'yup';\nimport { Duration, PageFilters } from '../types';\nimport { Group } from '@backstage-community/plugin-cost-insights-common';\nimport { getDefaultPageFilters } from '../utils/filters';\nimport { ConfigContextProps } from '../hooks/useConfig';\n\nconst schema = yup\n .object()\n .shape({\n group: yup.string().nullable(),\n project: yup.string().nullable(),\n })\n .required();\n\nexport const stringify = (queryParams: Partial<PageFilters>) =>\n qs.stringify(queryParams, { strictNullHandling: true });\n\nexport const parse = (queryString: string): Partial<PageFilters> =>\n qs.parse(queryString, { ignoreQueryPrefix: true, strictNullHandling: true });\n\nexport const validate = (queryString: string): Promise<PageFilters> => {\n return schema.validate(parse(queryString), {\n stripUnknown: true,\n strict: true,\n }) as Promise<PageFilters>;\n};\n\nexport const getInitialPageState = (\n groups: Group[],\n queryParams: Partial<PageFilters> = {},\n) => {\n return {\n ...getDefaultPageFilters(groups),\n ...(queryParams.project ? { project: queryParams.project } : {}),\n ...(queryParams.group ? { group: queryParams.group } : {}),\n };\n};\n\nexport const getInitialProductState = (config: ConfigContextProps) =>\n config.products.map(product => ({\n productType: product.kind,\n duration: Duration.P30D,\n }));\n"],"names":[],"mappings":";;;;;;AAuBA,MAAM,MAAA,GAAS,GAAA,CACZ,MAAA,EAAO,CACP,KAAA,CAAM;AAAA,EACL,KAAA,EAAO,GAAA,CAAI,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC7B,OAAA,EAAS,GAAA,CAAI,MAAA,EAAO,CAAE,QAAA;AACxB,CAAC,EACA,QAAA,EAAS;AAEL,MAAM,SAAA,GAAY,CAAC,WAAA,KACxB,EAAA,CAAG,UAAU,WAAA,EAAa,EAAE,kBAAA,EAAoB,IAAA,EAAM;AAEjD,MAAM,KAAA,GAAQ,CAAC,WAAA,KACpB,EAAA,CAAG,KAAA,CAAM,WAAA,EAAa,EAAE,iBAAA,EAAmB,IAAA,EAAM,kBAAA,EAAoB,IAAA,EAAM;AAEtE,MAAM,QAAA,GAAW,CAAC,WAAA,KAA8C;AACrE,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,WAAW,CAAA,EAAG;AAAA,IACzC,YAAA,EAAc,IAAA;AAAA,IACd,MAAA,EAAQ;AAAA,GACT,CAAA;AACH;AAEO,MAAM,mBAAA,GAAsB,CACjC,MAAA,EACA,WAAA,GAAoC,EAAC,KAClC;AACH,EAAA,OAAO;AAAA,IACL,GAAG,sBAAsB,MAAM,CAAA;AAAA,IAC/B,GAAI,YAAY,OAAA,GAAU,EAAE,SAAS,WAAA,CAAY,OAAA,KAAY,EAAC;AAAA,IAC9D,GAAI,YAAY,KAAA,GAAQ,EAAE,OAAO,WAAA,CAAY,KAAA,KAAU;AAAC,GAC1D;AACF;AAEO,MAAM,yBAAyB,CAAC,MAAA,KACrC,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,OAAA,MAAY;AAAA,EAC9B,aAAa,OAAA,CAAQ,IAAA;AAAA,EACrB,UAAU,QAAA,CAAS;AACrB,CAAA,CAAE;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"loading.esm.js","sources":["../../src/utils/loading.ts"],"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 { Duration, Loading } from '../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { DEFAULT_DURATION } from './duration';\n\nexport type ProductState = {\n product: Product;\n entity: Maybe<Entity>;\n duration: Duration;\n};\n\nexport enum DefaultLoadingAction {\n UserGroups = 'user-groups',\n LastCompleteBillingDate = 'billing-date',\n CostInsightsInitial = 'cost-insights-initial',\n CostInsightsPage = 'cost-insights-page',\n CostInsightsProducts = 'cost-insights-products',\n CostInsightsAlerts = 'cost-insights-alerts',\n}\n\nexport const INITIAL_LOADING_ACTIONS = [\n DefaultLoadingAction.UserGroups,\n DefaultLoadingAction.CostInsightsInitial,\n DefaultLoadingAction.CostInsightsProducts,\n];\n\nexport const getDefaultState = (loadingActions: string[]): Loading => {\n return loadingActions.reduce(\n (defaultState, action) => ({ ...defaultState, [action]: true }),\n {},\n );\n};\n\nexport const getResetState = (loadingActions: string[]): Loading => {\n return loadingActions.reduce(\n (defaultState, action) => ({ ...defaultState, [action]: false }),\n {},\n );\n};\n\nexport const getResetStateWithoutInitial = (\n loadingActions: string[],\n): Loading => {\n return loadingActions.reduce((defaultState, action) => {\n const loadingActionState = (INITIAL_LOADING_ACTIONS as string[]).includes(\n action,\n )\n ? false\n : true;\n return { ...defaultState, [action]: loadingActionState };\n }, {});\n};\n\nexport const settledResponseOf = (\n responses: PromiseSettledResult<Entity | any>[],\n): Array<Maybe<Entity>> => {\n return responses.map(response =>\n response.status === 'fulfilled' ? response.value : null,\n );\n};\n\nexport const initialStatesOf = (\n products: Product[],\n responses: Array<Maybe<Entity>>,\n): ProductState[] => {\n return products.map((product, index) => ({\n entity: responses[index],\n product: product,\n duration: DEFAULT_DURATION,\n }));\n};\n"],"names":["DefaultLoadingAction"],"mappings":";;AA8BY,IAAA,oBAAA,qBAAAA,qBAAL,KAAA;AACL,EAAAA,sBAAA,YAAa,CAAA,GAAA,aAAA;AACb,EAAAA,sBAAA,yBAA0B,CAAA,GAAA,cAAA;AAC1B,EAAAA,sBAAA,qBAAsB,CAAA,GAAA,uBAAA;AACtB,EAAAA,sBAAA,kBAAmB,CAAA,GAAA,oBAAA;AACnB,EAAAA,sBAAA,sBAAuB,CAAA,GAAA,wBAAA;AACvB,EAAAA,sBAAA,oBAAqB,CAAA,GAAA,sBAAA;AANX,EAAAA,OAAAA,qBAAAA;AAAA,CAAA,EAAA,oBAAA,IAAA,EAAA;AASL,MAAM,uBAA0B,GAAA;AAAA,EACrC,aAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AACF;AAEa,MAAA,eAAA,GAAkB,CAAC,cAAsC,KAAA;AACpE,EAAA,OAAO,cAAe,CAAA,MAAA;AAAA,IACpB,CAAC,cAAc,MAAY,MAAA,EAAE,GAAG,YAAc,EAAA,CAAC,MAAM,GAAG,IAAK,EAAA,CAAA;AAAA,IAC7D;AAAC,GACH;AACF;AAEa,MAAA,aAAA,GAAgB,CAAC,cAAsC,KAAA;AAClE,EAAA,OAAO,cAAe,CAAA,MAAA;AAAA,IACpB,CAAC,cAAc,MAAY,MAAA,EAAE,GAAG,YAAc,EAAA,CAAC,MAAM,GAAG,KAAM,EAAA,CAAA;AAAA,IAC9D;AAAC,GACH;AACF;AAEa,MAAA,2BAAA,GAA8B,CACzC,cACY,KAAA;AACZ,EAAA,OAAO,cAAe,CAAA,MAAA,CAAO,CAAC,YAAA,EAAc,MAAW,KAAA;AACrD,IAAA,MAAM,qBAAsB,uBAAqC,CAAA,QAAA;AAAA,MAC/D;AAAA,QAEE,KACA,GAAA,IAAA;AACJ,IAAA,OAAO,EAAE,GAAG,YAAA,EAAc,CAAC,MAAM,GAAG,kBAAmB,EAAA;AAAA,GACzD,EAAG,EAAE,CAAA;AACP;AAEa,MAAA,iBAAA,GAAoB,CAC/B,SACyB,KAAA;AACzB,EAAA,OAAO,SAAU,CAAA,GAAA;AAAA,IAAI,CACnB,QAAA,KAAA,QAAA,CAAS,MAAW,KAAA,WAAA,GAAc,SAAS,KAAQ,GAAA;AAAA,GACrD;AACF;AAEa,MAAA,eAAA,GAAkB,CAC7B,QAAA,EACA,SACmB,KAAA;AACnB,EAAA,OAAO,QAAS,CAAA,GAAA,CAAI,CAAC,OAAA,EAAS,KAAW,MAAA;AAAA,IACvC,MAAA,EAAQ,UAAU,KAAK,CAAA;AAAA,IACvB,OAAA;AAAA,IACA,QAAU,EAAA;AAAA,GACV,CAAA,CAAA;AACJ;;;;"}
1
+ {"version":3,"file":"loading.esm.js","sources":["../../src/utils/loading.ts"],"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 { Duration, Loading } from '../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { DEFAULT_DURATION } from './duration';\n\nexport type ProductState = {\n product: Product;\n entity: Maybe<Entity>;\n duration: Duration;\n};\n\nexport enum DefaultLoadingAction {\n UserGroups = 'user-groups',\n LastCompleteBillingDate = 'billing-date',\n CostInsightsInitial = 'cost-insights-initial',\n CostInsightsPage = 'cost-insights-page',\n CostInsightsProducts = 'cost-insights-products',\n CostInsightsAlerts = 'cost-insights-alerts',\n}\n\nexport const INITIAL_LOADING_ACTIONS = [\n DefaultLoadingAction.UserGroups,\n DefaultLoadingAction.CostInsightsInitial,\n DefaultLoadingAction.CostInsightsProducts,\n];\n\nexport const getDefaultState = (loadingActions: string[]): Loading => {\n return loadingActions.reduce(\n (defaultState, action) => ({ ...defaultState, [action]: true }),\n {},\n );\n};\n\nexport const getResetState = (loadingActions: string[]): Loading => {\n return loadingActions.reduce(\n (defaultState, action) => ({ ...defaultState, [action]: false }),\n {},\n );\n};\n\nexport const getResetStateWithoutInitial = (\n loadingActions: string[],\n): Loading => {\n return loadingActions.reduce((defaultState, action) => {\n const loadingActionState = (INITIAL_LOADING_ACTIONS as string[]).includes(\n action,\n )\n ? false\n : true;\n return { ...defaultState, [action]: loadingActionState };\n }, {});\n};\n\nexport const settledResponseOf = (\n responses: PromiseSettledResult<Entity | any>[],\n): Array<Maybe<Entity>> => {\n return responses.map(response =>\n response.status === 'fulfilled' ? response.value : null,\n );\n};\n\nexport const initialStatesOf = (\n products: Product[],\n responses: Array<Maybe<Entity>>,\n): ProductState[] => {\n return products.map((product, index) => ({\n entity: responses[index],\n product: product,\n duration: DEFAULT_DURATION,\n }));\n};\n"],"names":["DefaultLoadingAction"],"mappings":";;AA8BO,IAAK,oBAAA,qBAAAA,qBAAAA,KAAL;AACL,EAAAA,sBAAA,YAAA,CAAA,GAAa,aAAA;AACb,EAAAA,sBAAA,yBAAA,CAAA,GAA0B,cAAA;AAC1B,EAAAA,sBAAA,qBAAA,CAAA,GAAsB,uBAAA;AACtB,EAAAA,sBAAA,kBAAA,CAAA,GAAmB,oBAAA;AACnB,EAAAA,sBAAA,sBAAA,CAAA,GAAuB,wBAAA;AACvB,EAAAA,sBAAA,oBAAA,CAAA,GAAqB,sBAAA;AANX,EAAA,OAAAA,qBAAAA;AAAA,CAAA,EAAA,oBAAA,IAAA,EAAA;AASL,MAAM,uBAAA,GAA0B;AAAA,EACrC,aAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AACF;AAEO,MAAM,eAAA,GAAkB,CAAC,cAAA,KAAsC;AACpE,EAAA,OAAO,cAAA,CAAe,MAAA;AAAA,IACpB,CAAC,cAAc,MAAA,MAAY,EAAE,GAAG,YAAA,EAAc,CAAC,MAAM,GAAG,IAAA,EAAK,CAAA;AAAA,IAC7D;AAAC,GACH;AACF;AAEO,MAAM,aAAA,GAAgB,CAAC,cAAA,KAAsC;AAClE,EAAA,OAAO,cAAA,CAAe,MAAA;AAAA,IACpB,CAAC,cAAc,MAAA,MAAY,EAAE,GAAG,YAAA,EAAc,CAAC,MAAM,GAAG,KAAA,EAAM,CAAA;AAAA,IAC9D;AAAC,GACH;AACF;AAEO,MAAM,2BAAA,GAA8B,CACzC,cAAA,KACY;AACZ,EAAA,OAAO,cAAA,CAAe,MAAA,CAAO,CAAC,YAAA,EAAc,MAAA,KAAW;AACrD,IAAA,MAAM,qBAAsB,uBAAA,CAAqC,QAAA;AAAA,MAC/D;AAAA,QAEE,KAAA,GACA,IAAA;AACJ,IAAA,OAAO,EAAE,GAAG,YAAA,EAAc,CAAC,MAAM,GAAG,kBAAA,EAAmB;AAAA,EACzD,CAAA,EAAG,EAAE,CAAA;AACP;AAEO,MAAM,iBAAA,GAAoB,CAC/B,SAAA,KACyB;AACzB,EAAA,OAAO,SAAA,CAAU,GAAA;AAAA,IAAI,CAAA,QAAA,KACnB,QAAA,CAAS,MAAA,KAAW,WAAA,GAAc,SAAS,KAAA,GAAQ;AAAA,GACrD;AACF;AAEO,MAAM,eAAA,GAAkB,CAC7B,QAAA,EACA,SAAA,KACmB;AACnB,EAAA,OAAO,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,EAAS,KAAA,MAAW;AAAA,IACvC,MAAA,EAAQ,UAAU,KAAK,CAAA;AAAA,IACvB,OAAA;AAAA,IACA,QAAA,EAAU;AAAA,GACZ,CAAE,CAAA;AACJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.esm.js","sources":["../../src/utils/navigation.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 MoneyIcon from '@material-ui/icons/MonetizationOn';\nimport ActionIcon from '@material-ui/icons/Whatshot';\nimport Settings from '@material-ui/icons/Settings';\nimport AccountTree from '@material-ui/icons/AccountTree';\nimport Storage from '@material-ui/icons/Storage';\nimport Search from '@material-ui/icons/Search';\nimport CloudQueue from '@material-ui/icons/CloudQueue';\nimport School from '@material-ui/icons/School';\nimport ViewHeadline from '@material-ui/icons/ViewHeadline';\nimport { IconType } from '../types';\n\nexport enum DefaultNavigation {\n CostOverviewCard = 'cost-overview-card',\n AlertInsightsHeader = 'alert-insights-header',\n}\n\nexport type NavigationItem = {\n navigation: string;\n icon: JSX.Element;\n title: string;\n};\n\nexport const getDefaultNavigationItems = (alerts: number): NavigationItem[] => {\n const items = [\n {\n navigation: DefaultNavigation.CostOverviewCard,\n icon: <MoneyIcon />,\n title: 'Cost Overview',\n },\n ];\n if (alerts > 0) {\n items.push({\n navigation: DefaultNavigation.AlertInsightsHeader,\n icon: <ActionIcon />,\n title: 'Action Items',\n });\n }\n return items;\n};\n\nexport function getIcon(icon?: string): JSX.Element {\n switch (icon) {\n case IconType.Compute:\n return <Settings />;\n case IconType.Data:\n return <AccountTree />;\n case IconType.Database:\n return <ViewHeadline />;\n case IconType.Storage:\n return <Storage />;\n case IconType.Search:\n return <Search />;\n case IconType.ML:\n return <School />;\n default:\n return <CloudQueue />;\n }\n}\n"],"names":["DefaultNavigation"],"mappings":";;;;;;;;;;;;;AA0BY,IAAA,iBAAA,qBAAAA,kBAAL,KAAA;AACL,EAAAA,mBAAA,kBAAmB,CAAA,GAAA,oBAAA;AACnB,EAAAA,mBAAA,qBAAsB,CAAA,GAAA,uBAAA;AAFZ,EAAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;AAWC,MAAA,yBAAA,GAA4B,CAAC,MAAqC,KAAA;AAC7E,EAAA,MAAM,KAAQ,GAAA;AAAA,IACZ;AAAA,MACE,UAAY,EAAA,oBAAA;AAAA,MACZ,IAAA,sBAAO,SAAU,EAAA,EAAA,CAAA;AAAA,MACjB,KAAO,EAAA;AAAA;AACT,GACF;AACA,EAAA,IAAI,SAAS,CAAG,EAAA;AACd,IAAA,KAAA,CAAM,IAAK,CAAA;AAAA,MACT,UAAY,EAAA,uBAAA;AAAA,MACZ,IAAA,sBAAO,UAAW,EAAA,EAAA,CAAA;AAAA,MAClB,KAAO,EAAA;AAAA,KACR,CAAA;AAAA;AAEH,EAAO,OAAA,KAAA;AACT;AAEO,SAAS,QAAQ,IAA4B,EAAA;AAClD,EAAA,QAAQ,IAAM;AAAA,IACZ,KAAK,QAAS,CAAA,OAAA;AACZ,MAAA,2BAAQ,QAAS,EAAA,EAAA,CAAA;AAAA,IACnB,KAAK,QAAS,CAAA,IAAA;AACZ,MAAA,2BAAQ,WAAY,EAAA,EAAA,CAAA;AAAA,IACtB,KAAK,QAAS,CAAA,QAAA;AACZ,MAAA,2BAAQ,YAAa,EAAA,EAAA,CAAA;AAAA,IACvB,KAAK,QAAS,CAAA,OAAA;AACZ,MAAA,2BAAQ,OAAQ,EAAA,EAAA,CAAA;AAAA,IAClB,KAAK,QAAS,CAAA,MAAA;AACZ,MAAA,2BAAQ,MAAO,EAAA,EAAA,CAAA;AAAA,IACjB,KAAK,QAAS,CAAA,EAAA;AACZ,MAAA,2BAAQ,MAAO,EAAA,EAAA,CAAA;AAAA,IACjB;AACE,MAAA,2BAAQ,UAAW,EAAA,EAAA,CAAA;AAAA;AAEzB;;;;"}
1
+ {"version":3,"file":"navigation.esm.js","sources":["../../src/utils/navigation.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 MoneyIcon from '@material-ui/icons/MonetizationOn';\nimport ActionIcon from '@material-ui/icons/Whatshot';\nimport Settings from '@material-ui/icons/Settings';\nimport AccountTree from '@material-ui/icons/AccountTree';\nimport Storage from '@material-ui/icons/Storage';\nimport Search from '@material-ui/icons/Search';\nimport CloudQueue from '@material-ui/icons/CloudQueue';\nimport School from '@material-ui/icons/School';\nimport ViewHeadline from '@material-ui/icons/ViewHeadline';\nimport { IconType } from '../types';\n\nexport enum DefaultNavigation {\n CostOverviewCard = 'cost-overview-card',\n AlertInsightsHeader = 'alert-insights-header',\n}\n\nexport type NavigationItem = {\n navigation: string;\n icon: JSX.Element;\n title: string;\n};\n\nexport const getDefaultNavigationItems = (alerts: number): NavigationItem[] => {\n const items = [\n {\n navigation: DefaultNavigation.CostOverviewCard,\n icon: <MoneyIcon />,\n title: 'Cost Overview',\n },\n ];\n if (alerts > 0) {\n items.push({\n navigation: DefaultNavigation.AlertInsightsHeader,\n icon: <ActionIcon />,\n title: 'Action Items',\n });\n }\n return items;\n};\n\nexport function getIcon(icon?: string): JSX.Element {\n switch (icon) {\n case IconType.Compute:\n return <Settings />;\n case IconType.Data:\n return <AccountTree />;\n case IconType.Database:\n return <ViewHeadline />;\n case IconType.Storage:\n return <Storage />;\n case IconType.Search:\n return <Search />;\n case IconType.ML:\n return <School />;\n default:\n return <CloudQueue />;\n }\n}\n"],"names":["DefaultNavigation"],"mappings":";;;;;;;;;;;;;AA0BO,IAAK,iBAAA,qBAAAA,kBAAAA,KAAL;AACL,EAAAA,mBAAA,kBAAA,CAAA,GAAmB,oBAAA;AACnB,EAAAA,mBAAA,qBAAA,CAAA,GAAsB,uBAAA;AAFZ,EAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;AAWL,MAAM,yBAAA,GAA4B,CAAC,MAAA,KAAqC;AAC7E,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ;AAAA,MACE,UAAA,EAAY,oBAAA;AAAA,MACZ,IAAA,sBAAO,SAAA,EAAA,EAAU,CAAA;AAAA,MACjB,KAAA,EAAO;AAAA;AACT,GACF;AACA,EAAA,IAAI,SAAS,CAAA,EAAG;AACd,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,UAAA,EAAY,uBAAA;AAAA,MACZ,IAAA,sBAAO,UAAA,EAAA,EAAW,CAAA;AAAA,MAClB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,QAAQ,IAAA,EAA4B;AAClD,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,QAAA,CAAS,OAAA;AACZ,MAAA,2BAAQ,QAAA,EAAA,EAAS,CAAA;AAAA,IACnB,KAAK,QAAA,CAAS,IAAA;AACZ,MAAA,2BAAQ,WAAA,EAAA,EAAY,CAAA;AAAA,IACtB,KAAK,QAAA,CAAS,QAAA;AACZ,MAAA,2BAAQ,YAAA,EAAA,EAAa,CAAA;AAAA,IACvB,KAAK,QAAA,CAAS,OAAA;AACZ,MAAA,2BAAQ,OAAA,EAAA,EAAQ,CAAA;AAAA,IAClB,KAAK,QAAA,CAAS,MAAA;AACZ,MAAA,2BAAQ,MAAA,EAAA,EAAO,CAAA;AAAA,IACjB,KAAK,QAAA,CAAS,EAAA;AACZ,MAAA,2BAAQ,MAAA,EAAA,EAAO,CAAA;AAAA,IACjB;AACE,MAAA,2BAAQ,UAAA,EAAA,EAAW,CAAA;AAAA;AAEzB;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"scroll.esm.js","sources":["../../src/utils/scroll.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { useEffect, useRef } from 'react';\nimport { ScrollTo, useScroll } from '../hooks/useScroll';\n\n/*\n Utility component use in conjunction with useScroll that allows scrollable components to control behavior and offset.\n 1. ScrollAnchor must be a direct child of a scrollable component.\n 2. ScrollAnchor's parent position must be relative. \n 3. ScrollAnchor's id must be unique.\n*/\n\nexport interface ScrollAnchorProps extends ScrollIntoViewOptions {\n id: ScrollTo;\n top?: number;\n left?: number;\n}\n\nexport const ScrollAnchor = ({\n id,\n left = 0,\n top = -20,\n block = 'start',\n inline = 'nearest',\n behavior = 'smooth',\n}: ScrollAnchorProps) => {\n const divRef = useRef<HTMLDivElement>(null);\n const [scroll, setScroll] = useScroll();\n\n useEffect(() => {\n function scrollIntoView() {\n if (divRef.current && scroll === id) {\n divRef.current.scrollIntoView({\n block,\n inline,\n behavior,\n });\n setScroll(null);\n }\n }\n\n scrollIntoView();\n }, [scroll, setScroll, id, behavior, block, inline]);\n\n return (\n <div\n ref={divRef}\n style={{ position: 'absolute', height: 0, width: 0, top, left }}\n data-testid={`scroll-test-${id}`}\n />\n );\n};\n"],"names":[],"mappings":";;;;AA+BO,MAAM,eAAe,CAAC;AAAA,EAC3B,EAAA;AAAA,EACA,IAAO,GAAA,CAAA;AAAA,EACP,GAAM,GAAA,CAAA,EAAA;AAAA,EACN,KAAQ,GAAA,OAAA;AAAA,EACR,MAAS,GAAA,SAAA;AAAA,EACT,QAAW,GAAA;AACb,CAAyB,KAAA;AACvB,EAAM,MAAA,MAAA,GAAS,OAAuB,IAAI,CAAA;AAC1C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAU,EAAA;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAS,cAAiB,GAAA;AACxB,MAAI,IAAA,MAAA,CAAO,OAAW,IAAA,MAAA,KAAW,EAAI,EAAA;AACnC,QAAA,MAAA,CAAO,QAAQ,cAAe,CAAA;AAAA,UAC5B,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,SAAA,CAAU,IAAI,CAAA;AAAA;AAChB;AAGF,IAAe,cAAA,EAAA;AAAA,GACjB,EAAG,CAAC,MAAQ,EAAA,SAAA,EAAW,IAAI,QAAU,EAAA,KAAA,EAAO,MAAM,CAAC,CAAA;AAEnD,EACE,uBAAA,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAK,EAAA,MAAA;AAAA,MACL,KAAA,EAAO,EAAE,QAAU,EAAA,UAAA,EAAY,QAAQ,CAAG,EAAA,KAAA,EAAO,CAAG,EAAA,GAAA,EAAK,IAAK,EAAA;AAAA,MAC9D,aAAA,EAAa,eAAe,EAAE,CAAA;AAAA;AAAA,GAChC;AAEJ;;;;"}
1
+ {"version":3,"file":"scroll.esm.js","sources":["../../src/utils/scroll.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { useEffect, useRef } from 'react';\nimport { ScrollTo, useScroll } from '../hooks/useScroll';\n\n/*\n Utility component use in conjunction with useScroll that allows scrollable components to control behavior and offset.\n 1. ScrollAnchor must be a direct child of a scrollable component.\n 2. ScrollAnchor's parent position must be relative. \n 3. ScrollAnchor's id must be unique.\n*/\n\nexport interface ScrollAnchorProps extends ScrollIntoViewOptions {\n id: ScrollTo;\n top?: number;\n left?: number;\n}\n\nexport const ScrollAnchor = ({\n id,\n left = 0,\n top = -20,\n block = 'start',\n inline = 'nearest',\n behavior = 'smooth',\n}: ScrollAnchorProps) => {\n const divRef = useRef<HTMLDivElement>(null);\n const [scroll, setScroll] = useScroll();\n\n useEffect(() => {\n function scrollIntoView() {\n if (divRef.current && scroll === id) {\n divRef.current.scrollIntoView({\n block,\n inline,\n behavior,\n });\n setScroll(null);\n }\n }\n\n scrollIntoView();\n }, [scroll, setScroll, id, behavior, block, inline]);\n\n return (\n <div\n ref={divRef}\n style={{ position: 'absolute', height: 0, width: 0, top, left }}\n data-testid={`scroll-test-${id}`}\n />\n );\n};\n"],"names":[],"mappings":";;;;AA+BO,MAAM,eAAe,CAAC;AAAA,EAC3B,EAAA;AAAA,EACA,IAAA,GAAO,CAAA;AAAA,EACP,GAAA,GAAM,GAAA;AAAA,EACN,KAAA,GAAQ,OAAA;AAAA,EACR,MAAA,GAAS,SAAA;AAAA,EACT,QAAA,GAAW;AACb,CAAA,KAAyB;AACvB,EAAA,MAAM,MAAA,GAAS,OAAuB,IAAI,CAAA;AAC1C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAA,EAAU;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAS,cAAA,GAAiB;AACxB,MAAA,IAAI,MAAA,CAAO,OAAA,IAAW,MAAA,KAAW,EAAA,EAAI;AACnC,QAAA,MAAA,CAAO,QAAQ,cAAA,CAAe;AAAA,UAC5B,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,SAAA,CAAU,IAAI,CAAA;AAAA,MAChB;AAAA,IACF;AAEA,IAAA,cAAA,EAAe;AAAA,EACjB,CAAA,EAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,IAAI,QAAA,EAAU,KAAA,EAAO,MAAM,CAAC,CAAA;AAEnD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,QAAQ,CAAA,EAAG,KAAA,EAAO,CAAA,EAAG,GAAA,EAAK,IAAA,EAAK;AAAA,MAC9D,aAAA,EAAa,eAAe,EAAE,CAAA;AAAA;AAAA,GAChC;AAEJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"sort.esm.js","sources":["../../src/utils/sort.ts"],"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 { ResourceData } from '../types';\nimport { DateAggregation } from '@backstage-community/plugin-cost-insights-common';\nimport { ProductState } from './loading';\n\nexport const aggregationSort = (\n a: DateAggregation,\n b: DateAggregation,\n): number => a.date.localeCompare(b.date);\n\nexport const resourceSort = (a: ResourceData, b: ResourceData) =>\n b.previous + b.current - (a.previous + a.current);\n\nexport function totalAggregationSort(a: ProductState, b: ProductState): number {\n const [prevA, currA] = a.entity?.aggregation ?? [0, 0];\n const [prevB, currB] = b.entity?.aggregation ?? [0, 0];\n return prevB + currB - (prevA + currA);\n}\n"],"names":[],"mappings":"AAmBa,MAAA,eAAA,GAAkB,CAC7B,CACA,EAAA,CAAA,KACW,EAAE,IAAK,CAAA,aAAA,CAAc,EAAE,IAAI;AAE3B,MAAA,YAAA,GAAe,CAAC,CAAA,EAAiB,CAC5C,KAAA,CAAA,CAAE,WAAW,CAAE,CAAA,OAAA,IAAW,CAAE,CAAA,QAAA,GAAW,CAAE,CAAA,OAAA;AAE3B,SAAA,oBAAA,CAAqB,GAAiB,CAAyB,EAAA;AAC7E,EAAM,MAAA,CAAC,OAAO,KAAK,CAAA,GAAI,EAAE,MAAQ,EAAA,WAAA,IAAe,CAAC,CAAA,EAAG,CAAC,CAAA;AACrD,EAAM,MAAA,CAAC,OAAO,KAAK,CAAA,GAAI,EAAE,MAAQ,EAAA,WAAA,IAAe,CAAC,CAAA,EAAG,CAAC,CAAA;AACrD,EAAO,OAAA,KAAA,GAAQ,SAAS,KAAQ,GAAA,KAAA,CAAA;AAClC;;;;"}
1
+ {"version":3,"file":"sort.esm.js","sources":["../../src/utils/sort.ts"],"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 { ResourceData } from '../types';\nimport { DateAggregation } from '@backstage-community/plugin-cost-insights-common';\nimport { ProductState } from './loading';\n\nexport const aggregationSort = (\n a: DateAggregation,\n b: DateAggregation,\n): number => a.date.localeCompare(b.date);\n\nexport const resourceSort = (a: ResourceData, b: ResourceData) =>\n b.previous + b.current - (a.previous + a.current);\n\nexport function totalAggregationSort(a: ProductState, b: ProductState): number {\n const [prevA, currA] = a.entity?.aggregation ?? [0, 0];\n const [prevB, currB] = b.entity?.aggregation ?? [0, 0];\n return prevB + currB - (prevA + currA);\n}\n"],"names":[],"mappings":"AAmBO,MAAM,eAAA,GAAkB,CAC7B,CAAA,EACA,CAAA,KACW,EAAE,IAAA,CAAK,aAAA,CAAc,EAAE,IAAI;AAEjC,MAAM,YAAA,GAAe,CAAC,CAAA,EAAiB,CAAA,KAC5C,CAAA,CAAE,WAAW,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,OAAA;AAEpC,SAAS,oBAAA,CAAqB,GAAiB,CAAA,EAAyB;AAC7E,EAAA,MAAM,CAAC,OAAO,KAAK,CAAA,GAAI,EAAE,MAAA,EAAQ,WAAA,IAAe,CAAC,CAAA,EAAG,CAAC,CAAA;AACrD,EAAA,MAAM,CAAC,OAAO,KAAK,CAAA,GAAI,EAAE,MAAA,EAAQ,WAAA,IAAe,CAAC,CAAA,EAAG,CAAC,CAAA;AACrD,EAAA,OAAO,KAAA,GAAQ,SAAS,KAAA,GAAQ,KAAA,CAAA;AAClC;;;;"}