@electrolux-oss/plugin-infrawallet 0.1.12 → 0.2.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 (40) hide show
  1. package/dist/api/InfraWalletApi.esm.js.map +1 -1
  2. package/dist/api/InfraWalletApiClient.esm.js +35 -13
  3. package/dist/api/InfraWalletApiClient.esm.js.map +1 -1
  4. package/dist/api/functions.esm.js +5 -4
  5. package/dist/api/functions.esm.js.map +1 -1
  6. package/dist/components/Budgets/Budgets.esm.js +184 -0
  7. package/dist/components/Budgets/Budgets.esm.js.map +1 -0
  8. package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js +32 -23
  9. package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js.map +1 -1
  10. package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js +41 -42
  11. package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js.map +1 -1
  12. package/dist/components/CustomCostsComponent/BulkInsertButton.esm.js +195 -0
  13. package/dist/components/CustomCostsComponent/BulkInsertButton.esm.js.map +1 -0
  14. package/dist/components/CustomCostsComponent/CustomCostsComponent.esm.js +369 -0
  15. package/dist/components/CustomCostsComponent/CustomCostsComponent.esm.js.map +1 -0
  16. package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js +95 -87
  17. package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js.map +1 -1
  18. package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js +16 -16
  19. package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js.map +1 -1
  20. package/dist/components/FiltersComponent/FiltersComponent.esm.js +72 -66
  21. package/dist/components/FiltersComponent/FiltersComponent.esm.js.map +1 -1
  22. package/dist/components/InfraWalletIcon.esm.js +10 -10
  23. package/dist/components/InfraWalletIcon.esm.js.map +1 -1
  24. package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js +15 -17
  25. package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js.map +1 -1
  26. package/dist/components/PieChartComponent/PieChartComponent.esm.js +5 -5
  27. package/dist/components/PieChartComponent/PieChartComponent.esm.js.map +1 -1
  28. package/dist/components/ProviderIcons/ProviderIcons.esm.js +62 -63
  29. package/dist/components/ProviderIcons/ProviderIcons.esm.js.map +1 -1
  30. package/dist/components/ReportsComponent/ReportsComponent.esm.js +37 -26
  31. package/dist/components/ReportsComponent/ReportsComponent.esm.js.map +1 -1
  32. package/dist/components/Router.esm.js +2 -3
  33. package/dist/components/Router.esm.js.map +1 -1
  34. package/dist/components/SettingsComponent/SettingsComponent.esm.js +4 -7
  35. package/dist/components/SettingsComponent/SettingsComponent.esm.js.map +1 -1
  36. package/dist/components/TopbarComponent/TopbarComponent.esm.js +16 -17
  37. package/dist/components/TopbarComponent/TopbarComponent.esm.js.map +1 -1
  38. package/dist/components/index.esm.js +22 -26
  39. package/dist/components/index.esm.js.map +1 -1
  40. package/package.json +19 -14
