@electrolux-oss/plugin-infrawallet 0.1.10 → 0.1.11
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/README.md +45 -21
- package/dist/api/InfraWalletApi.esm.js.map +1 -1
- package/dist/api/InfraWalletApiClient.esm.js +12 -2
- package/dist/api/InfraWalletApiClient.esm.js.map +1 -1
- package/dist/api/functions.esm.js +33 -4
- package/dist/api/functions.esm.js.map +1 -1
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js +45 -28
- package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js.map +1 -1
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js +42 -37
- package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js.map +1 -1
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js +3 -2
- package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js.map +1 -1
- package/dist/components/FiltersComponent/FiltersComponent.esm.js +216 -10
- package/dist/components/FiltersComponent/FiltersComponent.esm.js.map +1 -1
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js +7 -0
- package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js.map +1 -1
- package/dist/components/ProviderIcons/ProviderIcons.esm.js +322 -0
- package/dist/components/ProviderIcons/ProviderIcons.esm.js.map +1 -0
- package/dist/components/ReportsComponent/ReportsComponent.esm.js +30 -15
- package/dist/components/ReportsComponent/ReportsComponent.esm.js.map +1 -1
- package/dist/components/TopbarComponent/TopbarComponent.esm.js +1 -1
- package/dist/components/TopbarComponent/TopbarComponent.esm.js.map +1 -1
- package/package.json +4 -6
- package/dist/components/CostReportsTableComponent/TrendBarComponent.esm.js +0 -43
- package/dist/components/CostReportsTableComponent/TrendBarComponent.esm.js.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import Chip from '@material-ui/core/Chip';
|
|
2
1
|
import Typography from '@material-ui/core/Typography';
|
|
3
2
|
import { makeStyles } from '@material-ui/core/styles';
|
|
4
3
|
import Box from '@mui/material/Box';
|
|
5
4
|
import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid';
|
|
6
5
|
import humanFormat from 'human-format';
|
|
7
6
|
import React from 'react';
|
|
8
|
-
import { getPreviousMonth } from '../../api/functions.esm.js';
|
|
9
|
-
import {
|
|
7
|
+
import { extractAccountInfo, getPreviousMonth } from '../../api/functions.esm.js';
|
|
8
|
+
import { getProviderIcon } from '../ProviderIcons/ProviderIcons.esm.js';
|
|
9
|
+
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
|
|
10
10
|
|
|
11
|
-
const useStyles = makeStyles(
|
|
11
|
+
const useStyles = makeStyles({
|
|
12
12
|
increase: {
|
|
13
13
|
color: "red"
|
|
14
14
|
},
|
|
@@ -18,15 +18,16 @@ const useStyles = makeStyles((theme) => ({
|
|
|
18
18
|
container: {
|
|
19
19
|
display: "flex",
|
|
20
20
|
alignItems: "center"
|
|
21
|
-
},
|
|
22
|
-
clip: {
|
|
23
|
-
backgroundColor: "#deebff",
|
|
24
|
-
color: "#0052cc",
|
|
25
|
-
marginLeft: theme.spacing(1)
|
|
26
21
|
}
|
|
27
|
-
})
|
|
22
|
+
});
|
|
28
23
|
function CustomToolbar() {
|
|
29
|
-
return /* @__PURE__ */ React.createElement(GridToolbarContainer, null, /* @__PURE__ */ React.createElement(
|
|
24
|
+
return /* @__PURE__ */ React.createElement(GridToolbarContainer, null, /* @__PURE__ */ React.createElement(
|
|
25
|
+
GridToolbarExport,
|
|
26
|
+
{
|
|
27
|
+
csvOptions: { fileName: "InfraWallet-export" },
|
|
28
|
+
printOptions: { disableToolbarButton: true }
|
|
29
|
+
}
|
|
30
|
+
));
|
|
30
31
|
}
|
|
31
32
|
const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
32
33
|
const classes = useStyles();
|
|
@@ -52,7 +53,19 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
52
53
|
}
|
|
53
54
|
return "-";
|
|
54
55
|
};
|
|
55
|
-
const columns = [
|
|
56
|
+
const columns = [];
|
|
57
|
+
if (["account", "provider", "service"].includes(aggregatedBy)) {
|
|
58
|
+
columns.push({
|
|
59
|
+
field: "PROVIDER",
|
|
60
|
+
headerName: "",
|
|
61
|
+
width: 30,
|
|
62
|
+
disableExport: true,
|
|
63
|
+
renderCell: (params) => {
|
|
64
|
+
return getProviderIcon(params.row.provider);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
columns.push(
|
|
56
69
|
{
|
|
57
70
|
field: aggregatedBy,
|
|
58
71
|
headerName: aggregatedBy.toLocaleUpperCase("en-US"),
|
|
@@ -60,15 +73,18 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
60
73
|
flex: 2,
|
|
61
74
|
renderCell: (params) => {
|
|
62
75
|
let formattedValue = params.formattedValue;
|
|
63
|
-
|
|
64
|
-
if (aggregatedBy === "service" || aggregatedBy === "name") {
|
|
76
|
+
if (aggregatedBy === "service" || aggregatedBy === "account") {
|
|
65
77
|
if (params.formattedValue !== void 0 && params.formattedValue.indexOf("/") !== -1) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
formattedValue = params.formattedValue.split("/")[1];
|
|
79
|
+
}
|
|
80
|
+
if (aggregatedBy === "account" && formattedValue) {
|
|
81
|
+
const account = extractAccountInfo(formattedValue);
|
|
82
|
+
const accountName = account.accountName;
|
|
83
|
+
const accountId = account.accountId;
|
|
84
|
+
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", className: classes.container }, accountName), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.container, color: "textSecondary" }, accountId));
|
|
69
85
|
}
|
|
70
86
|
}
|
|
71
|
-
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "div", className: classes.container }, formattedValue
|
|
87
|
+
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "div", className: classes.container }, formattedValue);
|
|
72
88
|
}
|
|
73
89
|
},
|
|
74
90
|
{
|
|
@@ -76,24 +92,13 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
76
92
|
headerName: "TREND",
|
|
77
93
|
width: 100,
|
|
78
94
|
disableExport: true,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
series: [
|
|
85
|
-
{
|
|
86
|
-
name: params.row.id,
|
|
87
|
-
data: periods.map(
|
|
88
|
-
(period) => params.row.reports[period] !== void 0 ? params.row.reports[period] : null
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
+
hideSortIcons: true,
|
|
96
|
+
renderCell: (params) => /* @__PURE__ */ React.createElement(SparkLineChart, { data: params.value ? params.value[0] : null, plotType: "bar" }),
|
|
97
|
+
valueGetter: (_, row) => [
|
|
98
|
+
periods.map((period) => row.reports[period] !== void 0 ? row.reports[period] : null)
|
|
99
|
+
]
|
|
95
100
|
}
|
|
96
|
-
|
|
101
|
+
);
|
|
97
102
|
periods.forEach((period) => {
|
|
98
103
|
columns.push({
|
|
99
104
|
field: period,
|
|
@@ -150,7 +155,6 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
150
155
|
DataGrid,
|
|
151
156
|
{
|
|
152
157
|
rows: reports,
|
|
153
|
-
rowHeight: 35,
|
|
154
158
|
columns,
|
|
155
159
|
initialState: {
|
|
156
160
|
sorting: {
|
|
@@ -165,7 +169,8 @@ const CostReportsTableComponent = ({ reports, aggregatedBy, periods }) => {
|
|
|
165
169
|
pageSizeOptions: [15],
|
|
166
170
|
slots: { toolbar: CustomToolbar },
|
|
167
171
|
disableRowSelectionOnClick: true,
|
|
168
|
-
disableColumnMenu: true
|
|
172
|
+
disableColumnMenu: true,
|
|
173
|
+
density: aggregatedBy === "account" ? "standard" : "compact"
|
|
169
174
|
}
|
|
170
175
|
));
|
|
171
176
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CostReportsTableComponent.esm.js","sources":["../../../src/components/CostReportsTableComponent/CostReportsTableComponent.tsx"],"sourcesContent":["import Chip from '@material-ui/core/Chip';\nimport 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 { getPreviousMonth } from '../../api/functions';\nimport { CostReportsTableComponentProps } from '../types';\nimport { TrendBarComponent } from './TrendBarComponent';\n\nconst useStyles = makeStyles(theme => ({\n increase: {\n color: 'red',\n },\n decrease: {\n color: 'green',\n },\n container: {\n display: 'flex',\n alignItems: 'center',\n },\n clip: {\n backgroundColor: '#deebff',\n color: '#0052cc',\n marginLeft: theme.spacing(1),\n },\n}));\n\nfunction CustomToolbar() {\n return (\n <GridToolbarContainer>\n <GridToolbarExport printOptions={{ disableToolbarButton: true }} />\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 {\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 let chipLabel = null;\n\n if (aggregatedBy === 'service' || aggregatedBy === 'name') {\n if (params.formattedValue !== undefined && params.formattedValue.indexOf('/') !== -1) {\n const splitValue = params.formattedValue.split('/');\n formattedValue = splitValue[1];\n chipLabel = splitValue[0].toLowerCase();\n }\n }\n\n return (\n <Typography variant=\"body2\" component=\"div\" className={classes.container}>\n {formattedValue}\n {chipLabel && <Chip size=\"small\" label={chipLabel} className={classes.clip} />}\n </Typography>\n );\n },\n },\n {\n field: 'TREND',\n headerName: 'TREND',\n width: 100,\n disableExport: true,\n renderCell: (params: GridRenderCellParams): React.ReactNode => {\n return (\n <TrendBarComponent\n categories={periods}\n series={[\n {\n name: params.row.id,\n data: periods.map(period =>\n params.row.reports[period] !== undefined ? params.row.reports[period] : null,\n ),\n },\n ]}\n />\n );\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 rowHeight={35}\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 />\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 { 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":";;;;;;;;;;AAWA,MAAM,YAAY,UAAW,CAAA;AAAA,EAC3B,QAAU,EAAA;AAAA,IACR,KAAO,EAAA,KAAA;AAAA,GACT;AAAA,EACA,QAAU,EAAA;AAAA,IACR,KAAO,EAAA,OAAA;AAAA,GACT;AAAA,EACA,SAAW,EAAA;AAAA,IACT,OAAS,EAAA,MAAA;AAAA,IACT,UAAY,EAAA,QAAA;AAAA,GACd;AACF,CAAC,CAAA,CAAA;AAED,SAAS,aAAgB,GAAA;AACvB,EAAA,2CACG,oBACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,iBAAA;AAAA,IAAA;AAAA,MACC,UAAA,EAAY,EAAE,QAAA,EAAU,oBAAqB,EAAA;AAAA,MAC7C,YAAA,EAAc,EAAE,oBAAA,EAAsB,IAAK,EAAA;AAAA,KAAA;AAAA,GAE/C,CAAA,CAAA;AAEJ,CAAA;AAEO,MAAM,4BAAgE,CAAC,EAAE,OAAS,EAAA,YAAA,EAAc,SAAc,KAAA;AACnH,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAM,MAAA,WAAA,GAAc,WAAY,CAAA,KAAA,CAAM,MAAO,CAAA,CAAC,IAAI,GAAK,EAAA,GAAA,EAAK,GAAG,CAAA,EAAG,GAAI,CAAA,CAAA;AAEtE,EAAM,MAAA,eAAA,GAAkB,CAAC,MAAA,EAA8B,MAA2B,KAAA;AAChF,IAAA,MAAM,QAAQ,MAAO,CAAA,KAAA,CAAA;AACrB,IAAI,IAAA,OAAO,UAAU,QAAU,EAAA;AAC7B,MAAA,MAAM,iBAAiB,MAAO,CAAA,MAAA,KAAW,IAAI,gBAAiB,CAAA,MAAA,CAAO,KAAK,CAAI,GAAA,EAAA,CAAA;AAC9E,MAAM,MAAA,cAAA,GAAiB,YAAY,KAAO,EAAA;AAAA,QACxC,KAAO,EAAA,WAAA;AAAA,QACP,SAAW,EAAA,EAAA;AAAA,QACX,QAAU,EAAA,CAAA;AAAA,OACX,CAAA,CAAA;AACD,MAAI,IAAA,OAAA,CAAQ,SAAS,cAAc,CAAA,IAAK,OAAO,GAAI,CAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,CAAG,EAAA;AAC9E,QAAM,MAAA,IAAA,GAAO,MAAO,CAAA,GAAA,CAAI,OAAQ,CAAA,MAAA,CAAO,KAAK,CAAI,GAAA,MAAA,CAAO,GAAI,CAAA,OAAA,CAAQ,cAAc,CAAA,CAAA;AACjF,QAAM,MAAA,UAAA,GAAa,KAAK,KAAO,CAAA,IAAA,GAAO,OAAO,GAAI,CAAA,OAAA,CAAQ,cAAc,CAAA,GAAK,GAAG,CAAA,CAAA;AAC/E,QAAM,MAAA,IAAA,GAAO,IAAO,GAAA,CAAA,GAAI,GAAM,GAAA,EAAA,CAAA;AAE9B,QAAI,IAAA,UAAA,IAAc,CAAK,IAAA,UAAA,IAAc,CAAI,CAAA,EAAA;AACvC,UAAA,OAAO,CAAI,CAAA,EAAA,cAAc,CAAK,EAAA,EAAA,IAAI,GAAG,UAAU,CAAA,EAAA,CAAA,CAAA;AAAA,SACjD;AAAA,OACF;AACA,MAAA,OAAO,IAAI,cAAc,CAAA,CAAA,CAAA;AAAA,KAC3B;AACA,IAAO,OAAA,GAAA,CAAA;AAAA,GACT,CAAA;AAEA,EAAA,MAAM,UAAwB,EAAC,CAAA;AAC/B,EAAA,IAAI,CAAC,SAAW,EAAA,UAAA,EAAY,SAAS,CAAE,CAAA,QAAA,CAAS,YAAY,CAAG,EAAA;AAC7D,IAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,MACX,KAAO,EAAA,UAAA;AAAA,MACP,UAAY,EAAA,EAAA;AAAA,MACZ,KAAO,EAAA,EAAA;AAAA,MACP,aAAe,EAAA,IAAA;AAAA,MACf,UAAA,EAAY,CAAC,MAAkD,KAAA;AAC7D,QAAO,OAAA,eAAA,CAAgB,MAAO,CAAA,GAAA,CAAI,QAAQ,CAAA,CAAA;AAAA,OAC5C;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAEA,EAAQ,OAAA,CAAA,IAAA;AAAA,IACN;AAAA,MACE,KAAO,EAAA,YAAA;AAAA,MACP,UAAA,EAAY,YAAa,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,MAClD,QAAU,EAAA,GAAA;AAAA,MACV,IAAM,EAAA,CAAA;AAAA,MACN,UAAA,EAAY,CAAC,MAAkD,KAAA;AAC7D,QAAA,IAAI,iBAAiB,MAAO,CAAA,cAAA,CAAA;AAE5B,QAAI,IAAA,YAAA,KAAiB,SAAa,IAAA,YAAA,KAAiB,SAAW,EAAA;AAE5D,UAAI,IAAA,MAAA,CAAO,mBAAmB,KAAa,CAAA,IAAA,MAAA,CAAO,eAAe,OAAQ,CAAA,GAAG,MAAM,CAAI,CAAA,EAAA;AACpF,YAAA,cAAA,GAAiB,MAAO,CAAA,cAAA,CAAe,KAAM,CAAA,GAAG,EAAE,CAAC,CAAA,CAAA;AAAA,WACrD;AAEA,UAAI,IAAA,YAAA,KAAiB,aAAa,cAAgB,EAAA;AAChD,YAAM,MAAA,OAAA,GAAU,mBAAmB,cAAc,CAAA,CAAA;AACjD,YAAA,MAAM,cAAc,OAAQ,CAAA,WAAA,CAAA;AAC5B,YAAA,MAAM,YAAY,OAAQ,CAAA,SAAA,CAAA;AAE1B,YACE,uBAAA,KAAA,CAAA,aAAA,CAAC,6BACE,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,OAAQ,EAAA,SAAA,EAAW,QAAQ,SAC5C,EAAA,EAAA,WACH,mBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,SAAU,EAAA,SAAA,EAAW,QAAQ,SAAW,EAAA,KAAA,EAAM,eAC/D,EAAA,EAAA,SACH,CACF,CAAA,CAAA;AAAA,WAEJ;AAAA,SACF;AAEA,QACE,uBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,OAAQ,EAAA,OAAA,EAAQ,WAAU,KAAM,EAAA,SAAA,EAAW,OAAQ,CAAA,SAAA,EAAA,EAC5D,cACH,CAAA,CAAA;AAAA,OAEJ;AAAA,KACF;AAAA,IACA;AAAA,MACE,KAAO,EAAA,OAAA;AAAA,MACP,UAAY,EAAA,OAAA;AAAA,MACZ,KAAO,EAAA,GAAA;AAAA,MACP,aAAe,EAAA,IAAA;AAAA,MACf,aAAe,EAAA,IAAA;AAAA,MACf,UAAY,EAAA,CAAC,MACX,qBAAA,KAAA,CAAA,aAAA,CAAC,kBAAe,IAAM,EAAA,MAAA,CAAO,KAAQ,GAAA,MAAA,CAAO,KAAM,CAAA,CAAC,CAAI,GAAA,IAAA,EAAM,UAAS,KAAM,EAAA,CAAA;AAAA,MAE9E,WAAA,EAAa,CAAC,CAAA,EAAG,GAAQ,KAAA;AAAA,QACvB,OAAQ,CAAA,GAAA,CAAI,CAAW,MAAA,KAAA,GAAA,CAAI,OAAQ,CAAA,MAAM,CAAM,KAAA,KAAA,CAAA,GAAY,GAAI,CAAA,OAAA,CAAQ,MAAM,CAAA,GAAI,IAAK,CAAA;AAAA,OACxF;AAAA,KACF;AAAA,GACF,CAAA;AAEA,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,MACX,KAAO,EAAA,MAAA;AAAA,MACP,UAAY,EAAA,MAAA;AAAA,MACZ,IAAM,EAAA,QAAA;AAAA,MACN,QAAU,EAAA,GAAA;AAAA,MACV,IAAM,EAAA,CAAA;AAAA,MACN,WAAA,EAAa,CAAC,CAAA,EAAG,GAAQ,KAAA;AACvB,QAAA,OAAO,IAAI,OAAQ,CAAA,MAAM,IAAI,GAAI,CAAA,OAAA,CAAQ,MAAM,CAAI,GAAA,IAAA,CAAA;AAAA,OACrD;AAAA,MACA,UAAA,EAAY,CAAC,MAAkD,KAAA;AAC7D,QAAM,MAAA,cAAA,GAAiB,eAAgB,CAAA,MAAA,EAAQ,MAAM,CAAA,CAAA;AACrD,QAAA,IAAI,SAAY,GAAA,EAAA,CAAA;AAChB,QAAM,MAAA,eAAA,GAAkB,cAAe,CAAA,OAAA,CAAQ,GAAG,CAAA,CAAA;AAClD,QAAA,MAAM,UAAU,eAAoB,KAAA,CAAA,CAAA,GAAK,iBAAiB,cAAe,CAAA,SAAA,CAAU,GAAG,eAAe,CAAA,CAAA;AACrG,QAAA,IAAI,gBAAgB,eAAoB,KAAA,CAAA,CAAA,GAAK,EAAK,GAAA,cAAA,CAAe,UAAU,eAAe,CAAA,CAAA;AAC1F,QAAI,IAAA,aAAA,CAAc,QAAS,CAAA,GAAG,CAAG,EAAA;AAC/B,UAAA,SAAA,GAAY,OAAQ,CAAA,QAAA,CAAA;AACpB,UAAgB,aAAA,GAAA,aAAA,CAAc,OAAQ,CAAA,GAAA,EAAK,QAAG,CAAA,CAAA;AAAA,SACrC,MAAA,IAAA,aAAA,CAAc,QAAS,CAAA,GAAG,CAAG,EAAA;AACtC,UAAA,SAAA,GAAY,OAAQ,CAAA,QAAA,CAAA;AACpB,UAAgB,aAAA,GAAA,aAAA,CAAc,OAAQ,CAAA,GAAA,EAAK,QAAG,CAAA,CAAA;AAAA,SAChD;AAEA,QACE,uBAAA,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,OACjB,EAAA,EAAA,OAAA,kBACA,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,SAAA,EAAU,SAC3B,EAAA,EAAA,aACH,CACF,CAAA,CAAA;AAAA,OAEJ;AAAA,KACD,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AAED,EAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,IACX,KAAO,EAAA,OAAA;AAAA,IACP,UAAY,EAAA,OAAA;AAAA,IACZ,IAAM,EAAA,QAAA;AAAA,IACN,QAAU,EAAA,GAAA;AAAA,IACV,IAAM,EAAA,CAAA;AAAA,IACN,WAAA,EAAa,CAAC,CAAA,EAAG,GAAQ,KAAA;AACvB,MAAA,IAAI,KAAQ,GAAA,CAAA,CAAA;AACZ,MAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,QAAA,KAAA,IAAS,IAAI,OAAQ,CAAA,MAAM,IAAI,GAAI,CAAA,OAAA,CAAQ,MAAM,CAAI,GAAA,CAAA,CAAA;AAAA,OACtD,CAAA,CAAA;AACD,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAAA,IACA,UAAA,EAAY,CAAC,MAAkD,KAAA;AAC7D,MAAA,IAAI,cAAiB,GAAA,GAAA,CAAA;AACrB,MAAI,IAAA,OAAO,MAAO,CAAA,KAAA,KAAU,QAAU,EAAA;AACpC,QAAiB,cAAA,GAAA,CAAA,CAAA,EAAI,WAAY,CAAA,MAAA,CAAO,KAAO,EAAA;AAAA,UAC7C,KAAO,EAAA,WAAA;AAAA,UACP,SAAW,EAAA,EAAA;AAAA,UACX,QAAU,EAAA,CAAA;AAAA,SACX,CAAC,CAAA,CAAA,CAAA;AAAA,OACJ;AACA,MAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,OAAA,EAAA,EAAS,cAAe,CAAA,CAAA;AAAA,KACrD;AAAA,GACD,CAAA,CAAA;AAED,EAAA,2CACG,GACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAM,EAAA,OAAA;AAAA,MACN,OAAA;AAAA,MACA,YAAc,EAAA;AAAA,QACZ,OAAS,EAAA;AAAA,UACP,WAAW,CAAC,EAAE,OAAO,OAAS,EAAA,IAAA,EAAM,QAAQ,CAAA;AAAA,SAC9C;AAAA,QACA,UAAY,EAAA;AAAA,UACV,eAAiB,EAAA;AAAA,YACf,QAAU,EAAA,EAAA;AAAA,WACZ;AAAA,SACF;AAAA,OACF;AAAA,MACA,eAAA,EAAiB,CAAC,EAAE,CAAA;AAAA,MACpB,KAAA,EAAO,EAAE,OAAA,EAAS,aAAc,EAAA;AAAA,MAChC,0BAA0B,EAAA,IAAA;AAAA,MAC1B,iBAAiB,EAAA,IAAA;AAAA,MACjB,OAAA,EAAS,YAAiB,KAAA,SAAA,GAAY,UAAa,GAAA,SAAA;AAAA,KAAA;AAAA,GAEvD,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -9,6 +9,7 @@ import TableHead from '@material-ui/core/TableHead';
|
|
|
9
9
|
import TableRow from '@material-ui/core/TableRow';
|
|
10
10
|
import CancelIcon from '@material-ui/icons/Close';
|
|
11
11
|
import Alert from '@material-ui/lab/Alert';
|
|
12
|
+
import AlertTitle from '@material-ui/lab/AlertTitle';
|
|
12
13
|
import React from 'react';
|
|
13
14
|
|
|
14
15
|
const ErrorsAlertComponent = ({ errors }) => {
|
|
@@ -31,8 +32,8 @@ const ErrorsAlertComponent = ({ errors }) => {
|
|
|
31
32
|
/* @__PURE__ */ React.createElement(CancelIcon, { fontSize: "inherit" })
|
|
32
33
|
)
|
|
33
34
|
},
|
|
34
|
-
/* @__PURE__ */ React.createElement(
|
|
35
|
-
/* @__PURE__ */ React.createElement(TableContainer, { component: Paper }, /* @__PURE__ */ React.createElement(Table, { "aria-label": "errors table" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: "150px" } }, "Account
|
|
35
|
+
/* @__PURE__ */ React.createElement(AlertTitle, null, "InfraWallet failed to fetch data from some accounts. Here is the list of errors."),
|
|
36
|
+
/* @__PURE__ */ React.createElement(TableContainer, { component: Paper }, /* @__PURE__ */ React.createElement(Table, { "aria-label": "errors table" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: "150px" } }, "Account/Integration"), /* @__PURE__ */ React.createElement(TableCell, null, "Error Message"))), /* @__PURE__ */ React.createElement(TableBody, null, errors.map((row) => /* @__PURE__ */ React.createElement(TableRow, { key: row.name }, /* @__PURE__ */ React.createElement(TableCell, null, row.name), /* @__PURE__ */ React.createElement(TableCell, null, row.error))))))
|
|
36
37
|
));
|
|
37
38
|
};
|
|
38
39
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorsAlertComponent.esm.js","sources":["../../../src/components/ErrorsAlertComponent/ErrorsAlertComponent.tsx"],"sourcesContent":["import Collapse from '@material-ui/core/Collapse';\nimport IconButton from '@material-ui/core/IconButton';\nimport Paper from '@material-ui/core/Paper';\nimport Table from '@material-ui/core/Table';\nimport TableBody from '@material-ui/core/TableBody';\nimport TableCell from '@material-ui/core/TableCell';\nimport TableContainer from '@material-ui/core/TableContainer';\nimport TableHead from '@material-ui/core/TableHead';\nimport TableRow from '@material-ui/core/TableRow';\nimport CloseIcon from '@material-ui/icons/Close';\nimport Alert from '@material-ui/lab/Alert';\nimport React, { FC } from 'react';\nimport { CloudProviderError } from '../../api/types';\n\nexport const ErrorsAlertComponent: FC<{ errors: CloudProviderError[] }> = ({ errors }) => {\n const [open, setOpen] = React.useState(true);\n\n return (\n <Collapse in={open}>\n <Alert\n severity=\"warning\"\n style={{ maxHeight: '300px', overflow: 'auto' }}\n action={\n <IconButton\n aria-label=\"close\"\n color=\"inherit\"\n size=\"small\"\n onClick={() => {\n setOpen(false);\n }}\n >\n <CloseIcon fontSize=\"inherit\" />\n </IconButton>\n }\n >\n <
|
|
1
|
+
{"version":3,"file":"ErrorsAlertComponent.esm.js","sources":["../../../src/components/ErrorsAlertComponent/ErrorsAlertComponent.tsx"],"sourcesContent":["import Collapse from '@material-ui/core/Collapse';\nimport IconButton from '@material-ui/core/IconButton';\nimport Paper from '@material-ui/core/Paper';\nimport Table from '@material-ui/core/Table';\nimport TableBody from '@material-ui/core/TableBody';\nimport TableCell from '@material-ui/core/TableCell';\nimport TableContainer from '@material-ui/core/TableContainer';\nimport TableHead from '@material-ui/core/TableHead';\nimport TableRow from '@material-ui/core/TableRow';\nimport CloseIcon from '@material-ui/icons/Close';\nimport Alert from '@material-ui/lab/Alert';\nimport AlertTitle from '@material-ui/lab/AlertTitle';\nimport React, { FC } from 'react';\nimport { CloudProviderError } from '../../api/types';\n\nexport const ErrorsAlertComponent: FC<{ errors: CloudProviderError[] }> = ({ errors }) => {\n const [open, setOpen] = React.useState(true);\n\n return (\n <Collapse in={open}>\n <Alert\n severity=\"warning\"\n style={{ maxHeight: '300px', overflow: 'auto' }}\n action={\n <IconButton\n aria-label=\"close\"\n color=\"inherit\"\n size=\"small\"\n onClick={() => {\n setOpen(false);\n }}\n >\n <CloseIcon fontSize=\"inherit\" />\n </IconButton>\n }\n >\n <AlertTitle>InfraWallet failed to fetch data from some accounts. Here is the list of errors.</AlertTitle>\n <TableContainer component={Paper}>\n <Table aria-label=\"errors table\">\n <TableHead>\n <TableRow>\n <TableCell style={{ minWidth: '150px' }}>Account/Integration</TableCell>\n <TableCell>Error Message</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {errors.map(row => (\n <TableRow key={row.name}>\n <TableCell>{row.name}</TableCell>\n <TableCell>{row.error}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Alert>\n </Collapse>\n );\n};\n"],"names":["CloseIcon"],"mappings":";;;;;;;;;;;;;;AAeO,MAAM,oBAA6D,GAAA,CAAC,EAAE,MAAA,EAAa,KAAA;AACxF,EAAA,MAAM,CAAC,IAAM,EAAA,OAAO,CAAI,GAAA,KAAA,CAAM,SAAS,IAAI,CAAA,CAAA;AAE3C,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,QAAS,EAAA,EAAA,EAAA,EAAI,IACZ,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,QAAS,EAAA,SAAA;AAAA,MACT,KAAO,EAAA,EAAE,SAAW,EAAA,OAAA,EAAS,UAAU,MAAO,EAAA;AAAA,MAC9C,MACE,kBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,YAAW,EAAA,OAAA;AAAA,UACX,KAAM,EAAA,SAAA;AAAA,UACN,IAAK,EAAA,OAAA;AAAA,UACL,SAAS,MAAM;AACb,YAAA,OAAA,CAAQ,KAAK,CAAA,CAAA;AAAA,WACf;AAAA,SAAA;AAAA,wBAEA,KAAA,CAAA,aAAA,CAACA,UAAU,EAAA,EAAA,QAAA,EAAS,SAAU,EAAA,CAAA;AAAA,OAChC;AAAA,KAAA;AAAA,oBAGF,KAAA,CAAA,aAAA,CAAC,kBAAW,kFAAgF,CAAA;AAAA,oBAC5F,KAAA,CAAA,aAAA,CAAC,kBAAe,SAAW,EAAA,KAAA,EAAA,sCACxB,KAAM,EAAA,EAAA,YAAA,EAAW,cAChB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,SACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,gCACE,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,EAAU,KAAO,EAAA,EAAE,QAAU,EAAA,OAAA,MAAW,qBAAmB,CAAA,kBAC3D,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,IAAA,EAAU,eAAa,CAC1B,CACF,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,iBACE,MAAO,CAAA,GAAA,CAAI,yBACT,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAS,GAAK,EAAA,GAAA,CAAI,IACjB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,iBAAW,GAAI,CAAA,IAAK,CACrB,kBAAA,KAAA,CAAA,aAAA,CAAC,SAAW,EAAA,IAAA,EAAA,GAAA,CAAI,KAAM,CACxB,CACD,CACH,CACF,CACF,CAAA;AAAA,GAEJ,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Tooltip, Grid, FormControl, Typography, Divider, CircularProgress, Chip } from '@material-ui/core';
|
|
2
2
|
import Button from '@material-ui/core/Button';
|
|
3
3
|
import Checkbox from '@material-ui/core/Checkbox';
|
|
4
4
|
import TextField from '@material-ui/core/TextField';
|
|
5
|
-
import { makeStyles } from '@material-ui/core/styles';
|
|
5
|
+
import { makeStyles, withStyles } from '@material-ui/core/styles';
|
|
6
6
|
import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
|
7
7
|
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
|
|
8
8
|
import Autocomplete from '@material-ui/lab/Autocomplete';
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import { getReportKeyAndValues } from '../../api/functions.esm.js';
|
|
9
|
+
import React, { useState, useEffect } from 'react';
|
|
10
|
+
import { getReportKeyAndValues, extractProvider, extractAccountInfo, tagExists } from '../../api/functions.esm.js';
|
|
11
|
+
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
12
|
+
import { infraWalletApiRef } from '../../api/InfraWalletApi.esm.js';
|
|
13
|
+
import { getProviderIcon } from '../ProviderIcons/ProviderIcons.esm.js';
|
|
11
14
|
|
|
12
15
|
const useStyles = makeStyles((theme) => ({
|
|
13
16
|
formControl: {
|
|
@@ -18,25 +21,228 @@ const useStyles = makeStyles((theme) => ({
|
|
|
18
21
|
}));
|
|
19
22
|
const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
|
|
20
23
|
const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
|
|
21
|
-
const
|
|
24
|
+
const HtmlTooltip = withStyles((theme) => ({
|
|
25
|
+
tooltip: {
|
|
26
|
+
backgroundColor: "#f5f5f9",
|
|
27
|
+
color: "rgba(0, 0, 0, 0.87)",
|
|
28
|
+
maxWidth: 400,
|
|
29
|
+
fontSize: theme.typography.pxToRem(14),
|
|
30
|
+
border: "1px solid #dadde9"
|
|
31
|
+
}
|
|
32
|
+
}))(Tooltip);
|
|
33
|
+
const FiltersComponent = ({
|
|
34
|
+
reports,
|
|
35
|
+
filters,
|
|
36
|
+
monthRange,
|
|
37
|
+
filtersSetter,
|
|
38
|
+
selectedTagsSetter,
|
|
39
|
+
providerErrorsSetter
|
|
40
|
+
}) => {
|
|
22
41
|
const classes = useStyles();
|
|
23
42
|
const keyValues = getReportKeyAndValues(reports);
|
|
43
|
+
const [tagProviders, _setTagProviders] = useState(["AWS", "Azure"]);
|
|
44
|
+
const [tagProvider, setTagProvider] = useState();
|
|
45
|
+
const [openTagKey, setOpenTagKey] = useState(false);
|
|
46
|
+
const [tagKeys, setTagKeys] = useState([]);
|
|
47
|
+
const [selectedTagKey, setSelectedTagKey] = useState(null);
|
|
48
|
+
const [resetTagKeys, setResetTagKeys] = useState(false);
|
|
49
|
+
const loadingTagKeys = openTagKey && tagKeys.length === 0;
|
|
50
|
+
const [openTagValue, setOpenTagValue] = useState(false);
|
|
51
|
+
const [tagValues, setTagValues] = useState([]);
|
|
52
|
+
const [resetTagValues, setResetTagValues] = useState(false);
|
|
53
|
+
const loadingTagValues = openTagValue && tagValues.length === 0;
|
|
54
|
+
const [selectedTags, setSelectedTags] = useState([]);
|
|
55
|
+
const infraWalletApi = useApi(infraWalletApiRef);
|
|
56
|
+
const alertApi = useApi(alertApiRef);
|
|
24
57
|
const handleFiltersChange = (key, newValue) => {
|
|
25
58
|
filtersSetter({ ...filters, [key]: newValue });
|
|
26
59
|
};
|
|
27
|
-
|
|
60
|
+
const handleTagProviderChange = (provider) => {
|
|
61
|
+
setTagProvider("");
|
|
62
|
+
setOpenTagKey(false);
|
|
63
|
+
setResetTagKeys((prev) => !prev);
|
|
64
|
+
setTagKeys([]);
|
|
65
|
+
setSelectedTagKey(null);
|
|
66
|
+
setOpenTagValue(false);
|
|
67
|
+
setTagValues([]);
|
|
68
|
+
if (provider) {
|
|
69
|
+
setTagProvider(provider);
|
|
70
|
+
setOpenTagKey(true);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const handleTagKeyChange = (tagKey) => {
|
|
74
|
+
setSelectedTagKey(null);
|
|
75
|
+
setTagValues([]);
|
|
76
|
+
setResetTagValues((prev) => !prev);
|
|
77
|
+
if (typeof tagKey === "string") {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (tagKey) {
|
|
81
|
+
setSelectedTagKey(tagKey);
|
|
82
|
+
setOpenTagValue(true);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const handleTagValueSelection = (tag) => {
|
|
86
|
+
if (typeof tag === "string") {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (tag && !tagExists(selectedTags, tag)) {
|
|
90
|
+
setResetTagValues((prev) => !prev);
|
|
91
|
+
setSelectedTags([...selectedTags, tag]);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const handleDeleteTag = (tagToDelete) => () => {
|
|
95
|
+
setSelectedTags(
|
|
96
|
+
selectedTags.filter(
|
|
97
|
+
(tag) => !(tag.provider === tagToDelete.provider && tag.key === tagToDelete.key && tag.value === tagToDelete.value)
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!loadingTagKeys) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
(async () => {
|
|
106
|
+
if (tagProvider) {
|
|
107
|
+
await infraWalletApi.getTagKeys(tagProvider, monthRange.startMonth, monthRange.endMonth).then((response) => {
|
|
108
|
+
if (response.data && response.data.length > 0) {
|
|
109
|
+
setTagKeys(response.data);
|
|
110
|
+
}
|
|
111
|
+
if (response.status === 207 && response.errors) {
|
|
112
|
+
providerErrorsSetter(response.errors);
|
|
113
|
+
}
|
|
114
|
+
}).catch((e) => alertApi.post({ message: `${e.message}`, severity: "error" }));
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
}, [loadingTagKeys, tagProvider, monthRange, infraWalletApi, alertApi, providerErrorsSetter]);
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!loadingTagValues) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
(async () => {
|
|
123
|
+
if (selectedTagKey) {
|
|
124
|
+
await infraWalletApi.getTagValues(selectedTagKey, monthRange.startMonth, monthRange.endMonth).then((response) => {
|
|
125
|
+
if (response.data && response.data.length > 0) {
|
|
126
|
+
setTagValues(response.data);
|
|
127
|
+
}
|
|
128
|
+
if (response.status === 207 && response.errors) {
|
|
129
|
+
providerErrorsSetter(response.errors);
|
|
130
|
+
}
|
|
131
|
+
}).catch((e) => alertApi.post({ message: `${e.message}`, severity: "error" }));
|
|
132
|
+
}
|
|
133
|
+
})();
|
|
134
|
+
}, [loadingTagValues, selectedTagKey, monthRange, infraWalletApi, alertApi, providerErrorsSetter]);
|
|
135
|
+
return /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, Object.keys(keyValues).map((key) => /* @__PURE__ */ React.createElement(FormControl, { className: classes.formControl, key: `form-${key}` }, /* @__PURE__ */ React.createElement(
|
|
28
136
|
Autocomplete,
|
|
29
137
|
{
|
|
30
138
|
multiple: true,
|
|
31
139
|
id: `checkboxes-${key}`,
|
|
32
|
-
options: keyValues[key],
|
|
140
|
+
options: keyValues[key].sort(),
|
|
33
141
|
value: filters[key] || [],
|
|
34
142
|
onChange: (_event, value, _reason) => handleFiltersChange(key, value),
|
|
35
143
|
disableCloseOnSelect: true,
|
|
36
|
-
renderOption: (option, { selected }) =>
|
|
37
|
-
|
|
144
|
+
renderOption: (option, { selected }) => {
|
|
145
|
+
let provider = void 0;
|
|
146
|
+
let providerIcon = void 0;
|
|
147
|
+
let accountName = void 0;
|
|
148
|
+
let accountId = void 0;
|
|
149
|
+
if (key === "provider") {
|
|
150
|
+
provider = option;
|
|
151
|
+
providerIcon = getProviderIcon(provider);
|
|
152
|
+
} else if (["account", "service"].includes(key)) {
|
|
153
|
+
provider = extractProvider(option);
|
|
154
|
+
providerIcon = getProviderIcon(provider);
|
|
155
|
+
}
|
|
156
|
+
if (key === "account") {
|
|
157
|
+
const account = extractAccountInfo(option.replace(`${provider}/`, ""));
|
|
158
|
+
accountName = account.accountName;
|
|
159
|
+
accountId = account.accountId;
|
|
160
|
+
}
|
|
161
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: `option-${option}` }, /* @__PURE__ */ React.createElement(Checkbox, { icon, checkedIcon, style: { marginRight: 8 }, checked: selected }), providerIcon && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, null, providerIcon), "\xA0\xA0"), key === "account" ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, accountName), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, accountId)) : /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, option.replace(`${provider}/`, "")));
|
|
162
|
+
},
|
|
163
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "standard", label: key.charAt(0).toUpperCase() + key.slice(1) })
|
|
164
|
+
}
|
|
165
|
+
))), /* @__PURE__ */ React.createElement(FormControl, { style: { marginTop: 10 } }, /* @__PURE__ */ React.createElement(Button, { variant: "contained", color: "primary", onClick: () => filtersSetter({}) }, "Clear filters"))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(FormControl, { className: classes.formControl }, /* @__PURE__ */ React.createElement(
|
|
166
|
+
Autocomplete,
|
|
167
|
+
{
|
|
168
|
+
id: "tag-providers",
|
|
169
|
+
options: tagProviders,
|
|
170
|
+
onChange: (_, provider) => handleTagProviderChange(provider),
|
|
171
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "standard", label: "Tag provider" }),
|
|
172
|
+
renderOption: (option) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, option))
|
|
173
|
+
}
|
|
174
|
+
)), /* @__PURE__ */ React.createElement(FormControl, { className: classes.formControl }, /* @__PURE__ */ React.createElement(
|
|
175
|
+
Autocomplete,
|
|
176
|
+
{
|
|
177
|
+
id: "tag-keys",
|
|
178
|
+
key: String(resetTagKeys),
|
|
179
|
+
freeSolo: true,
|
|
180
|
+
disabled: tagProvider ? false : true,
|
|
181
|
+
open: openTagKey,
|
|
182
|
+
onOpen: () => setOpenTagKey(true),
|
|
183
|
+
onClose: () => setOpenTagKey(false),
|
|
184
|
+
options: tagKeys,
|
|
185
|
+
getOptionLabel: (tag) => tag.key,
|
|
186
|
+
loading: loadingTagKeys,
|
|
187
|
+
onChange: (_, tagKey) => handleTagKeyChange(tagKey),
|
|
188
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
189
|
+
TextField,
|
|
190
|
+
{
|
|
191
|
+
...params,
|
|
192
|
+
variant: "standard",
|
|
193
|
+
label: "Tag keys",
|
|
194
|
+
InputProps: {
|
|
195
|
+
...params.InputProps,
|
|
196
|
+
endAdornment: /* @__PURE__ */ React.createElement(React.Fragment, null, loadingTagKeys ? /* @__PURE__ */ React.createElement(CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps && params.InputProps.endAdornment)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
renderOption: (option) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, option.key))
|
|
201
|
+
}
|
|
202
|
+
)), /* @__PURE__ */ React.createElement(FormControl, { className: classes.formControl }, /* @__PURE__ */ React.createElement(
|
|
203
|
+
Autocomplete,
|
|
204
|
+
{
|
|
205
|
+
id: "tag-values",
|
|
206
|
+
key: String(resetTagValues),
|
|
207
|
+
freeSolo: true,
|
|
208
|
+
disabled: selectedTagKey ? false : true,
|
|
209
|
+
open: openTagValue,
|
|
210
|
+
onOpen: () => setOpenTagValue(true),
|
|
211
|
+
onClose: () => setOpenTagValue(false),
|
|
212
|
+
options: tagValues,
|
|
213
|
+
getOptionLabel: (tag) => (tag == null ? void 0 : tag.value) || "",
|
|
214
|
+
getOptionDisabled: (tag) => tagExists(selectedTags, tag),
|
|
215
|
+
loading: loadingTagValues,
|
|
216
|
+
onChange: (_, tag) => handleTagValueSelection(tag),
|
|
217
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
218
|
+
TextField,
|
|
219
|
+
{
|
|
220
|
+
...params,
|
|
221
|
+
variant: "standard",
|
|
222
|
+
label: "Tag values",
|
|
223
|
+
InputProps: {
|
|
224
|
+
...params.InputProps,
|
|
225
|
+
endAdornment: /* @__PURE__ */ React.createElement(React.Fragment, null, loadingTagValues ? /* @__PURE__ */ React.createElement(CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps && params.InputProps.endAdornment)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
),
|
|
229
|
+
renderOption: (option) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, option.value))
|
|
230
|
+
}
|
|
231
|
+
)), /* @__PURE__ */ React.createElement(FormControl, { style: { marginTop: 10 } }, /* @__PURE__ */ React.createElement(
|
|
232
|
+
HtmlTooltip,
|
|
233
|
+
{
|
|
234
|
+
title: /* @__PURE__ */ React.createElement(React.Fragment, null, "You can apply ", /* @__PURE__ */ React.createElement("b", null, "tags"), " to ", /* @__PURE__ */ React.createElement("em", null, "one or more"), " providers. ", /* @__PURE__ */ React.createElement("b", null, "Tags"), " will only filter the costs for the ", /* @__PURE__ */ React.createElement("b", null, "selected providers"), ", while ", /* @__PURE__ */ React.createElement("em", null, "others remain unchanged"), ".")
|
|
235
|
+
},
|
|
236
|
+
/* @__PURE__ */ React.createElement(Button, { variant: "contained", color: "primary", onClick: () => selectedTagsSetter(selectedTags) }, "Apply")
|
|
237
|
+
))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, selectedTags.map((tag) => /* @__PURE__ */ React.createElement(
|
|
238
|
+
Chip,
|
|
239
|
+
{
|
|
240
|
+
size: "small",
|
|
241
|
+
key: `${tag.provider}/${tag.key}=${tag.value}`,
|
|
242
|
+
label: `${tag.provider}/${tag.key}=${tag.value}`,
|
|
243
|
+
onDelete: handleDeleteTag(tag)
|
|
38
244
|
}
|
|
39
|
-
)))
|
|
245
|
+
))));
|
|
40
246
|
};
|
|
41
247
|
|
|
42
248
|
export { FiltersComponent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FiltersComponent.esm.js","sources":["../../../src/components/FiltersComponent/FiltersComponent.tsx"],"sourcesContent":["import { Box, FormControl } from '@material-ui/core';\nimport Button from '@material-ui/core/Button';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport TextField from '@material-ui/core/TextField';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React, { FC } from 'react';\nimport { getReportKeyAndValues } from '../../api/functions';\nimport { FiltersComponentProps } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n formControl: {\n marginLeft: theme.spacing(1),\n marginRight: theme.spacing(3),\n width: 300,\n },\n}));\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const FiltersComponent: FC<FiltersComponentProps> = ({ reports, filters, filtersSetter }) => {\n const classes = useStyles();\n const keyValues: { [key: string]: string[] } = getReportKeyAndValues(reports);\n const handleFiltersChange = (key: string, newValue: string[]): void => {\n filtersSetter({ ...filters, [key]: newValue });\n };\n\n return (\n <Box>\n {Object.keys(keyValues).map(key => (\n <FormControl className={classes.formControl} key={`form-${key}`}>\n <Autocomplete\n multiple\n id={`checkboxes-${key}`}\n options={keyValues[key]}\n value={filters[key] || []}\n onChange={(_event, value: string[], _reason) => handleFiltersChange(key, value)}\n disableCloseOnSelect\n renderOption={(option, { selected }) => (\n <React.Fragment key={`option-${option}`}>\n <Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />\n {option}\n </React.Fragment>\n )}\n renderInput={params => <TextField {...params} variant=\"standard\" label={key} />}\n />\n </FormControl>\n ))}\n <FormControl className={classes.formControl} style={{ marginTop: 10 }}>\n <Button variant=\"contained\" color=\"primary\" onClick={() => filtersSetter({})}>\n Clear filters\n </Button>\n </FormControl>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAYA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,WAAa,EAAA;AAAA,IACX,UAAA,EAAY,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC3B,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC5B,KAAO,EAAA,GAAA;AAAA,GACT;AACF,CAAE,CAAA,CAAA,CAAA;AACF,MAAM,IAAO,mBAAA,KAAA,CAAA,aAAA,CAAC,wBAAyB,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA,CAAA,CAAA;AACxD,MAAM,WAAc,mBAAA,KAAA,CAAA,aAAA,CAAC,YAAa,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA,CAAA,CAAA;AAE5C,MAAM,mBAA8C,CAAC,EAAE,OAAS,EAAA,OAAA,EAAS,eAAoB,KAAA;AAClG,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAM,MAAA,SAAA,GAAyC,sBAAsB,OAAO,CAAA,CAAA;AAC5E,EAAM,MAAA,mBAAA,GAAsB,CAAC,GAAA,EAAa,QAA6B,KAAA;AACrE,IAAA,aAAA,CAAc,EAAE,GAAG,OAAA,EAAS,CAAC,GAAG,GAAG,UAAU,CAAA,CAAA;AAAA,GAC/C,CAAA;AAEA,EAAA,2CACG,GACE,EAAA,IAAA,EAAA,MAAA,CAAO,IAAK,CAAA,SAAS,EAAE,GAAI,CAAA,CAAA,GAAA,qBACzB,KAAA,CAAA,aAAA,CAAA,WAAA,EAAA,EAAY,WAAW,OAAQ,CAAA,WAAA,EAAa,GAAK,EAAA,CAAA,KAAA,EAAQ,GAAG,CAC3D,CAAA,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,QAAQ,EAAA,IAAA;AAAA,MACR,EAAA,EAAI,cAAc,GAAG,CAAA,CAAA;AAAA,MACrB,OAAA,EAAS,UAAU,GAAG,CAAA;AAAA,MACtB,KAAO,EAAA,OAAA,CAAQ,GAAG,CAAA,IAAK,EAAC;AAAA,MACxB,UAAU,CAAC,MAAA,EAAQ,OAAiB,OAAY,KAAA,mBAAA,CAAoB,KAAK,KAAK,CAAA;AAAA,MAC9E,oBAAoB,EAAA,IAAA;AAAA,MACpB,YAAA,EAAc,CAAC,MAAA,EAAQ,EAAE,QAAA,EACvB,qBAAA,KAAA,CAAA,aAAA,CAAC,KAAM,CAAA,QAAA,EAAN,EAAe,GAAA,EAAK,CAAU,OAAA,EAAA,MAAM,sBAClC,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAS,IAAY,EAAA,WAAA,EAA0B,KAAO,EAAA,EAAE,WAAa,EAAA,CAAA,EAAK,EAAA,OAAA,EAAS,QAAU,EAAA,CAAA,EAC7F,MACH,CAAA;AAAA,MAEF,WAAA,EAAa,4BAAW,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,EAAW,GAAG,MAAQ,EAAA,OAAA,EAAQ,UAAW,EAAA,KAAA,EAAO,GAAK,EAAA,CAAA;AAAA,KAAA;AAAA,GAEjF,CACD,CAAA,kBACA,KAAA,CAAA,aAAA,CAAA,WAAA,EAAA,EAAY,SAAW,EAAA,OAAA,CAAQ,WAAa,EAAA,KAAA,EAAO,EAAE,SAAA,EAAW,EAAG,EAAA,EAAA,kBACjE,KAAA,CAAA,aAAA,CAAA,MAAA,EAAA,EAAO,OAAQ,EAAA,WAAA,EAAY,KAAM,EAAA,SAAA,EAAU,OAAS,EAAA,MAAM,aAAc,CAAA,EAAE,CAAA,EAAA,EAAG,eAE9E,CACF,CACF,CAAA,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"FiltersComponent.esm.js","sources":["../../../src/components/FiltersComponent/FiltersComponent.tsx"],"sourcesContent":["import { Chip, CircularProgress, Divider, FormControl, Grid, Tooltip, Typography } from '@material-ui/core';\nimport Button from '@material-ui/core/Button';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport TextField from '@material-ui/core/TextField';\nimport { Theme, makeStyles, withStyles } from '@material-ui/core/styles';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React, { FC, useEffect, useState } from 'react';\nimport { extractAccountInfo, extractProvider, getReportKeyAndValues, tagExists } from '../../api/functions';\nimport { FiltersComponentProps } from '../types';\nimport { Tag } from '../../api/types';\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { getProviderIcon } from '../ProviderIcons';\n\nconst useStyles = makeStyles(theme => ({\n formControl: {\n marginLeft: theme.spacing(1),\n marginRight: theme.spacing(3),\n width: 300,\n },\n}));\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nconst HtmlTooltip = withStyles((theme: Theme) => ({\n tooltip: {\n backgroundColor: '#f5f5f9',\n color: 'rgba(0, 0, 0, 0.87)',\n maxWidth: 400,\n fontSize: theme.typography.pxToRem(14),\n border: '1px solid #dadde9',\n },\n}))(Tooltip);\n\nexport const FiltersComponent: FC<FiltersComponentProps> = ({\n reports,\n filters,\n monthRange,\n filtersSetter,\n selectedTagsSetter,\n providerErrorsSetter,\n}) => {\n const classes = useStyles();\n const keyValues: { [key: string]: string[] } = getReportKeyAndValues(reports);\n\n // tag providers\n const [tagProviders, _setTagProviders] = useState<string[]>(['AWS', 'Azure']);\n const [tagProvider, setTagProvider] = useState<string>();\n\n // tag keys\n const [openTagKey, setOpenTagKey] = useState(false);\n const [tagKeys, setTagKeys] = useState<Tag[]>([]);\n const [selectedTagKey, setSelectedTagKey] = useState<Tag | null>(null);\n const [resetTagKeys, setResetTagKeys] = useState(false);\n const loadingTagKeys = openTagKey && tagKeys.length === 0;\n\n // tag values\n const [openTagValue, setOpenTagValue] = useState(false);\n const [tagValues, setTagValues] = useState<Tag[]>([]);\n const [resetTagValues, setResetTagValues] = useState(false);\n const loadingTagValues = openTagValue && tagValues.length === 0;\n\n // user selected tags\n const [selectedTags, setSelectedTags] = useState<Tag[]>([]);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n const alertApi = useApi(alertApiRef);\n\n const handleFiltersChange = (key: string, newValue: string[]): void => {\n filtersSetter({ ...filters, [key]: newValue });\n };\n\n const handleTagProviderChange = (provider: string | null) => {\n setTagProvider('');\n setOpenTagKey(false);\n setResetTagKeys(prev => !prev);\n setTagKeys([]);\n setSelectedTagKey(null);\n setOpenTagValue(false);\n setTagValues([]);\n\n if (provider) {\n setTagProvider(provider);\n setOpenTagKey(true);\n }\n };\n\n const handleTagKeyChange = (tagKey: Tag | string | null) => {\n setSelectedTagKey(null);\n setTagValues([]);\n setResetTagValues(prev => !prev);\n\n if (typeof tagKey === 'string') {\n return;\n }\n\n if (tagKey) {\n setSelectedTagKey(tagKey);\n setOpenTagValue(true);\n }\n };\n\n const handleTagValueSelection = (tag: Tag | string | null) => {\n if (typeof tag === 'string') {\n return;\n }\n\n if (tag && !tagExists(selectedTags, tag)) {\n setResetTagValues(prev => !prev);\n setSelectedTags([...selectedTags, tag]);\n }\n };\n\n const handleDeleteTag = (tagToDelete: Tag) => () => {\n setSelectedTags(\n selectedTags.filter(\n tag =>\n !(tag.provider === tagToDelete.provider && tag.key === tagToDelete.key && tag.value === tagToDelete.value),\n ),\n );\n };\n\n useEffect(() => {\n if (!loadingTagKeys) {\n return;\n }\n\n (async () => {\n if (tagProvider) {\n await infraWalletApi\n .getTagKeys(tagProvider, monthRange.startMonth, monthRange.endMonth)\n .then(response => {\n if (response.data && response.data.length > 0) {\n setTagKeys(response.data);\n }\n if (response.status === 207 && response.errors) {\n providerErrorsSetter(response.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }\n })();\n }, [loadingTagKeys, tagProvider, monthRange, infraWalletApi, alertApi, providerErrorsSetter]);\n\n useEffect(() => {\n if (!loadingTagValues) {\n return;\n }\n\n (async () => {\n if (selectedTagKey) {\n await infraWalletApi\n .getTagValues(selectedTagKey, monthRange.startMonth, monthRange.endMonth)\n .then(response => {\n if (response.data && response.data.length > 0) {\n setTagValues(response.data);\n }\n if (response.status === 207 && response.errors) {\n providerErrorsSetter(response.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }\n })();\n }, [loadingTagValues, selectedTagKey, monthRange, infraWalletApi, alertApi, providerErrorsSetter]);\n\n return (\n <Grid container>\n <Grid item xs={12}>\n {Object.keys(keyValues).map(key => (\n <FormControl className={classes.formControl} key={`form-${key}`}>\n <Autocomplete\n multiple\n id={`checkboxes-${key}`}\n options={keyValues[key].sort()}\n value={filters[key] || []}\n onChange={(_event, value: string[], _reason) => handleFiltersChange(key, value)}\n disableCloseOnSelect\n renderOption={(option, { selected }) => {\n let provider = undefined;\n let providerIcon = undefined;\n let accountName = undefined;\n let accountId = undefined;\n if (key === 'provider') {\n provider = option;\n providerIcon = getProviderIcon(provider);\n } else if (['account', 'service'].includes(key)) {\n provider = extractProvider(option);\n providerIcon = getProviderIcon(provider);\n }\n\n if (key === 'account') {\n const account = extractAccountInfo(option.replace(`${provider}/`, ''));\n accountName = account.accountName;\n accountId = account.accountId;\n }\n\n return (\n <React.Fragment key={`option-${option}`}>\n <Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />\n {providerIcon && (\n <>\n <Typography>{providerIcon}</Typography>\n \n </>\n )}\n {key === 'account' ? (\n <div>\n <Typography variant=\"body2\">{accountName}</Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {accountId}\n </Typography>\n </div>\n ) : (\n <Typography variant=\"body2\">{option.replace(`${provider}/`, '')}</Typography>\n )}\n </React.Fragment>\n );\n }}\n renderInput={params => (\n <TextField {...params} variant=\"standard\" label={key.charAt(0).toUpperCase() + key.slice(1)} />\n )}\n />\n </FormControl>\n ))}\n <FormControl style={{ marginTop: 10 }}>\n <Button variant=\"contained\" color=\"primary\" onClick={() => filtersSetter({})}>\n Clear filters\n </Button>\n </FormControl>\n </Grid>\n <Grid item xs={12}>\n <Divider />\n </Grid>\n <Grid item xs={12}>\n <FormControl className={classes.formControl}>\n <Autocomplete\n id=\"tag-providers\"\n options={tagProviders}\n onChange={(_, provider) => handleTagProviderChange(provider)}\n renderInput={params => <TextField {...params} variant=\"standard\" label=\"Tag provider\" />}\n renderOption={option => (\n <React.Fragment>\n <Typography variant=\"body2\">{option}</Typography>\n </React.Fragment>\n )}\n />\n </FormControl>\n <FormControl className={classes.formControl}>\n <Autocomplete\n id=\"tag-keys\"\n key={String(resetTagKeys)}\n freeSolo\n disabled={tagProvider ? false : true}\n open={openTagKey}\n onOpen={() => setOpenTagKey(true)}\n onClose={() => setOpenTagKey(false)}\n options={tagKeys}\n getOptionLabel={tag => tag.key}\n loading={loadingTagKeys}\n onChange={(_, tagKey) => handleTagKeyChange(tagKey)}\n renderInput={params => (\n <TextField\n {...params}\n variant=\"standard\"\n label=\"Tag keys\"\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <React.Fragment>\n {loadingTagKeys ? <CircularProgress color=\"inherit\" size={20} /> : null}\n {params.InputProps && params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n />\n )}\n renderOption={option => (\n <React.Fragment>\n <Typography variant=\"body2\">{option.key}</Typography>\n </React.Fragment>\n )}\n />\n </FormControl>\n <FormControl className={classes.formControl}>\n <Autocomplete\n id=\"tag-values\"\n key={String(resetTagValues)}\n freeSolo\n disabled={selectedTagKey ? false : true}\n open={openTagValue}\n onOpen={() => setOpenTagValue(true)}\n onClose={() => setOpenTagValue(false)}\n options={tagValues}\n getOptionLabel={tag => tag?.value || ''}\n getOptionDisabled={tag => tagExists(selectedTags, tag)}\n loading={loadingTagValues}\n onChange={(_, tag) => handleTagValueSelection(tag)}\n renderInput={params => (\n <TextField\n {...params}\n variant=\"standard\"\n label=\"Tag values\"\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <React.Fragment>\n {loadingTagValues ? <CircularProgress color=\"inherit\" size={20} /> : null}\n {params.InputProps && params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n />\n )}\n renderOption={option => (\n <React.Fragment>\n <Typography variant=\"body2\">{option.value}</Typography>\n </React.Fragment>\n )}\n />\n </FormControl>\n <FormControl style={{ marginTop: 10 }}>\n <HtmlTooltip\n title={\n <React.Fragment>\n You can apply <b>tags</b> to <em>one or more</em> providers. <b>Tags</b> will only filter the costs for\n the <b>selected providers</b>, while <em>others remain unchanged</em>.\n </React.Fragment>\n }\n >\n <Button variant=\"contained\" color=\"primary\" onClick={() => selectedTagsSetter(selectedTags)}>\n Apply\n </Button>\n </HtmlTooltip>\n </FormControl>\n </Grid>\n <Grid item xs={12}>\n {selectedTags.map(tag => (\n <Chip\n size=\"small\"\n key={`${tag.provider}/${tag.key}=${tag.value}`}\n label={`${tag.provider}/${tag.key}=${tag.value}`}\n onDelete={handleDeleteTag(tag)}\n />\n ))}\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,7 @@ const MetricConfigurationComponent = ({ wallet }) => {
|
|
|
32
32
|
config_name: "",
|
|
33
33
|
metric_name: "",
|
|
34
34
|
description: "",
|
|
35
|
+
group: "",
|
|
35
36
|
query: "",
|
|
36
37
|
isNew: true
|
|
37
38
|
}
|
|
@@ -142,6 +143,12 @@ const MetricConfigurationComponent = ({ wallet }) => {
|
|
|
142
143
|
width: 220,
|
|
143
144
|
editable: !readOnly
|
|
144
145
|
},
|
|
146
|
+
{
|
|
147
|
+
field: "group",
|
|
148
|
+
headerName: "Group",
|
|
149
|
+
width: 100,
|
|
150
|
+
editable: !readOnly
|
|
151
|
+
},
|
|
145
152
|
{
|
|
146
153
|
field: "query",
|
|
147
154
|
headerName: "Query",
|