@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.
- package/dist/api/InfraWalletApi.esm.js.map +1 -1
- package/dist/api/InfraWalletApiClient.esm.js +35 -13
- package/dist/api/InfraWalletApiClient.esm.js.map +1 -1
- package/dist/api/functions.esm.js +5 -4
- package/dist/api/functions.esm.js.map +1 -1
- package/dist/components/Budgets/Budgets.esm.js +184 -0
- package/dist/components/Budgets/Budgets.esm.js.map +1 -0
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js +32 -23
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js.map +1 -1
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js +41 -42
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js.map +1 -1
- package/dist/components/CustomCostsComponent/BulkInsertButton.esm.js +195 -0
- package/dist/components/CustomCostsComponent/BulkInsertButton.esm.js.map +1 -0
- package/dist/components/CustomCostsComponent/CustomCostsComponent.esm.js +369 -0
- package/dist/components/CustomCostsComponent/CustomCostsComponent.esm.js.map +1 -0
- package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js +95 -87
- package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js.map +1 -1
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js +16 -16
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js.map +1 -1
- package/dist/components/FiltersComponent/FiltersComponent.esm.js +72 -66
- package/dist/components/FiltersComponent/FiltersComponent.esm.js.map +1 -1
- package/dist/components/InfraWalletIcon.esm.js +10 -10
- package/dist/components/InfraWalletIcon.esm.js.map +1 -1
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js +15 -17
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js.map +1 -1
- package/dist/components/PieChartComponent/PieChartComponent.esm.js +5 -5
- package/dist/components/PieChartComponent/PieChartComponent.esm.js.map +1 -1
- package/dist/components/ProviderIcons/ProviderIcons.esm.js +62 -63
- package/dist/components/ProviderIcons/ProviderIcons.esm.js.map +1 -1
- package/dist/components/ReportsComponent/ReportsComponent.esm.js +37 -26
- package/dist/components/ReportsComponent/ReportsComponent.esm.js.map +1 -1
- package/dist/components/Router.esm.js +2 -3
- package/dist/components/Router.esm.js.map +1 -1
- package/dist/components/SettingsComponent/SettingsComponent.esm.js +4 -7
- package/dist/components/SettingsComponent/SettingsComponent.esm.js.map +1 -1
- package/dist/components/TopbarComponent/TopbarComponent.esm.js +16 -17
- package/dist/components/TopbarComponent/TopbarComponent.esm.js.map +1 -1
- package/dist/components/index.esm.js +22 -26
- package/dist/components/index.esm.js.map +1 -1
- 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":";;
|
|
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
|
|
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/
|
|
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}/
|
|
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}/
|
|
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}/
|
|
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
|
-
|
|
15
|
-
const idsToBeKept =
|
|
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
|
|
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)} \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
|
|
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
|
|
5
|
-
import
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
85
|
-
costs && costsSeries ? /* @__PURE__ */
|
|
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:
|
|
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
|
|
112
|
+
highlightedItemSetter(highlighted?.seriesId);
|
|
112
113
|
}
|
|
113
114
|
},
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
-
/* @__PURE__ */
|
|
119
|
-
/* @__PURE__ */
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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 (
|
|
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__ */
|
|
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__ */
|
|
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
|
|