@@ -1 +1 @@
1
- {"version":3,"file":"InfraWalletApi.esm.js","sources":["../../src/api/InfraWalletApi.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\n\n/** @public */\nexport const infraWalletApiRef = createApiRef<InfraWalletApi>({\n id: 'plugin.infrawallet',\n});\n\n/** @public */\nexport interface InfraWalletApi {\n getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse>;\n getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse>;\n getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse>;\n getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse>;\n getMetricConfigs(): Promise<MetricConfigsResponse>;\n getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse>;\n updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }>;\n deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }>;\n getWalletByName(walletName: string): Promise<GetWalletResponse>;\n}\n"],"names":[],"mappings":";;AAaO,MAAM,oBAAoB,YAA6B,CAAA;AAAA,EAC5D,EAAI,EAAA,oBAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"InfraWalletApi.esm.js","sources":["../../src/api/InfraWalletApi.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport {\n Budget,\n BudgetsResponse,\n CostReportsResponse,\n CustomCost,\n CustomCostsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\n\n/** @public */\nexport const infraWalletApiRef = createApiRef<InfraWalletApi>({\n id: 'plugin.infrawallet',\n});\n\n/** @public */\nexport interface InfraWalletApi {\n getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse>;\n getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse>;\n getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse>;\n getBudgets(walletName: string): Promise<BudgetsResponse>;\n getBudget(walletName: string, provider: string): Promise<BudgetsResponse>;\n updateBudget(walletName: string, budget: Budget): Promise<{ updated: boolean; status: number }>;\n getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse>;\n getMetricConfigs(): Promise<MetricConfigsResponse>;\n getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse>;\n updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }>;\n deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }>;\n getWalletByName(walletName: string): Promise<GetWalletResponse>;\n getCustomCosts(): Promise<CustomCostsResponse>;\n createCustomCosts(customCosts: CustomCost[]): Promise<{ created: number; status: number }>;\n updateCustomCost(customCost: CustomCost): Promise<{ updated: boolean; status: number }>;\n deleteCustomCost(customCost: CustomCost): Promise<{ deleted: boolean; status: number }>;\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,oBAAoB,YAA6B,CAAA;AAAA,EAC5D,EAAI,EAAA,oBAAA;AACN,CAAC;;;;"}
@@ -1,16 +1,10 @@
1
1
  import fetch from 'node-fetch';
2
2
  import { tagsToString } from './functions.esm.js';
3
3
 
4
- var __defProp = Object.defineProperty;
5
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
- var __publicField = (obj, key, value) => {
7
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
- return value;
9
- };
10
4
  class InfraWalletApiClient {
5
+ identityApi;
6
+ backendUrl;
11
7
  constructor(options) {
12
- __publicField(this, "identityApi");
13
- __publicField(this, "backendUrl");
14
8
  this.identityApi = options.identityApi;
15
9
  this.backendUrl = options.configApi.getString("backend.baseUrl");
16
10
  }
@@ -23,7 +17,7 @@ class InfraWalletApiClient {
23
17
  }
24
18
  const request = {
25
19
  headers,
26
- method: method != null ? method : "GET"
20
+ method: method ?? "GET"
27
21
  };
28
22
  if (payload) {
29
23
  request.body = JSON.stringify(payload);
@@ -51,6 +45,18 @@ class InfraWalletApiClient {
51
45
  const url = `api/infrawallet/tag-values?provider=${provider}&tag=${tagKey}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;
52
46
  return await this.request(url);
53
47
  }
48
+ async getBudgets(walletName) {
49
+ const url = `api/infrawallet/${walletName}/budgets`;
50
+ return await this.request(url);
51
+ }
52
+ async getBudget(walletName, provider) {
53
+ const url = `api/infrawallet/${walletName}/budgets?provider=${provider}`;
54
+ return await this.request(url);
55
+ }
56
+ async updateBudget(walletName, budget) {
57
+ const url = `api/infrawallet/${walletName}/budgets`;
58
+ return await this.request(url, "PUT", budget);
59
+ }
54
60
  async getWalletByName(walletName) {
55
61
  const url = `api/infrawallet/${walletName}`;
56
62
  return await this.request(url);
@@ -60,21 +66,37 @@ class InfraWalletApiClient {
60
66
  return await this.request(url);
61
67
  }
62
68
  async getMetricConfigs() {
63
- const url = "api/infrawallet/metric/metric_configs";
69
+ const url = "api/infrawallet/metric/metric-configs";
64
70
  return await this.request(url);
65
71
  }
66
72
  async getWalletMetricsSetting(walletName) {
67
- const url = `api/infrawallet/${walletName}/metrics_setting`;
73
+ const url = `api/infrawallet/${walletName}/metrics-setting`;
68
74
  return await this.request(url);
69
75
  }
70
76
  async updateWalletMetricSetting(walletName, metricSetting) {
71
- const url = `api/infrawallet/${walletName}/metrics_setting`;
77
+ const url = `api/infrawallet/${walletName}/metrics-setting`;
72
78
  return await this.request(url, "PUT", metricSetting);
73
79
  }
74
80
  async deleteWalletMetricSetting(walletName, metricSetting) {
75
- const url = `api/infrawallet/${walletName}/metrics_setting`;
81
+ const url = `api/infrawallet/${walletName}/metrics-setting`;
76
82
  return await this.request(url, "DELETE", metricSetting);
77
83
  }
84
+ async getCustomCosts() {
85
+ const url = `api/infrawallet/custom-costs`;
86
+ return await this.request(url);
87
+ }
88
+ async createCustomCosts(customCosts) {
89
+ const url = `api/infrawallet/custom-costs`;
90
+ return await this.request(url, "POST", customCosts);
91
+ }
92
+ async updateCustomCost(customCost) {
93
+ const url = `api/infrawallet/custom-cost`;
94
+ return await this.request(url, "PUT", customCost);
95
+ }
96
+ async deleteCustomCost(customCost) {
97
+ const url = `api/infrawallet/custom-cost`;
98
+ return await this.request(url, "DELETE", customCost);
99
+ }
78
100
  }
79
101
 
80
102
  export { InfraWalletApiClient };
@@ -1 +1 @@
1
- {"version":3,"file":"InfraWalletApiClient.esm.js","sources":["../../src/api/InfraWalletApiClient.ts"],"sourcesContent":["import { ConfigApi, IdentityApi } from '@backstage/core-plugin-api';\nimport fetch from 'node-fetch';\nimport { InfraWalletApi } from './InfraWalletApi';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\nimport { tagsToString } from './functions';\n\n/** @public */\nexport class InfraWalletApiClient implements InfraWalletApi {\n private readonly identityApi: IdentityApi;\n private readonly backendUrl: string;\n\n constructor(options: { identityApi: IdentityApi; configApi: ConfigApi }) {\n this.identityApi = options.identityApi;\n this.backendUrl = options.configApi.getString('backend.baseUrl');\n }\n\n async request(path: string, method?: string, payload?: Record<string, string | undefined>) {\n const url = `${this.backendUrl}/${path}`;\n const { token: idToken } = await this.identityApi.getCredentials();\n const headers: Record<string, string> = idToken ? { Authorization: `Bearer ${idToken}` } : {};\n\n if (method !== undefined && method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n const request: any = {\n headers: headers,\n method: method ?? 'GET',\n };\n\n if (payload) {\n request.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, request);\n\n if (!response.ok) {\n const res = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${res}`;\n throw new Error(message);\n }\n\n return await response.json();\n }\n\n async getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse> {\n const tagsString = tagsToString(tags);\n const url = `api/infrawallet/reports?granularity=${granularity}&groups=${groups}&filters=${filters}&tags=${tagsString}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n\n return await this.request(url);\n }\n async getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse> {\n const url = `api/infrawallet/tag-keys?provider=${provider}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse> {\n const provider = tag.provider;\n const tagKey = tag.key;\n const url = `api/infrawallet/tag-values?provider=${provider}&tag=${tagKey}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getWalletByName(walletName: string): Promise<GetWalletResponse> {\n const url = `api/infrawallet/${walletName}`;\n return await this.request(url);\n }\n\n async getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse> {\n const url = `api/infrawallet/${walletName}/metrics?&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getMetricConfigs(): Promise<MetricConfigsResponse> {\n const url = 'api/infrawallet/metric/metric_configs';\n return await this.request(url);\n }\n\n async getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url);\n }\n async updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'PUT', metricSetting);\n }\n\n async deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'DELETE', metricSetting);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InfraWalletApiClient.esm.js","sources":["../../src/api/InfraWalletApiClient.ts"],"sourcesContent":["import { ConfigApi, IdentityApi } from '@backstage/core-plugin-api';\nimport fetch from 'node-fetch';\nimport { InfraWalletApi } from './InfraWalletApi';\nimport { tagsToString } from './functions';\nimport {\n Budget,\n BudgetsResponse,\n CostReportsResponse,\n CustomCost,\n CustomCostsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\n\n/** @public */\nexport class InfraWalletApiClient implements InfraWalletApi {\n private readonly identityApi: IdentityApi;\n private readonly backendUrl: string;\n\n constructor(options: { identityApi: IdentityApi; configApi: ConfigApi }) {\n this.identityApi = options.identityApi;\n this.backendUrl = options.configApi.getString('backend.baseUrl');\n }\n\n async request(path: string, method?: string, payload?: Record<string, any>) {\n const url = `${this.backendUrl}/${path}`;\n const { token: idToken } = await this.identityApi.getCredentials();\n const headers: Record<string, string> = idToken ? { Authorization: `Bearer ${idToken}` } : {};\n\n if (method !== undefined && method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n const request: any = {\n headers: headers,\n method: method ?? 'GET',\n };\n\n if (payload) {\n request.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, request);\n\n if (!response.ok) {\n const res = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${res}`;\n throw new Error(message);\n }\n\n return await response.json();\n }\n\n async getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse> {\n const tagsString = tagsToString(tags);\n const url = `api/infrawallet/reports?granularity=${granularity}&groups=${groups}&filters=${filters}&tags=${tagsString}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n\n return await this.request(url);\n }\n\n async getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse> {\n const url = `api/infrawallet/tag-keys?provider=${provider}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse> {\n const provider = tag.provider;\n const tagKey = tag.key;\n const url = `api/infrawallet/tag-values?provider=${provider}&tag=${tagKey}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getBudgets(walletName: string): Promise<BudgetsResponse> {\n const url = `api/infrawallet/${walletName}/budgets`;\n return await this.request(url);\n }\n\n async getBudget(walletName: string, provider: string): Promise<BudgetsResponse> {\n const url = `api/infrawallet/${walletName}/budgets?provider=${provider}`;\n return await this.request(url);\n }\n\n async updateBudget(walletName: string, budget: Budget): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/budgets`;\n return await this.request(url, 'PUT', budget);\n }\n\n async getWalletByName(walletName: string): Promise<GetWalletResponse> {\n const url = `api/infrawallet/${walletName}`;\n return await this.request(url);\n }\n\n async getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse> {\n const url = `api/infrawallet/${walletName}/metrics?&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getMetricConfigs(): Promise<MetricConfigsResponse> {\n const url = 'api/infrawallet/metric/metric-configs';\n return await this.request(url);\n }\n\n async getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse> {\n const url = `api/infrawallet/${walletName}/metrics-setting`;\n return await this.request(url);\n }\n\n async updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics-setting`;\n return await this.request(url, 'PUT', metricSetting);\n }\n\n async deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics-setting`;\n return await this.request(url, 'DELETE', metricSetting);\n }\n\n async getCustomCosts(): Promise<CustomCostsResponse> {\n const url = `api/infrawallet/custom-costs`;\n return await this.request(url);\n }\n\n async createCustomCosts(customCosts: CustomCost[]): Promise<{ created: number; status: number }> {\n const url = `api/infrawallet/custom-costs`;\n return await this.request(url, 'POST', customCosts);\n }\n\n async updateCustomCost(customCost: CustomCost): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/custom-cost`;\n return await this.request(url, 'PUT', customCost);\n }\n\n async deleteCustomCost(customCost: CustomCost): Promise<{ deleted: boolean; status: number }> {\n const url = `api/infrawallet/custom-cost`;\n return await this.request(url, 'DELETE', customCost);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -11,8 +11,8 @@ const mergeCostReports = (reports, threshold) => {
11
11
  });
12
12
  totalCosts.push({ id: report.id, total });
13
13
  });
14
- const sortedTotalCosts = totalCosts.sort((a, b) => b.total - a.total);
15
- const idsToBeKept = sortedTotalCosts.slice(0, threshold).map((v) => v.id);
14
+ totalCosts.sort((a, b) => b.total - a.total);
15
+ const idsToBeKept = totalCosts.slice(0, threshold).map((v) => v.id);
16
16
  const mergedReports = reduce(
17
17
  reports,
18
18
  (accumulator, report) => {
@@ -37,7 +37,7 @@ const mergeCostReports = (reports, threshold) => {
37
37
  },
38
38
  {}
39
39
  );
40
- return Object.values(mergedReports);
40
+ return Object.values(mergedReports).sort((a, b) => a.id.localeCompare(b.id));
41
41
  };
42
42
  const filterCostReports = (reports, filters) => {
43
43
  const filteredReports = reports.filter((report) => {
@@ -87,7 +87,7 @@ const aggregateCostReports = (reports, aggregatedBy) => {
87
87
  const getReportKeyAndValues = (reports) => {
88
88
  const excludedKeys = ["id", "reports"];
89
89
  const keyValueSets = {};
90
- reports.forEach((report) => {
90
+ reports?.forEach((report) => {
91
91
  Object.keys(report).forEach((key) => {
92
92
  if (!excludedKeys.includes(key)) {
93
93
  if (keyValueSets[key] === void 0) {
@@ -100,6 +100,7 @@ const getReportKeyAndValues = (reports) => {
100
100
  const keyValues = {};
101
101
  Object.keys(keyValueSets).forEach((key) => {
102
102
  keyValues[key] = Array.from(keyValueSets[key]);
103
+ keyValues[key].sort((a, b) => a.localeCompare(b));
103
104
  });
104
105
  return keyValues;
105
106
  };
@@ -1 +1 @@
1
- {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subDays, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Filters, Report, Tag } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n const sortedTotalCosts = totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = sortedTotalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'Others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports);\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0 && !filters[key].includes(report[key] as string)) {\n match = false;\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n provider: report.provider,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[]): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n }\n\n keyValueSets[key].add(report[key] as string);\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n });\n return keyValues;\n};\n\nexport const extractProvider = (input: string): string | undefined => {\n let provider = undefined;\n if (input && input.indexOf('/') !== -1) {\n provider = input.split('/')[0];\n }\n\n return provider;\n};\n\nexport const extractAccountInfo = (input: string): { accountName: string; accountId?: string } => {\n // try to match format: accountName (accountId), e.g. aws-dev (123456789012)\n const regex = /^(.*?)\\s*\\(([^)]+)\\)$/;\n const match = input.match(regex);\n\n if (match) {\n const accountName = match[1];\n const accountId = match[2];\n return { accountName: accountName, accountId: accountId };\n }\n\n return { accountName: input };\n};\n\n// check if targetTag exists in tags\nexport function tagExists(tags: Tag[], targetTag: Tag): boolean {\n return tags.some(\n tag => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value,\n );\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport const tagsToString = (tags: Tag[]): string => {\n if (tags.length === 0) {\n return '()';\n }\n\n const keyValuePairs = tags.map(tag => `${tag.provider}:${tag.key}=${tag.value}`);\n return `(${keyValuePairs.join(' OR ')})`;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'account', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPreviousDay = (day: string): string => {\n const date = parse(day, 'yyyy-MM-dd', new Date());\n const previousDay = subDays(date, 1);\n return format(previousDay, 'yyyy-MM-dd');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n\nexport const formatCurrency = (number: number, currency?: string): string => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n notation: 'compact',\n }).format(number);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subDays, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Filters, Report, Tag } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold?: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = totalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'Others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports).sort((a, b) => a.id.localeCompare(b.id));\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0 && !filters[key].includes(report[key] as string)) {\n match = false;\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n provider: report.provider,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[] | undefined): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports?.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n }\n\n keyValueSets[key].add(report[key] as string);\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n keyValues[key].sort((a, b) => a.localeCompare(b));\n });\n return keyValues;\n};\n\nexport const extractProvider = (input: string): string | undefined => {\n let provider = undefined;\n if (input && input.indexOf('/') !== -1) {\n provider = input.split('/')[0];\n }\n\n return provider;\n};\n\nexport const extractAccountInfo = (input: string): { accountName: string; accountId?: string } => {\n // try to match format: accountName (accountId), e.g. aws-dev (123456789012)\n const regex = /^(.*?)\\s*\\(([^)]+)\\)$/;\n const match = input.match(regex);\n\n if (match) {\n const accountName = match[1];\n const accountId = match[2];\n return { accountName: accountName, accountId: accountId };\n }\n\n return { accountName: input };\n};\n\n// check if targetTag exists in tags\nexport function tagExists(tags: Tag[], targetTag: Tag): boolean {\n return tags.some(\n tag => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value,\n );\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport const tagsToString = (tags: Tag[]): string => {\n if (tags.length === 0) {\n return '()';\n }\n\n const keyValuePairs = tags.map(tag => `${tag.provider}:${tag.key}=${tag.value}`);\n return `(${keyValuePairs.join(' OR ')})`;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'account', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPreviousDay = (day: string): string => {\n const date = parse(day, 'yyyy-MM-dd', new Date());\n const previousDay = subDays(date, 1);\n return format(previousDay, 'yyyy-MM-dd');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n\nexport const formatCurrency = (number: number, currency?: string): string => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n notation: 'compact',\n }).format(number);\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,184 @@
1
+ import { useApi, alertApiRef } from '@backstage/core-plugin-api';
2
+ import Box from '@mui/material/Box';
3
+ import Button from '@mui/material/Button';
4
+ import Dialog from '@mui/material/Dialog';
5
+ import DialogActions from '@mui/material/DialogActions';
6
+ import DialogContent from '@mui/material/DialogContent';
7
+ import DialogContentText from '@mui/material/DialogContentText';
8
+ import DialogTitle from '@mui/material/DialogTitle';
9
+ import FormControl from '@mui/material/FormControl';
10
+ import Grid from '@mui/material/Grid';
11
+ import Input from '@mui/material/Input';
12
+ import InputAdornment from '@mui/material/InputAdornment';
13
+ import InputLabel from '@mui/material/InputLabel';
14
+ import Paper from '@mui/material/Paper';
15
+ import Skeleton from '@mui/material/Skeleton';
16
+ import { useTheme } from '@mui/material/styles';
17
+ import Switch from '@mui/material/Switch';
18
+ import Typography from '@mui/material/Typography';
19
+ import { useDrawingArea, ChartContainer, ChartsGrid, ChartsAxisHighlight, LinePlot, BarPlot, MarkPlot, LineHighlightPlot, ChartsReferenceLine, ChartsXAxis, ChartsYAxis, ChartsTooltip } from '@mui/x-charts';
20
+ import { max } from 'lodash';
21
+ import moment from 'moment';
22
+ import React__default, { useState, useCallback, useEffect } from 'react';
23
+ import { aggregateCostReports, mergeCostReports, formatCurrency } from '../../api/functions.esm.js';
24
+ import { infraWalletApiRef } from '../../api/InfraWalletApi.esm.js';
25
+ import { colorList } from '../constants.esm.js';
26
+ import { getProviderIcon } from '../ProviderIcons/ProviderIcons.esm.js';
27
+
28
+ function BudgetChart(props) {
29
+ const { width, height } = useDrawingArea();
30
+ const theme = useTheme();
31
+ const { provider, monthlyCosts, view } = props;
32
+ const [annualBudget, setAnnualBudget] = useState(void 0);
33
+ const [openManageBudget, setOpenManageBudget] = useState(false);
34
+ const [refreshTrigger, setRefreshTrigger] = useState(false);
35
+ const infraWalletApi = useApi(infraWalletApiRef);
36
+ useEffect(() => {
37
+ const fetchBudget = async () => {
38
+ const response = await infraWalletApi.getBudget("default", provider);
39
+ const updatedBudget = response.data?.find((a) => a.provider.toLowerCase() === provider.toLowerCase());
40
+ setAnnualBudget(updatedBudget);
41
+ };
42
+ fetchBudget();
43
+ }, [refreshTrigger, provider, infraWalletApi]);
44
+ const updateBudget = async (event) => {
45
+ event.preventDefault();
46
+ const formData = new FormData(event.currentTarget);
47
+ const name = annualBudget?.name || `${provider} annual budget`;
48
+ const amount = formData.get("amount");
49
+ const newAnnualBudget = {
50
+ id: annualBudget?.id,
51
+ provider,
52
+ name,
53
+ amount: amount ? Number(amount) : 0
54
+ };
55
+ await infraWalletApi.updateBudget("default", newAnnualBudget);
56
+ setRefreshTrigger((prev) => !prev);
57
+ setOpenManageBudget(false);
58
+ };
59
+ const accumulatedCosts = [];
60
+ for (let i = 0; i < monthlyCosts.length; i++) {
61
+ if (i === 0) {
62
+ accumulatedCosts[i] = monthlyCosts[i];
63
+ } else {
64
+ accumulatedCosts[i] = accumulatedCosts[i - 1] + monthlyCosts[i];
65
+ }
66
+ }
67
+ let budgetAmount = annualBudget?.amount || 0;
68
+ if (view === "Monthly" /* MONTHLY */) {
69
+ budgetAmount = budgetAmount / 12;
70
+ }
71
+ return /* @__PURE__ */ React__default.createElement(Paper, { sx: { padding: 2 } }, /* @__PURE__ */ React__default.createElement(
72
+ ChartContainer,
73
+ {
74
+ width: width + 20,
75
+ height,
76
+ series: [
77
+ {
78
+ data: view === "Annual" /* ANNUAL */ ? accumulatedCosts : monthlyCosts,
79
+ type: view === "Annual" /* ANNUAL */ ? "line" : "bar",
80
+ valueFormatter: (value) => {
81
+ return formatCurrency(value || 0);
82
+ },
83
+ showMark: false
84
+ }
85
+ ],
86
+ xAxis: [
87
+ {
88
+ data: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
89
+ scaleType: "band"
90
+ }
91
+ ],
92
+ yAxis: [
93
+ {
94
+ min: 0,
95
+ max: view === "Annual" /* ANNUAL */ ? max([...accumulatedCosts, budgetAmount]) : max([...monthlyCosts, budgetAmount]),
96
+ valueFormatter: (value) => {
97
+ return formatCurrency(value || 0);
98
+ },
99
+ colorMap: {
100
+ type: "piecewise",
101
+ thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],
102
+ colors: [colorList[0], theme.palette.error.main]
103
+ }
104
+ }
105
+ ]
106
+ },
107
+ /* @__PURE__ */ React__default.createElement(ChartsGrid, { horizontal: true }),
108
+ /* @__PURE__ */ React__default.createElement(ChartsAxisHighlight, { x: view === "Annual" /* ANNUAL */ ? "line" : "band" }),
109
+ /* @__PURE__ */ React__default.createElement(LinePlot, null),
110
+ /* @__PURE__ */ React__default.createElement(BarPlot, null),
111
+ /* @__PURE__ */ React__default.createElement(MarkPlot, null),
112
+ /* @__PURE__ */ React__default.createElement(LineHighlightPlot, null),
113
+ /* @__PURE__ */ React__default.createElement(
114
+ ChartsReferenceLine,
115
+ {
116
+ y: budgetAmount,
117
+ label: budgetAmount ? formatCurrency(budgetAmount) : void 0,
118
+ labelAlign: "end",
119
+ lineStyle: {
120
+ stroke: budgetAmount ? theme.palette.error.main : "transparent",
121
+ strokeDasharray: "5 5",
122
+ strokeWidth: 1.5,
123
+ strokeOpacity: 0.8
124
+ },
125
+ labelStyle: { fill: theme.palette.error.main, fontSize: "0.9em" }
126
+ }
127
+ ),
128
+ /* @__PURE__ */ React__default.createElement(ChartsXAxis, null),
129
+ /* @__PURE__ */ React__default.createElement(ChartsYAxis, null),
130
+ /* @__PURE__ */ React__default.createElement(ChartsTooltip, null)
131
+ ), /* @__PURE__ */ React__default.createElement("div", { style: { textAlign: "center", fontWeight: "bold" } }, getProviderIcon(provider), " ", provider), /* @__PURE__ */ React__default.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React__default.createElement(Button, { onClick: () => setOpenManageBudget(true) }, "Manage budget"), /* @__PURE__ */ React__default.createElement(Dialog, { fullWidth: true, maxWidth: "sm", open: openManageBudget, onClose: () => setOpenManageBudget(false) }, /* @__PURE__ */ React__default.createElement("form", { onSubmit: updateBudget }, /* @__PURE__ */ React__default.createElement(DialogTitle, null, "Manage Budget"), /* @__PURE__ */ React__default.createElement(DialogContent, null, /* @__PURE__ */ React__default.createElement(DialogContentText, null, "Please enter your ", props.provider, " annual budget here."), /* @__PURE__ */ React__default.createElement(Box, { sx: { display: "flex", alignItems: "flex-end" } }, getProviderIcon(provider), "\xA0\xA0", /* @__PURE__ */ React__default.createElement(FormControl, { variant: "standard" }, /* @__PURE__ */ React__default.createElement(InputLabel, null, "Amount"), /* @__PURE__ */ React__default.createElement(
132
+ Input,
133
+ {
134
+ id: "amount",
135
+ name: "amount",
136
+ type: "number",
137
+ startAdornment: /* @__PURE__ */ React__default.createElement(InputAdornment, { position: "start" }, "$"),
138
+ defaultValue: annualBudget?.amount
139
+ }
140
+ )))), /* @__PURE__ */ React__default.createElement(DialogActions, null, /* @__PURE__ */ React__default.createElement(Button, { type: "submit", variant: "contained" }, "Submit"), /* @__PURE__ */ React__default.createElement(Button, { onClick: () => setOpenManageBudget(false) }, "Cancel"))))));
141
+ }
142
+ const Budgets = ({ providerErrorsSetter }) => {
143
+ const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState(void 0);
144
+ const [budgetView, setBudgetView] = useState("Annual" /* ANNUAL */);
145
+ const infraWalletApi = useApi(infraWalletApiRef);
146
+ const alertApi = useApi(alertApiRef);
147
+ const fetchCosts = useCallback(async () => {
148
+ await infraWalletApi.getCostReports("", [], "", "monthly", moment().startOf("y").toDate(), moment().endOf("d").toDate()).then((reportsResponse) => {
149
+ if (reportsResponse.data) {
150
+ const aggregatedReports = aggregateCostReports(reportsResponse.data, "provider");
151
+ const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);
152
+ setReportsAggregatedAndMerged(aggregatedAndMergedReports);
153
+ }
154
+ if (reportsResponse.status === 207 && reportsResponse.errors) {
155
+ providerErrorsSetter(reportsResponse.errors);
156
+ }
157
+ }).catch((e) => alertApi.post({ message: `${e.message}`, severity: "error" }));
158
+ }, [alertApi, infraWalletApi, providerErrorsSetter]);
159
+ useEffect(() => {
160
+ fetchCosts();
161
+ }, [fetchCosts]);
162
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "h5" }, moment().year(), " ", budgetView, " Budgets")), /* @__PURE__ */ React__default.createElement(Grid, { container: true, justifyContent: "flex-end", spacing: 1 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Annual"), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, /* @__PURE__ */ React__default.createElement(
163
+ Switch,
164
+ {
165
+ size: "small",
166
+ onChange: (event) => setBudgetView(event.target.checked ? "Monthly" /* MONTHLY */ : "Annual" /* ANNUAL */)
167
+ }
168
+ )), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Monthly")), reportsAggregatedAndMerged !== void 0 ? reportsAggregatedAndMerged.map((report) => /* @__PURE__ */ React__default.createElement(Grid, { item: true, key: `${report.id}-grid`, xs: 4 }, /* @__PURE__ */ React__default.createElement(BudgetChart, { provider: report.id, monthlyCosts: Object.values(report.reports), view: budgetView }))) : /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
169
+ Paper,
170
+ {
171
+ sx: {
172
+ display: "flex",
173
+ flexDirection: "column",
174
+ height: 500,
175
+ backgroundColor: "transparent",
176
+ boxShadow: "none"
177
+ }
178
+ },
179
+ /* @__PURE__ */ React__default.createElement("div", { style: { width: "60%", margin: "auto" } }, /* @__PURE__ */ React__default.createElement(Skeleton, null), /* @__PURE__ */ React__default.createElement(Skeleton, null), /* @__PURE__ */ React__default.createElement(Skeleton, null))
180
+ )));
181
+ };
182
+
183
+ export { Budgets };
184
+ //# sourceMappingURL=Budgets.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Budgets.esm.js","sources":["../../../src/components/Budgets/Budgets.tsx"],"sourcesContent":["import { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport FormControl from '@mui/material/FormControl';\nimport Grid from '@mui/material/Grid';\nimport Input from '@mui/material/Input';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport InputLabel from '@mui/material/InputLabel';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport { useTheme } from '@mui/material/styles';\nimport Switch from '@mui/material/Switch';\nimport Typography from '@mui/material/Typography';\nimport {\n BarPlot,\n ChartContainer,\n ChartsAxisHighlight,\n ChartsGrid,\n ChartsReferenceLine,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n useDrawingArea,\n} from '@mui/x-charts';\nimport { max } from 'lodash';\nimport moment from 'moment';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport { aggregateCostReports, formatCurrency, mergeCostReports } from '../../api/functions';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { Budget, Report } from '../../api/types';\nimport { colorList } from '../constants';\nimport { getProviderIcon } from '../ProviderIcons';\nimport { BudgetsProps } from '../types';\n\nconst enum BUDGET_VIEW {\n MONTHLY = 'Monthly',\n ANNUAL = 'Annual',\n}\n\ninterface BudgetChartProps {\n provider: string;\n monthlyCosts: number[];\n view: string;\n}\n\nfunction BudgetChart(props: Readonly<BudgetChartProps>) {\n const { width, height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view } = props;\n\n const [annualBudget, setAnnualBudget] = useState<Budget | undefined>(undefined);\n const [openManageBudget, setOpenManageBudget] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(false);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n\n useEffect(() => {\n const fetchBudget = async () => {\n const response = await infraWalletApi.getBudget('default', provider);\n const updatedBudget = response.data?.find(a => a.provider.toLowerCase() === provider.toLowerCase());\n setAnnualBudget(updatedBudget);\n };\n\n fetchBudget();\n }, [refreshTrigger, provider, infraWalletApi]);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const name = annualBudget?.name || `${provider} annual budget`;\n const amount = formData.get('amount');\n const newAnnualBudget: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: name,\n amount: amount ? Number(amount) : 0,\n };\n await infraWalletApi.updateBudget('default', newAnnualBudget);\n setRefreshTrigger(prev => !prev);\n setOpenManageBudget(false);\n };\n\n const accumulatedCosts: number[] = [];\n for (let i = 0; i < monthlyCosts.length; i++) {\n if (i === 0) {\n accumulatedCosts[i] = monthlyCosts[i];\n } else {\n accumulatedCosts[i] = accumulatedCosts[i - 1] + monthlyCosts[i];\n }\n }\n\n let budgetAmount = annualBudget?.amount || 0;\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n }\n\n return (\n <Paper sx={{ padding: 2 }}>\n <ChartContainer\n width={width + 20}\n height={height}\n series={[\n {\n data: view === BUDGET_VIEW.ANNUAL ? accumulatedCosts : monthlyCosts,\n type: view === BUDGET_VIEW.ANNUAL ? 'line' : 'bar',\n valueFormatter: (value: number | null) => {\n return formatCurrency(value || 0);\n },\n showMark: false,\n },\n ]}\n xAxis={[\n {\n data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n min: 0,\n max:\n view === BUDGET_VIEW.ANNUAL\n ? max([...accumulatedCosts, budgetAmount])\n : max([...monthlyCosts, budgetAmount]),\n valueFormatter: value => {\n return formatCurrency(value || 0);\n },\n colorMap: {\n type: 'piecewise',\n thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],\n colors: [colorList[0], theme.palette.error.main],\n },\n },\n ]}\n >\n <ChartsGrid horizontal />\n <ChartsAxisHighlight x={view === BUDGET_VIEW.ANNUAL ? 'line' : 'band'} />\n <LinePlot />\n <BarPlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsReferenceLine\n y={budgetAmount}\n label={budgetAmount ? formatCurrency(budgetAmount) : undefined}\n labelAlign=\"end\"\n lineStyle={{\n stroke: budgetAmount ? theme.palette.error.main : 'transparent',\n strokeDasharray: '5 5',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.error.main, fontSize: '0.9em' }}\n />\n <ChartsXAxis />\n <ChartsYAxis />\n <ChartsTooltip />\n </ChartContainer>\n <div style={{ textAlign: 'center', fontWeight: 'bold' }}>\n {getProviderIcon(provider)} {provider}\n </div>\n <div style={{ textAlign: 'center' }}>\n <Button onClick={() => setOpenManageBudget(true)}>Manage budget</Button>\n <Dialog fullWidth maxWidth=\"sm\" open={openManageBudget} onClose={() => setOpenManageBudget(false)}>\n <form onSubmit={updateBudget}>\n <DialogTitle>Manage Budget</DialogTitle>\n <DialogContent>\n <DialogContentText>Please enter your {props.provider} annual budget here.</DialogContentText>\n <Box sx={{ display: 'flex', alignItems: 'flex-end' }}>\n {getProviderIcon(provider)}&nbsp;&nbsp;\n <FormControl variant=\"standard\">\n <InputLabel>Amount</InputLabel>\n <Input\n id=\"amount\"\n name=\"amount\"\n type=\"number\"\n startAdornment={<InputAdornment position=\"start\">$</InputAdornment>}\n defaultValue={annualBudget?.amount}\n />\n </FormControl>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button type=\"submit\" variant=\"contained\">\n Submit\n </Button>\n <Button onClick={() => setOpenManageBudget(false)}>Cancel</Button>\n </DialogActions>\n </form>\n </Dialog>\n </div>\n </Paper>\n );\n}\n\nexport const Budgets: FC<BudgetsProps> = ({ providerErrorsSetter }) => {\n const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState<Report[] | undefined>(undefined);\n const [budgetView, setBudgetView] = useState(BUDGET_VIEW.ANNUAL);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n const alertApi = useApi(alertApiRef);\n\n const fetchCosts = useCallback(async () => {\n await infraWalletApi\n .getCostReports('', [], '', 'monthly', moment().startOf('y').toDate(), moment().endOf('d').toDate())\n .then(reportsResponse => {\n if (reportsResponse.data) {\n const aggregatedReports = aggregateCostReports(reportsResponse.data, 'provider');\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n providerErrorsSetter(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [alertApi, infraWalletApi, providerErrorsSetter]);\n\n useEffect(() => {\n fetchCosts();\n }, [fetchCosts]);\n\n return (\n <Grid container spacing={3}>\n <Grid item>\n <Typography variant=\"h5\">\n {moment().year()} {budgetView} Budgets\n </Typography>\n </Grid>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Annual</Grid>\n <Grid item>\n <Switch\n size=\"small\"\n onChange={event => setBudgetView(event.target.checked ? BUDGET_VIEW.MONTHLY : BUDGET_VIEW.ANNUAL)}\n />\n </Grid>\n <Grid item>Monthly</Grid>\n </Grid>\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart provider={report.id} monthlyCosts={Object.values(report.reports)} view={budgetView} />\n </Grid>\n ))\n ) : (\n <Grid item xs={12}>\n <Paper\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: 500,\n backgroundColor: 'transparent',\n boxShadow: 'none',\n }}\n >\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n </Paper>\n </Grid>\n )}\n </Grid>\n );\n};\n"],"names":["React"],"mappings":"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"}
@@ -1,15 +1,16 @@
1
- import { Grid, Switch } from '@material-ui/core';
1
+ import Grid from '@mui/material/Grid';
2
2
  import Paper from '@mui/material/Paper';
3
3
  import Skeleton from '@mui/material/Skeleton';
4
- import { ResponsiveChartContainer, ChartsGrid, BarPlot, LinePlot, LineHighlightPlot, MarkPlot, ChartsXAxis, ChartsYAxis, ChartsTooltip, ChartsLegend, DefaultChartsLegend } from '@mui/x-charts';
5
- import React, { useState, useCallback, useEffect } from 'react';
4
+ import Switch from '@mui/material/Switch';
5
+ import { ResponsiveChartContainer, ChartsGrid, BarPlot, LinePlot, MarkPlot, LineHighlightPlot, ChartsXAxis, ChartsYAxis, ChartsTooltip, ChartsLegend, DefaultChartsLegend } from '@mui/x-charts';
6
+ import React__default, { useState, useCallback, useEffect } from 'react';
6
7
  import { formatCurrency } from '../../api/functions.esm.js';
7
8
  import { colorList } from '../constants.esm.js';
8
9
 
9
10
  const ColumnsChartComponent = ({
10
11
  granularity,
11
12
  granularitySetter,
12
- categories,
13
+ periods,
13
14
  costs,
14
15
  metrics,
15
16
  height,
@@ -71,7 +72,7 @@ const ColumnsChartComponent = ({
71
72
  useEffect(() => {
72
73
  initChartCallback();
73
74
  }, [initChartCallback]);
74
- return /* @__PURE__ */ React.createElement(
75
+ return /* @__PURE__ */ React__default.createElement(
75
76
  Paper,
76
77
  {
77
78
  sx: {
@@ -81,17 +82,17 @@ const ColumnsChartComponent = ({
81
82
  height: height ? height : 300
82
83
  }
83
84
  },
84
- /* @__PURE__ */ React.createElement(Grid, { container: true, justifyContent: "flex-end", spacing: 1 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, "Monthly"), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Switch, { size: "small", onChange: (event) => granularitySetter(event.target.checked ? "daily" : "monthly") })), /* @__PURE__ */ React.createElement(Grid, { item: true }, "Daily"), /* @__PURE__ */ React.createElement(Grid, { item: true }, " | "), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Switch, { size: "small", checked: showMetrics, onChange: (_) => setShowMetrics((ori) => !ori) })), /* @__PURE__ */ React.createElement(Grid, { item: true }, "Show Metrics")),
85
- costs && costsSeries ? /* @__PURE__ */ React.createElement(
85
+ /* @__PURE__ */ React__default.createElement(Grid, { container: true, justifyContent: "flex-end", spacing: 1 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Monthly"), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, /* @__PURE__ */ React__default.createElement(Switch, { size: "small", onChange: (event) => granularitySetter(event.target.checked ? "daily" : "monthly") })), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Daily"), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, " | "), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, /* @__PURE__ */ React__default.createElement(Switch, { size: "small", checked: showMetrics, onChange: (_) => setShowMetrics((ori) => !ori) })), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Show Metrics")),
86
+ costs !== void 0 && costsSeries !== void 0 ? /* @__PURE__ */ React__default.createElement(
86
87
  ResponsiveChartContainer,
87
88
  {
88
89
  margin: {
89
- bottom: 80
90
+ bottom: 6 * costsSeries.length + 80
90
91
  },
91
92
  series: [...costsSeries, ...metricsSeries ? metricsSeries : []],
92
93
  xAxis: [
93
94
  {
94
- data: categories,
95
+ data: periods,
95
96
  scaleType: "band"
96
97
  }
97
98
  ],
@@ -108,30 +109,37 @@ const ColumnsChartComponent = ({
108
109
  colors: colorList,
109
110
  highlightedItem: highlightedItem ? { seriesId: highlightedItem } : null,
110
111
  onHighlightChange: (highlighted) => {
111
- highlightedItemSetter(highlighted == null ? void 0 : highlighted.seriesId);
112
+ highlightedItemSetter(highlighted?.seriesId);
112
113
  }
113
114
  },
114
- /* @__PURE__ */ React.createElement(ChartsGrid, { horizontal: true }),
115
- /* @__PURE__ */ React.createElement(BarPlot, null),
116
- /* @__PURE__ */ React.createElement(LinePlot, null),
117
- /* @__PURE__ */ React.createElement(LineHighlightPlot, null),
118
- /* @__PURE__ */ React.createElement(MarkPlot, null),
119
- /* @__PURE__ */ React.createElement(ChartsXAxis, null),
120
- /* @__PURE__ */ React.createElement(ChartsYAxis, null),
121
- /* @__PURE__ */ React.createElement(ChartsTooltip, { trigger: "item" }),
122
- /* @__PURE__ */ React.createElement(
115
+ /* @__PURE__ */ React__default.createElement(ChartsGrid, { horizontal: true }),
116
+ /* @__PURE__ */ React__default.createElement(BarPlot, null),
117
+ /* @__PURE__ */ React__default.createElement(LinePlot, null),
118
+ /* @__PURE__ */ React__default.createElement(MarkPlot, null),
119
+ /* @__PURE__ */ React__default.createElement(LineHighlightPlot, null),
120
+ /* @__PURE__ */ React__default.createElement(
121
+ ChartsXAxis,
122
+ {
123
+ tickLabelStyle: {
124
+ angle: periods.length > 12 ? -45 : 0,
125
+ textAnchor: periods.length > 12 ? "end" : "middle"
126
+ }
127
+ }
128
+ ),
129
+ /* @__PURE__ */ React__default.createElement(ChartsYAxis, null),
130
+ /* @__PURE__ */ React__default.createElement(ChartsTooltip, { trigger: "item" }),
131
+ /* @__PURE__ */ React__default.createElement(
123
132
  ChartsLegend,
124
133
  {
125
134
  slots: {
126
135
  legend: (props) => {
127
- var _a;
128
136
  const seriesToDisplay = [];
129
137
  for (const s of props.seriesToDisplay) {
130
- if (((_a = props.series.bar) == null ? void 0 : _a.seriesOrder) && props.series.bar.seriesOrder.indexOf(s.id) !== -1) {
138
+ if (props.series.bar?.seriesOrder && props.series.bar.seriesOrder.indexOf(s.id) !== -1) {
131
139
  seriesToDisplay.push(s);
132
140
  }
133
141
  }
134
- return /* @__PURE__ */ React.createElement(
142
+ return /* @__PURE__ */ React__default.createElement(
135
143
  DefaultChartsLegend,
136
144
  {
137
145
  series: props.series,
@@ -141,6 +149,7 @@ const ColumnsChartComponent = ({
141
149
  position: { vertical: "bottom", horizontal: "middle" },
142
150
  itemMarkHeight: 10,
143
151
  itemMarkWidth: 10,
152
+ itemGap: costsSeries.length > 5 ? 5 : 10,
144
153
  labelStyle: {
145
154
  fontSize: "0.9em"
146
155
  },
@@ -158,7 +167,7 @@ const ColumnsChartComponent = ({
158
167
  }
159
168
  }
160
169
  )
161
- ) : /* @__PURE__ */ React.createElement("div", { style: { width: "60%", margin: "auto" } }, /* @__PURE__ */ React.createElement(Skeleton, null), /* @__PURE__ */ React.createElement(Skeleton, null), /* @__PURE__ */ React.createElement(Skeleton, null))
170
+ ) : /* @__PURE__ */ React__default.createElement("div", { style: { width: "60%", margin: "auto" } }, /* @__PURE__ */ React__default.createElement(Skeleton, null), /* @__PURE__ */ React__default.createElement(Skeleton, null), /* @__PURE__ */ React__default.createElement(Skeleton, null))
162
171
  );
163
172
  };
164
173