@electrolux-oss/plugin-infrawallet 0.1.11 → 0.1.12
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/InfraWalletApiClient.esm.js +4 -2
- package/dist/api/InfraWalletApiClient.esm.js.map +1 -1
- package/dist/api/functions.esm.js +15 -3
- package/dist/api/functions.esm.js.map +1 -1
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js +142 -173
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js.map +1 -1
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js +147 -91
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js.map +1 -1
- package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js +254 -0
- package/dist/components/EntityInfraWalletCard/EntityInfraWalletCard.esm.js.map +1 -0
- package/dist/components/EntityInfraWalletCard/index.esm.js +2 -0
- package/dist/components/EntityInfraWalletCard/index.esm.js.map +1 -0
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js +4 -4
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js.map +1 -1
- package/dist/components/FiltersComponent/FiltersComponent.esm.js +27 -27
- package/dist/components/FiltersComponent/FiltersComponent.esm.js.map +1 -1
- package/dist/components/InfraWalletAppData.esm.js +9 -0
- package/dist/components/InfraWalletAppData.esm.js.map +1 -0
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js +22 -10
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js.map +1 -1
- package/dist/components/PieChartComponent/PieChartComponent.esm.js +63 -59
- package/dist/components/PieChartComponent/PieChartComponent.esm.js.map +1 -1
- package/dist/components/ProviderIcons/ProviderIcons.esm.js +8 -8
- package/dist/components/ProviderIcons/ProviderIcons.esm.js.map +1 -1
- package/dist/components/ReportsComponent/ReportsComponent.esm.js +24 -20
- package/dist/components/ReportsComponent/ReportsComponent.esm.js.map +1 -1
- package/dist/components/SettingsComponent/SettingsComponent.esm.js +4 -4
- package/dist/components/SettingsComponent/SettingsComponent.esm.js.map +1 -1
- package/dist/components/index.esm.js +36 -0
- package/dist/components/index.esm.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.esm.js +2 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +10 -2
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +9 -11
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
import Typography from '@material-ui/core/Typography';
|
|
2
|
-
import { makeStyles } from '@material-ui/core/styles';
|
|
3
2
|
import Box from '@mui/material/Box';
|
|
3
|
+
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
|
|
4
4
|
import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid';
|
|
5
|
-
import humanFormat from 'human-format';
|
|
6
5
|
import React from 'react';
|
|
7
|
-
import { extractAccountInfo, getPreviousMonth } from '../../api/functions.esm.js';
|
|
6
|
+
import { extractAccountInfo, formatCurrency, getPreviousMonth, getPreviousDay } from '../../api/functions.esm.js';
|
|
8
7
|
import { getProviderIcon } from '../ProviderIcons/ProviderIcons.esm.js';
|
|
9
|
-
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
|
|
10
8
|
|
|
11
|
-
const useStyles = makeStyles({
|
|
12
|
-
increase: {
|
|
13
|
-
color: "red"
|
|
14
|
-
},
|
|
15
|
-
decrease: {
|
|
16
|
-
color: "green"
|
|
17
|
-
},
|
|
18
|
-
container: {
|
|
19
|
-
display: "flex",
|
|
20
|
-
alignItems: "center"
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
9
|
function CustomToolbar() {
|
|
24
10
|
return /* @__PURE__ */ React.createElement(GridToolbarContainer, null, /* @__PURE__ */ React.createElement(
|
|
25
11
|
GridToolbarExport,
|
|
@@ -30,46 +16,44 @@ function CustomToolbar() {
|
|
|
30
16
|
));
|
|
31
17
|
}
|
|
32
18
|
const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
decimals: 2
|
|
43
|
-
});
|
|
44
|
-
if (periods.includes(previousPeriod) && params.row.reports[previousPeriod] > 0) {
|
|
45
|
-
const diff = params.row.reports[params.field] - params.row.reports[previousPeriod];
|
|
46
|
-
const percentage = Math.round(diff / params.row.reports[previousPeriod] * 100);
|
|
47
|
-
const mark = diff > 0 ? "+" : "";
|
|
48
|
-
if (percentage >= 1 || percentage <= -1) {
|
|
49
|
-
return `$${formattedValue} (${mark}${percentage}%)`;
|
|
19
|
+
let rows = reports;
|
|
20
|
+
const columns = [];
|
|
21
|
+
const columnTotals = {};
|
|
22
|
+
const columnGroupingModel = [];
|
|
23
|
+
if (reports && aggregatedBy !== "none") {
|
|
24
|
+
for (const period of periods) {
|
|
25
|
+
for (const report of reports) {
|
|
26
|
+
if (columnTotals[period] === void 0) {
|
|
27
|
+
columnTotals[period] = 0;
|
|
50
28
|
}
|
|
29
|
+
columnTotals[period] += report.reports[period] || 0;
|
|
51
30
|
}
|
|
52
|
-
return `$${formattedValue}`;
|
|
53
31
|
}
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
const columns = [];
|
|
32
|
+
rows = [...reports, { id: "Total", reports: columnTotals }];
|
|
33
|
+
}
|
|
57
34
|
if (["account", "provider", "service"].includes(aggregatedBy)) {
|
|
58
35
|
columns.push({
|
|
59
|
-
field: "
|
|
36
|
+
field: "icon",
|
|
60
37
|
headerName: "",
|
|
61
38
|
width: 30,
|
|
39
|
+
display: "flex",
|
|
62
40
|
disableExport: true,
|
|
41
|
+
sortable: false,
|
|
63
42
|
renderCell: (params) => {
|
|
64
|
-
|
|
43
|
+
if (params.id === "Total") {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
return /* @__PURE__ */ React.createElement("div", null, getProviderIcon(params.row.provider));
|
|
65
47
|
}
|
|
66
48
|
});
|
|
67
49
|
}
|
|
68
50
|
columns.push(
|
|
51
|
+
// groupBy column
|
|
69
52
|
{
|
|
70
53
|
field: aggregatedBy,
|
|
71
|
-
headerName: aggregatedBy.
|
|
54
|
+
headerName: aggregatedBy === "none" ? "" : aggregatedBy.charAt(0).toUpperCase() + aggregatedBy.slice(1),
|
|
72
55
|
minWidth: 200,
|
|
56
|
+
display: "flex",
|
|
73
57
|
flex: 2,
|
|
74
58
|
renderCell: (params) => {
|
|
75
59
|
let formattedValue = params.formattedValue;
|
|
@@ -81,18 +65,29 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
81
65
|
const account = extractAccountInfo(formattedValue);
|
|
82
66
|
const accountName = account.accountName;
|
|
83
67
|
const accountId = account.accountId;
|
|
84
|
-
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2"
|
|
68
|
+
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, accountName), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, accountId));
|
|
85
69
|
}
|
|
86
70
|
}
|
|
87
|
-
|
|
71
|
+
if (params.id === "Total") {
|
|
72
|
+
return /* @__PURE__ */ React.createElement("div", { style: { fontWeight: "bold" } }, "Total");
|
|
73
|
+
}
|
|
74
|
+
return /* @__PURE__ */ React.createElement("div", null, formattedValue);
|
|
75
|
+
},
|
|
76
|
+
sortComparator: (value1, value2, params1, params2) => {
|
|
77
|
+
if (params1.id === "Total" || params2.id === "Total") {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
return value1 - value2;
|
|
88
81
|
}
|
|
89
82
|
},
|
|
83
|
+
// trend line column
|
|
90
84
|
{
|
|
91
|
-
field: "
|
|
92
|
-
headerName: "
|
|
85
|
+
field: "trend",
|
|
86
|
+
headerName: "Trend",
|
|
93
87
|
width: 100,
|
|
88
|
+
display: "flex",
|
|
94
89
|
disableExport: true,
|
|
95
|
-
|
|
90
|
+
sortable: false,
|
|
96
91
|
renderCell: (params) => /* @__PURE__ */ React.createElement(SparkLineChart, { data: params.value ? params.value[0] : null, plotType: "bar" }),
|
|
97
92
|
valueGetter: (_, row) => [
|
|
98
93
|
periods.map((period) => row.reports[period] !== void 0 ? row.reports[period] : null)
|
|
@@ -100,65 +95,120 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
100
95
|
}
|
|
101
96
|
);
|
|
102
97
|
periods.forEach((period) => {
|
|
103
|
-
columns.push(
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
columns.push(
|
|
99
|
+
// cost column
|
|
100
|
+
{
|
|
101
|
+
field: `cost-${period}`,
|
|
102
|
+
headerName: "Cost",
|
|
103
|
+
type: "number",
|
|
104
|
+
valueGetter: (_, row) => {
|
|
105
|
+
return row.reports[period] ? row.reports[period] : null;
|
|
106
|
+
},
|
|
107
|
+
renderCell: (params) => {
|
|
108
|
+
const value = params.value;
|
|
109
|
+
let cost = "-";
|
|
110
|
+
if (typeof value === "number") {
|
|
111
|
+
cost = formatCurrency(value);
|
|
112
|
+
}
|
|
113
|
+
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { style: { fontWeight: params.id === "Total" ? "bold" : "normal" } }, cost));
|
|
114
|
+
},
|
|
115
|
+
sortComparator: (value1, value2, params1, params2) => {
|
|
116
|
+
if (params1.id === "Total" || params2.id === "Total") {
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
return value1 - value2;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
// change% column
|
|
123
|
+
{
|
|
124
|
+
field: `change-${period}`,
|
|
125
|
+
headerName: "Change",
|
|
126
|
+
disableExport: true,
|
|
127
|
+
sortable: false,
|
|
128
|
+
renderCell: (params) => {
|
|
129
|
+
let percentage = void 0;
|
|
130
|
+
const previousPeriod = period.length === 7 ? getPreviousMonth(period) : getPreviousDay(period);
|
|
131
|
+
if (params.row.reports[period] && periods.includes(previousPeriod) && params.row.reports[previousPeriod] > 0) {
|
|
132
|
+
const diff = params.row.reports[period] - params.row.reports[previousPeriod];
|
|
133
|
+
percentage = Math.round(diff / params.row.reports[previousPeriod] * 100);
|
|
134
|
+
}
|
|
135
|
+
if (percentage === void 0) {
|
|
136
|
+
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", null, "-"));
|
|
137
|
+
}
|
|
138
|
+
let color = "#0052cc";
|
|
139
|
+
let backgroundColor = "#e9f2ff";
|
|
140
|
+
let mark = "";
|
|
141
|
+
if (percentage < 0) {
|
|
142
|
+
color = "#216e4e";
|
|
143
|
+
backgroundColor = "#dcfff1";
|
|
144
|
+
mark = "\u25BC";
|
|
145
|
+
} else if (percentage > 0) {
|
|
146
|
+
color = "#ae2e24";
|
|
147
|
+
backgroundColor = "#ffeceb";
|
|
148
|
+
mark = "\u25B2";
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
151
|
+
"span",
|
|
152
|
+
{
|
|
153
|
+
style: {
|
|
154
|
+
fontSize: "0.82em",
|
|
155
|
+
paddingInline: "2px",
|
|
156
|
+
borderRadius: "4px",
|
|
157
|
+
color,
|
|
158
|
+
backgroundColor
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
mark,
|
|
162
|
+
Math.abs(percentage).toLocaleString(),
|
|
163
|
+
"%"
|
|
164
|
+
));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
columnGroupingModel.push({
|
|
169
|
+
groupId: period,
|
|
170
|
+
children: [{ field: `cost-${period}` }, { field: `change-${period}` }]
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
columns.push(
|
|
174
|
+
// total column
|
|
175
|
+
{
|
|
176
|
+
field: "total",
|
|
177
|
+
headerName: "Total",
|
|
106
178
|
type: "number",
|
|
107
179
|
minWidth: 150,
|
|
108
|
-
flex: 1,
|
|
109
180
|
valueGetter: (_, row) => {
|
|
110
|
-
|
|
181
|
+
let total = 0;
|
|
182
|
+
periods.forEach((period) => {
|
|
183
|
+
total += row.reports[period] ? row.reports[period] : 0;
|
|
184
|
+
});
|
|
185
|
+
return total;
|
|
111
186
|
},
|
|
112
187
|
renderCell: (params) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const costStr = percentageIndex === -1 ? formattedValue : formattedValue.substring(0, percentageIndex);
|
|
117
|
-
let percentageStr = percentageIndex === -1 ? "" : formattedValue.substring(percentageIndex);
|
|
118
|
-
if (percentageStr.includes("-")) {
|
|
119
|
-
className = classes.decrease;
|
|
120
|
-
percentageStr = percentageStr.replace("-", "\u25BC");
|
|
121
|
-
} else if (percentageStr.includes("+")) {
|
|
122
|
-
className = classes.increase;
|
|
123
|
-
percentageStr = percentageStr.replace("+", "\u25B2");
|
|
188
|
+
let formattedValue = "-";
|
|
189
|
+
if (typeof params.value === "number") {
|
|
190
|
+
formattedValue = formatCurrency(params.value);
|
|
124
191
|
}
|
|
125
|
-
return /* @__PURE__ */ React.createElement(
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
type: "number",
|
|
133
|
-
minWidth: 150,
|
|
134
|
-
flex: 1,
|
|
135
|
-
valueGetter: (_, row) => {
|
|
136
|
-
let total = 0;
|
|
137
|
-
periods.forEach((period) => {
|
|
138
|
-
total += row.reports[period] ? row.reports[period] : 0;
|
|
139
|
-
});
|
|
140
|
-
return total;
|
|
141
|
-
},
|
|
142
|
-
renderCell: (params) => {
|
|
143
|
-
let formattedValue = "-";
|
|
144
|
-
if (typeof params.value === "number") {
|
|
145
|
-
formattedValue = `$${humanFormat(params.value, {
|
|
146
|
-
scale: customScale,
|
|
147
|
-
separator: "",
|
|
148
|
-
decimals: 2
|
|
149
|
-
})}`;
|
|
192
|
+
return /* @__PURE__ */ React.createElement("div", { style: { fontWeight: "bold" } }, formattedValue);
|
|
193
|
+
},
|
|
194
|
+
sortComparator: (value1, value2, params1, params2) => {
|
|
195
|
+
if (params1.id === "Total" || params2.id === "Total") {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
return value1 - value2;
|
|
150
199
|
}
|
|
151
|
-
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, formattedValue);
|
|
152
200
|
}
|
|
153
|
-
|
|
154
|
-
return /* @__PURE__ */ React.createElement(Box,
|
|
201
|
+
);
|
|
202
|
+
return /* @__PURE__ */ React.createElement(Box, { sx: { height: 700 } }, /* @__PURE__ */ React.createElement(
|
|
155
203
|
DataGrid,
|
|
156
204
|
{
|
|
157
|
-
|
|
205
|
+
loading: rows === void 0,
|
|
206
|
+
rows,
|
|
158
207
|
columns,
|
|
208
|
+
columnGroupingModel,
|
|
159
209
|
initialState: {
|
|
160
210
|
sorting: {
|
|
161
|
-
sortModel: [{ field: "
|
|
211
|
+
sortModel: [{ field: "total", sort: "desc" }]
|
|
162
212
|
},
|
|
163
213
|
pagination: {
|
|
164
214
|
paginationModel: {
|
|
@@ -168,6 +218,12 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
168
218
|
},
|
|
169
219
|
pageSizeOptions: [15],
|
|
170
220
|
slots: { toolbar: CustomToolbar },
|
|
221
|
+
slotProps: {
|
|
222
|
+
loadingOverlay: {
|
|
223
|
+
variant: "skeleton",
|
|
224
|
+
noRowsVariant: "skeleton"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
171
227
|
disableRowSelectionOnClick: true,
|
|
172
228
|
disableColumnMenu: true,
|
|
173
229
|
density: aggregatedBy === "account" ? "standard" : "compact"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CostReportsTableComponent.esm.js","sources":["../../../src/components/CostReportsTableComponent/CostReportsTableComponent.tsx"],"sourcesContent":["import Typography from '@material-ui/core/Typography';\nimport { makeStyles } from '@material-ui/core/styles';\nimport Box from '@mui/material/Box';\nimport { DataGrid, GridColDef, GridRenderCellParams, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid';\nimport humanFormat from 'human-format';\nimport React, { FC } from 'react';\nimport { extractAccountInfo, getPreviousMonth } from '../../api/functions';\nimport { CostReportsTableComponentProps } from '../types';\nimport { getProviderIcon } from '../ProviderIcons';\nimport { SparkLineChart } from '@mui/x-charts/SparkLineChart';\n\nconst useStyles = makeStyles({\n increase: {\n color: 'red',\n },\n decrease: {\n color: 'green',\n },\n container: {\n display: 'flex',\n alignItems: 'center',\n },\n});\n\nfunction CustomToolbar() {\n return (\n <GridToolbarContainer>\n <GridToolbarExport\n csvOptions={{ fileName: 'InfraWallet-export' }}\n printOptions={{ disableToolbarButton: true }}\n />\n </GridToolbarContainer>\n );\n}\n\nexport const CostReportsTableComponent: FC<CostReportsTableComponentProps> = ({ reports, aggregatedBy, periods }) => {\n const classes = useStyles();\n const customScale = humanFormat.Scale.create(['', 'K', 'M', 'B'], 1000);\n\n const formatCostValue = (params: GridRenderCellParams, period: string): string => {\n const value = params.value;\n if (typeof value === 'number') {\n const previousPeriod = period.length === 7 ? getPreviousMonth(params.field) : '';\n const formattedValue = humanFormat(value, {\n scale: customScale,\n separator: '',\n decimals: 2,\n });\n if (periods.includes(previousPeriod) && params.row.reports[previousPeriod] > 0) {\n const diff = params.row.reports[params.field] - params.row.reports[previousPeriod];\n const percentage = Math.round((diff / params.row.reports[previousPeriod]) * 100);\n const mark = diff > 0 ? '+' : '';\n // only display percentage change if it is larger than 1% or less than -1%\n if (percentage >= 1 || percentage <= -1) {\n return `$${formattedValue} (${mark}${percentage}%)`;\n }\n }\n return `$${formattedValue}`;\n }\n return '-';\n };\n\n const columns: GridColDef[] = [];\n if (['account', 'provider', 'service'].includes(aggregatedBy)) {\n columns.push({\n field: 'PROVIDER',\n headerName: '',\n width: 30,\n disableExport: true,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n return getProviderIcon(params.row.provider);\n },\n });\n }\n\n columns.push(\n {\n field: aggregatedBy,\n headerName: aggregatedBy.toLocaleUpperCase('en-US'),\n minWidth: 200,\n flex: 2,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n let formattedValue = params.formattedValue;\n\n if (aggregatedBy === 'service' || aggregatedBy === 'account') {\n // remove provider names\n if (params.formattedValue !== undefined && params.formattedValue.indexOf('/') !== -1) {\n formattedValue = params.formattedValue.split('/')[1];\n }\n\n if (aggregatedBy === 'account' && formattedValue) {\n const account = extractAccountInfo(formattedValue);\n const accountName = account.accountName;\n const accountId = account.accountId;\n\n return (\n <div>\n <Typography variant=\"body2\" className={classes.container}>\n {accountName}\n </Typography>\n <Typography variant=\"caption\" className={classes.container} color=\"textSecondary\">\n {accountId}\n </Typography>\n </div>\n );\n }\n }\n\n return (\n <Typography variant=\"body2\" component=\"div\" className={classes.container}>\n {formattedValue}\n </Typography>\n );\n },\n },\n {\n field: 'TREND',\n headerName: 'TREND',\n width: 100,\n disableExport: true,\n hideSortIcons: true,\n renderCell: (params: GridRenderCellParams) => (\n <SparkLineChart data={params.value ? params.value[0] : null} plotType=\"bar\" />\n ),\n valueGetter: (_, row) => [\n periods.map(period => (row.reports[period] !== undefined ? row.reports[period] : null)),\n ],\n },\n );\n\n periods.forEach(period => {\n columns.push({\n field: period,\n headerName: period,\n type: 'number',\n minWidth: 150,\n flex: 1,\n valueGetter: (_, row) => {\n return row.reports[period] ? row.reports[period] : null;\n },\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n const formattedValue = formatCostValue(params, period);\n let className = '';\n const percentageIndex = formattedValue.indexOf('(');\n const costStr = percentageIndex === -1 ? formattedValue : formattedValue.substring(0, percentageIndex);\n let percentageStr = percentageIndex === -1 ? '' : formattedValue.substring(percentageIndex);\n if (percentageStr.includes('-')) {\n className = classes.decrease;\n percentageStr = percentageStr.replace('-', '▼');\n } else if (percentageStr.includes('+')) {\n className = classes.increase;\n percentageStr = percentageStr.replace('+', '▲');\n }\n\n return (\n <Typography variant=\"body2\">\n {costStr}\n <Typography variant=\"inherit\" className={className}>\n {percentageStr}\n </Typography>\n </Typography>\n );\n },\n });\n });\n\n columns.push({\n field: 'TOTAL',\n headerName: 'TOTAL',\n type: 'number',\n minWidth: 150,\n flex: 1,\n valueGetter: (_, row) => {\n let total = 0;\n periods.forEach(period => {\n total += row.reports[period] ? row.reports[period] : 0;\n });\n return total;\n },\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n let formattedValue = '-';\n if (typeof params.value === 'number') {\n formattedValue = `$${humanFormat(params.value, {\n scale: customScale,\n separator: '',\n decimals: 2,\n })}`;\n }\n return <Typography variant=\"body2\">{formattedValue}</Typography>;\n },\n });\n\n return (\n <Box>\n <DataGrid\n rows={reports}\n columns={columns}\n initialState={{\n sorting: {\n sortModel: [{ field: 'TOTAL', sort: 'desc' }],\n },\n pagination: {\n paginationModel: {\n pageSize: 15,\n },\n },\n }}\n pageSizeOptions={[15]}\n slots={{ toolbar: CustomToolbar }}\n disableRowSelectionOnClick\n disableColumnMenu\n density={aggregatedBy === 'account' ? 'standard' : 'compact'}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CostReportsTableComponent.esm.js","sources":["../../../src/components/CostReportsTableComponent/CostReportsTableComponent.tsx"],"sourcesContent":["import Typography from '@material-ui/core/Typography';\nimport Box from '@mui/material/Box';\nimport { SparkLineChart } from '@mui/x-charts/SparkLineChart';\nimport {\n DataGrid,\n GridColDef,\n GridColumnGroupingModel,\n GridRenderCellParams,\n GridToolbarContainer,\n GridToolbarExport,\n} from '@mui/x-data-grid';\nimport React, { FC } from 'react';\nimport { extractAccountInfo, formatCurrency, getPreviousDay, getPreviousMonth } from '../../api/functions';\nimport { getProviderIcon } from '../ProviderIcons';\nimport { CostReportsTableComponentProps } from '../types';\n\nfunction CustomToolbar() {\n return (\n <GridToolbarContainer>\n <GridToolbarExport\n csvOptions={{ fileName: 'InfraWallet-export' }}\n printOptions={{ disableToolbarButton: true }}\n />\n </GridToolbarContainer>\n );\n}\n\nexport const CostReportsTableComponent: FC<CostReportsTableComponentProps> = ({ reports, aggregatedBy, periods }) => {\n let rows: any[] | undefined = reports;\n const columns: GridColDef[] = [];\n const columnTotals: { [key: string]: number } = {};\n const columnGroupingModel: GridColumnGroupingModel = [];\n\n if (reports && aggregatedBy !== 'none') {\n for (const period of periods) {\n for (const report of reports) {\n if (columnTotals[period] === undefined) {\n columnTotals[period] = 0;\n }\n\n columnTotals[period] += report.reports[period] || 0;\n }\n }\n\n rows = [...reports, { id: 'Total', reports: columnTotals }];\n }\n\n if (['account', 'provider', 'service'].includes(aggregatedBy)) {\n // provider icon column\n columns.push({\n field: 'icon',\n headerName: '',\n width: 30,\n display: 'flex',\n disableExport: true,\n sortable: false,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n if (params.id === 'Total') {\n return undefined;\n }\n\n return <div>{getProviderIcon(params.row.provider)}</div>;\n },\n });\n }\n\n columns.push(\n // groupBy column\n {\n field: aggregatedBy,\n headerName: aggregatedBy === 'none' ? '' : aggregatedBy.charAt(0).toUpperCase() + aggregatedBy.slice(1),\n minWidth: 200,\n display: 'flex',\n flex: 2,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n let formattedValue = params.formattedValue;\n\n if (aggregatedBy === 'service' || aggregatedBy === 'account') {\n // remove provider names\n if (params.formattedValue !== undefined && params.formattedValue.indexOf('/') !== -1) {\n formattedValue = params.formattedValue.split('/')[1];\n }\n\n if (aggregatedBy === 'account' && formattedValue) {\n const account = extractAccountInfo(formattedValue);\n const accountName = account.accountName;\n const accountId = account.accountId;\n\n return (\n <div>\n <Typography variant=\"body2\">{accountName}</Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {accountId}\n </Typography>\n </div>\n );\n }\n }\n\n if (params.id === 'Total') {\n return <div style={{ fontWeight: 'bold' }}>Total</div>;\n }\n\n return <div>{formattedValue}</div>;\n },\n sortComparator: (value1, value2, params1, params2) => {\n if (params1.id === 'Total' || params2.id === 'Total') {\n return 0;\n }\n return value1 - value2;\n },\n },\n // trend line column\n {\n field: 'trend',\n headerName: 'Trend',\n width: 100,\n display: 'flex',\n disableExport: true,\n sortable: false,\n renderCell: (params: GridRenderCellParams) => (\n <SparkLineChart data={params.value ? params.value[0] : null} plotType=\"bar\" />\n ),\n valueGetter: (_, row) => [\n periods.map(period => (row.reports[period] !== undefined ? row.reports[period] : null)),\n ],\n },\n );\n\n periods.forEach(period => {\n columns.push(\n // cost column\n {\n field: `cost-${period}`,\n headerName: 'Cost',\n type: 'number',\n valueGetter: (_, row) => {\n return row.reports[period] ? row.reports[period] : null;\n },\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n const value = params.value;\n let cost = '-';\n\n if (typeof value === 'number') {\n cost = formatCurrency(value);\n }\n\n return (\n <div>\n <span style={{ fontWeight: params.id === 'Total' ? 'bold' : 'normal' }}>{cost}</span>\n </div>\n );\n },\n sortComparator: (value1, value2, params1, params2) => {\n if (params1.id === 'Total' || params2.id === 'Total') {\n return 0;\n }\n return value1 - value2;\n },\n },\n // change% column\n {\n field: `change-${period}`,\n headerName: 'Change',\n disableExport: true,\n sortable: false,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n let percentage = undefined;\n const previousPeriod = period.length === 7 ? getPreviousMonth(period) : getPreviousDay(period);\n if (\n params.row.reports[period] &&\n periods.includes(previousPeriod) &&\n params.row.reports[previousPeriod] > 0\n ) {\n const diff = params.row.reports[period] - params.row.reports[previousPeriod];\n percentage = Math.round((diff / params.row.reports[previousPeriod]) * 100);\n }\n\n if (percentage === undefined) {\n return (\n <div>\n <span>-</span>\n </div>\n );\n }\n\n let color = '#0052cc';\n let backgroundColor = '#e9f2ff';\n let mark = '';\n if (percentage < 0) {\n color = '#216e4e';\n backgroundColor = '#dcfff1';\n mark = '▼';\n } else if (percentage > 0) {\n color = '#ae2e24';\n backgroundColor = '#ffeceb';\n mark = '▲';\n }\n\n return (\n <div>\n <span\n style={{\n fontSize: '0.82em',\n paddingInline: '2px',\n borderRadius: '4px',\n color: color,\n backgroundColor: backgroundColor,\n }}\n >\n {mark}\n {Math.abs(percentage).toLocaleString()}%\n </span>\n </div>\n );\n },\n },\n );\n\n columnGroupingModel.push({\n groupId: period,\n children: [{ field: `cost-${period}` }, { field: `change-${period}` }],\n });\n });\n\n columns.push(\n // total column\n {\n field: 'total',\n headerName: 'Total',\n type: 'number',\n minWidth: 150,\n valueGetter: (_, row) => {\n let total = 0;\n periods.forEach(period => {\n total += row.reports[period] ? row.reports[period] : 0;\n });\n return total;\n },\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n let formattedValue = '-';\n if (typeof params.value === 'number') {\n formattedValue = formatCurrency(params.value);\n }\n return <div style={{ fontWeight: 'bold' }}>{formattedValue}</div>;\n },\n sortComparator: (value1, value2, params1, params2) => {\n if (params1.id === 'Total' || params2.id === 'Total') {\n return 0;\n }\n return value1 - value2;\n },\n },\n );\n\n return (\n <Box sx={{ height: 700 }}>\n <DataGrid\n loading={rows === undefined}\n rows={rows}\n columns={columns}\n columnGroupingModel={columnGroupingModel}\n initialState={{\n sorting: {\n sortModel: [{ field: 'total', sort: 'desc' }],\n },\n pagination: {\n paginationModel: {\n pageSize: 15,\n },\n },\n }}\n pageSizeOptions={[15]}\n slots={{ toolbar: CustomToolbar }}\n slotProps={{\n loadingOverlay: {\n variant: 'skeleton',\n noRowsVariant: 'skeleton',\n },\n }}\n disableRowSelectionOnClick\n disableColumnMenu\n density={aggregatedBy === 'account' ? 'standard' : 'compact'}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Progress, InfoCard } from '@backstage/core-components';
|
|
2
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
3
|
+
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
4
|
+
import { Tabs, Tab, Box, Typography, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
|
|
5
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
6
|
+
import Alert from '@mui/material/Alert';
|
|
7
|
+
import React, { useState, useEffect } from 'react';
|
|
8
|
+
import { ResponsiveContainer, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line } from 'recharts';
|
|
9
|
+
import { infraWalletApiRef } from '../../api/InfraWalletApi.esm.js';
|
|
10
|
+
|
|
11
|
+
const useStyles = makeStyles({
|
|
12
|
+
increase: {
|
|
13
|
+
color: "#ae2e24",
|
|
14
|
+
backgroundColor: "#ffeceb",
|
|
15
|
+
borderRadius: "4px",
|
|
16
|
+
padding: "2px 4px",
|
|
17
|
+
fontSize: "0.82em",
|
|
18
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif'
|
|
19
|
+
},
|
|
20
|
+
decrease: {
|
|
21
|
+
color: "#216e4e",
|
|
22
|
+
backgroundColor: "#dcfff1",
|
|
23
|
+
borderRadius: "4px",
|
|
24
|
+
padding: "2px 4px",
|
|
25
|
+
fontSize: "0.82em",
|
|
26
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif'
|
|
27
|
+
},
|
|
28
|
+
noChange: {
|
|
29
|
+
color: "#0052cc",
|
|
30
|
+
backgroundColor: "#e9f2ff",
|
|
31
|
+
borderRadius: "4px",
|
|
32
|
+
padding: "2px 4px",
|
|
33
|
+
fontSize: "0.82em",
|
|
34
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif'
|
|
35
|
+
},
|
|
36
|
+
regular: {
|
|
37
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif'
|
|
38
|
+
},
|
|
39
|
+
bold: {
|
|
40
|
+
fontWeight: "bold",
|
|
41
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif'
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const COLORS = [
|
|
45
|
+
"#8884d8",
|
|
46
|
+
"#82ca9d",
|
|
47
|
+
"#ffc658",
|
|
48
|
+
"#ff7300",
|
|
49
|
+
"#413ea0",
|
|
50
|
+
"#ff0000",
|
|
51
|
+
"#00ff00",
|
|
52
|
+
"#0000ff",
|
|
53
|
+
"#ff00ff",
|
|
54
|
+
"#00ffff"
|
|
55
|
+
];
|
|
56
|
+
const EntityInfraWalletCard = () => {
|
|
57
|
+
var _a, _b;
|
|
58
|
+
const classes = useStyles();
|
|
59
|
+
const { entity } = useEntity();
|
|
60
|
+
const infrawalletApi = useApi(infraWalletApiRef);
|
|
61
|
+
const [costData, setCostData] = useState(null);
|
|
62
|
+
const [loading, setLoading] = useState(true);
|
|
63
|
+
const [error, setError] = useState(null);
|
|
64
|
+
const [tabIndex, setTabIndex] = useState(0);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const fetchCostData = async () => {
|
|
67
|
+
setLoading(true);
|
|
68
|
+
setError(null);
|
|
69
|
+
try {
|
|
70
|
+
const annotations = entity.metadata.annotations || {};
|
|
71
|
+
const annotationKeys = [
|
|
72
|
+
"infrawallet.io/project",
|
|
73
|
+
"infrawallet.io/account",
|
|
74
|
+
"infrawallet.io/service",
|
|
75
|
+
"infrawallet.io/category",
|
|
76
|
+
"infrawallet.io/provider"
|
|
77
|
+
];
|
|
78
|
+
const filtersObj = {};
|
|
79
|
+
annotationKeys.forEach((key) => {
|
|
80
|
+
if (annotations[key]) {
|
|
81
|
+
const shortKey = key.replace("infrawallet.io/", "");
|
|
82
|
+
const values = annotations[key].split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
83
|
+
filtersObj[shortKey] = values;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
if (annotations["infrawallet.io/extra-filters"]) {
|
|
87
|
+
const extraFilters = annotations["infrawallet.io/extra-filters"];
|
|
88
|
+
extraFilters.split(",").forEach((pair) => {
|
|
89
|
+
const [key, value] = pair.split(":").map((s) => s.trim());
|
|
90
|
+
if (key && value) {
|
|
91
|
+
const values = value.split("|").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
92
|
+
filtersObj[key] = values;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const filtersArray = Object.entries(filtersObj).map(([key, values]) => {
|
|
97
|
+
if (values.length === 1) {
|
|
98
|
+
return `${key}:${values[0]}`;
|
|
99
|
+
}
|
|
100
|
+
const valuesString = values.join("|");
|
|
101
|
+
return `${key}:(${valuesString})`;
|
|
102
|
+
});
|
|
103
|
+
const filters = `(${filtersArray.join(",")})`;
|
|
104
|
+
const tags = [];
|
|
105
|
+
const groups = "";
|
|
106
|
+
const granularity = "monthly";
|
|
107
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
108
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
109
|
+
startTime.setMonth(endTime.getMonth() - 2);
|
|
110
|
+
const costReportsResponse = await infrawalletApi.getCostReports(
|
|
111
|
+
filters,
|
|
112
|
+
tags,
|
|
113
|
+
groups,
|
|
114
|
+
granularity,
|
|
115
|
+
startTime,
|
|
116
|
+
endTime
|
|
117
|
+
);
|
|
118
|
+
if (costReportsResponse.status !== 200) {
|
|
119
|
+
throw new Error("Failed to fetch cost reports");
|
|
120
|
+
}
|
|
121
|
+
setCostData(costReportsResponse.data || null);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
setError(e instanceof Error ? e.message : "Unknown error occurred");
|
|
124
|
+
} finally {
|
|
125
|
+
setLoading(false);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
fetchCostData();
|
|
129
|
+
}, [entity, infrawalletApi]);
|
|
130
|
+
if (loading) {
|
|
131
|
+
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
132
|
+
}
|
|
133
|
+
if (error) {
|
|
134
|
+
return /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error);
|
|
135
|
+
}
|
|
136
|
+
if (!costData || costData.length === 0) {
|
|
137
|
+
return /* @__PURE__ */ React.createElement(Alert, { severity: "info" }, "No cost data available for this entity.");
|
|
138
|
+
}
|
|
139
|
+
const periods = /* @__PURE__ */ new Set();
|
|
140
|
+
costData.forEach((report) => {
|
|
141
|
+
Object.keys(report.reports).forEach((period) => periods.add(period));
|
|
142
|
+
});
|
|
143
|
+
const sortedPeriods = Array.from(periods).sort();
|
|
144
|
+
const totalCostsByPeriod = sortedPeriods.map((period) => {
|
|
145
|
+
const total = costData.reduce((sum, report) => {
|
|
146
|
+
return sum + (report.reports[period] || 0);
|
|
147
|
+
}, 0);
|
|
148
|
+
return { period, total };
|
|
149
|
+
});
|
|
150
|
+
const currentPeriod = sortedPeriods[sortedPeriods.length - 1];
|
|
151
|
+
const previousPeriod = sortedPeriods.length > 1 ? sortedPeriods[sortedPeriods.length - 2] : null;
|
|
152
|
+
const totalCost = ((_a = totalCostsByPeriod.find((item) => item.period === currentPeriod)) == null ? void 0 : _a.total) || 0;
|
|
153
|
+
const previousTotalCost = previousPeriod ? ((_b = totalCostsByPeriod.find((item) => item.period === previousPeriod)) == null ? void 0 : _b.total) || 0 : 0;
|
|
154
|
+
const percentageChange = previousTotalCost !== 0 ? (totalCost - previousTotalCost) / previousTotalCost * 100 : 0;
|
|
155
|
+
const percentageChangeFormatted = Math.abs(percentageChange).toFixed(2);
|
|
156
|
+
let mark = "";
|
|
157
|
+
if (percentageChange < 0) {
|
|
158
|
+
mark = "\u25BC";
|
|
159
|
+
} else if (percentageChange > 0) {
|
|
160
|
+
mark = "\u25B2";
|
|
161
|
+
}
|
|
162
|
+
const projects = Array.from(
|
|
163
|
+
new Set(
|
|
164
|
+
costData.map((report) => {
|
|
165
|
+
const project = report.project;
|
|
166
|
+
return typeof project === "string" ? project : void 0;
|
|
167
|
+
}).filter((project) => !!project)
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
if (projects.length === 0) {
|
|
171
|
+
return /* @__PURE__ */ React.createElement(Alert, { severity: "warning" }, "No project data available to display in the chart.");
|
|
172
|
+
}
|
|
173
|
+
const chartData = sortedPeriods.map((period) => {
|
|
174
|
+
const dataPoint = { period };
|
|
175
|
+
projects.forEach((project) => {
|
|
176
|
+
const projectReports = costData.filter((report) => report.project === project);
|
|
177
|
+
const total = projectReports.reduce((sum, report) => {
|
|
178
|
+
return sum + (report.reports[period] || 0);
|
|
179
|
+
}, 0);
|
|
180
|
+
dataPoint[project] = total;
|
|
181
|
+
});
|
|
182
|
+
return dataPoint;
|
|
183
|
+
});
|
|
184
|
+
const perServiceCosts = costData.reduce((acc, report) => {
|
|
185
|
+
const service = report.service;
|
|
186
|
+
const cost = report.reports[currentPeriod] || 0;
|
|
187
|
+
if (typeof service === "string") {
|
|
188
|
+
if (!acc[service]) {
|
|
189
|
+
acc[service] = 0;
|
|
190
|
+
}
|
|
191
|
+
acc[service] += cost;
|
|
192
|
+
}
|
|
193
|
+
return acc;
|
|
194
|
+
}, {});
|
|
195
|
+
const prevPerServiceCosts = previousPeriod ? costData.reduce((acc, report) => {
|
|
196
|
+
const service = report.service;
|
|
197
|
+
const cost = report.reports[previousPeriod] || 0;
|
|
198
|
+
if (typeof service === "string") {
|
|
199
|
+
if (!acc[service]) {
|
|
200
|
+
acc[service] = 0;
|
|
201
|
+
}
|
|
202
|
+
acc[service] += cost;
|
|
203
|
+
}
|
|
204
|
+
return acc;
|
|
205
|
+
}, {}) : {};
|
|
206
|
+
const serviceRows = Object.entries(perServiceCosts).map(([service, cost]) => {
|
|
207
|
+
const prevCost = prevPerServiceCosts[service] || 0;
|
|
208
|
+
const change = prevCost !== 0 ? (cost - prevCost) / prevCost * 100 : 0;
|
|
209
|
+
return {
|
|
210
|
+
service,
|
|
211
|
+
cost,
|
|
212
|
+
change
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
const getChangeClass = (change) => {
|
|
216
|
+
if (change > 0) {
|
|
217
|
+
return classes.increase;
|
|
218
|
+
}
|
|
219
|
+
if (change === 0) {
|
|
220
|
+
return classes.noChange;
|
|
221
|
+
}
|
|
222
|
+
return classes.decrease;
|
|
223
|
+
};
|
|
224
|
+
const getChangeMark = (change) => {
|
|
225
|
+
if (change < 0) {
|
|
226
|
+
return "\u25BC";
|
|
227
|
+
}
|
|
228
|
+
if (change > 0) {
|
|
229
|
+
return "\u25B2";
|
|
230
|
+
}
|
|
231
|
+
return "";
|
|
232
|
+
};
|
|
233
|
+
const handleTabChange = (_event, newValue) => {
|
|
234
|
+
setTabIndex(newValue);
|
|
235
|
+
};
|
|
236
|
+
return /* @__PURE__ */ React.createElement(InfoCard, { title: "InfraWallet" }, /* @__PURE__ */ React.createElement(Tabs, { value: tabIndex, onChange: handleTabChange, "aria-label": "Cost Tabs" }, /* @__PURE__ */ React.createElement(Tab, { label: "Total Cost" }), /* @__PURE__ */ React.createElement(Tab, { label: "Service Breakdown" })), tabIndex === 0 && /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, previousTotalCost > 0 && /* @__PURE__ */ React.createElement(Box, { className: getChangeClass(percentageChange), mr: 1 }, mark, " ", percentageChangeFormatted, "%"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", className: classes.bold }, "Current Month: $", totalCost.toFixed(2))), /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%", height: 300 }, /* @__PURE__ */ React.createElement(LineChart, { data: chartData }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "period" }), /* @__PURE__ */ React.createElement(YAxis, null), /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), projects.map((project, index) => /* @__PURE__ */ React.createElement(
|
|
237
|
+
Line,
|
|
238
|
+
{
|
|
239
|
+
key: project,
|
|
240
|
+
type: "monotone",
|
|
241
|
+
dataKey: project,
|
|
242
|
+
stroke: COLORS[index % COLORS.length],
|
|
243
|
+
activeDot: { r: 8 }
|
|
244
|
+
}
|
|
245
|
+
))))), tabIndex === 1 && /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { className: classes.bold }, "Service"), /* @__PURE__ */ React.createElement(TableCell, { align: "right", className: classes.bold }, "Monthly Cost"), /* @__PURE__ */ React.createElement(TableCell, { align: "right", className: classes.bold }, "Monthly Change"))), /* @__PURE__ */ React.createElement(TableBody, null, serviceRows.map((row) => {
|
|
246
|
+
const serviceChangeClass = getChangeClass(row.change);
|
|
247
|
+
const serviceMark = getChangeMark(row.change);
|
|
248
|
+
const changeFormatted = Math.abs(row.change).toFixed(2);
|
|
249
|
+
return /* @__PURE__ */ React.createElement(TableRow, { key: row.service }, /* @__PURE__ */ React.createElement(TableCell, { component: "th", scope: "row" }, row.service), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "$", row.cost.toFixed(2)), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(Box, { className: serviceChangeClass, display: "inline" }, serviceMark, " ", changeFormatted, "%")));
|
|
250
|
+
})))));
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export { EntityInfraWalletCard };
|
|
254
|
+
//# sourceMappingURL=EntityInfraWalletCard.esm.js.map
|