@electrolux-oss/plugin-infrawallet 0.2.0 → 0.2.1
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.
|
@@ -25,6 +25,20 @@ import { infraWalletApiRef } from '../../api/InfraWalletApi.esm.js';
|
|
|
25
25
|
import { colorList } from '../constants.esm.js';
|
|
26
26
|
import { getProviderIcon } from '../ProviderIcons/ProviderIcons.esm.js';
|
|
27
27
|
|
|
28
|
+
const monthList = {
|
|
29
|
+
"01": "Jan",
|
|
30
|
+
"02": "Feb",
|
|
31
|
+
"03": "Mar",
|
|
32
|
+
"04": "Apr",
|
|
33
|
+
"05": "May",
|
|
34
|
+
"06": "Jun",
|
|
35
|
+
"07": "Jul",
|
|
36
|
+
"08": "Aug",
|
|
37
|
+
"09": "Sep",
|
|
38
|
+
"10": "Oct",
|
|
39
|
+
"11": "Nov",
|
|
40
|
+
"12": "Dec"
|
|
41
|
+
};
|
|
28
42
|
function BudgetChart(props) {
|
|
29
43
|
const { width, height } = useDrawingArea();
|
|
30
44
|
const theme = useTheme();
|
|
@@ -56,12 +70,23 @@ function BudgetChart(props) {
|
|
|
56
70
|
setRefreshTrigger((prev) => !prev);
|
|
57
71
|
setOpenManageBudget(false);
|
|
58
72
|
};
|
|
73
|
+
const nonAccumulatedCosts = [];
|
|
59
74
|
const accumulatedCosts = [];
|
|
60
|
-
for (
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
for (const month of Object.keys(monthList).sort((a, b) => Number(a) - Number(b))) {
|
|
76
|
+
const yearMonth = `${moment().year()}-${month}`;
|
|
77
|
+
let cost;
|
|
78
|
+
if (yearMonth in monthlyCosts) {
|
|
79
|
+
cost = monthlyCosts[yearMonth];
|
|
80
|
+
} else if (Number(month) < moment().month()) {
|
|
81
|
+
cost = 0;
|
|
82
|
+
} else {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
nonAccumulatedCosts.push(cost);
|
|
86
|
+
if (month === "01") {
|
|
87
|
+
accumulatedCosts.push(cost);
|
|
63
88
|
} else {
|
|
64
|
-
accumulatedCosts[
|
|
89
|
+
accumulatedCosts.push(accumulatedCosts[accumulatedCosts.length - 1] + cost);
|
|
65
90
|
}
|
|
66
91
|
}
|
|
67
92
|
let budgetAmount = annualBudget?.amount || 0;
|
|
@@ -75,7 +100,7 @@ function BudgetChart(props) {
|
|
|
75
100
|
height,
|
|
76
101
|
series: [
|
|
77
102
|
{
|
|
78
|
-
data: view === "Annual" /* ANNUAL */ ? accumulatedCosts :
|
|
103
|
+
data: view === "Annual" /* ANNUAL */ ? accumulatedCosts : nonAccumulatedCosts,
|
|
79
104
|
type: view === "Annual" /* ANNUAL */ ? "line" : "bar",
|
|
80
105
|
valueFormatter: (value) => {
|
|
81
106
|
return formatCurrency(value || 0);
|
|
@@ -85,14 +110,14 @@ function BudgetChart(props) {
|
|
|
85
110
|
],
|
|
86
111
|
xAxis: [
|
|
87
112
|
{
|
|
88
|
-
data: [
|
|
113
|
+
data: Object.entries(monthList).sort(([a], [b]) => a.localeCompare(b)).map(([_, value]) => value),
|
|
89
114
|
scaleType: "band"
|
|
90
115
|
}
|
|
91
116
|
],
|
|
92
117
|
yAxis: [
|
|
93
118
|
{
|
|
94
119
|
min: 0,
|
|
95
|
-
max: view === "Annual" /* ANNUAL */ ? max([...accumulatedCosts, budgetAmount]) : max([...
|
|
120
|
+
max: view === "Annual" /* ANNUAL */ ? max([...accumulatedCosts, budgetAmount]) : max([...nonAccumulatedCosts, budgetAmount]),
|
|
96
121
|
valueFormatter: (value) => {
|
|
97
122
|
return formatCurrency(value || 0);
|
|
98
123
|
},
|
|
@@ -165,7 +190,7 @@ const Budgets = ({ providerErrorsSetter }) => {
|
|
|
165
190
|
size: "small",
|
|
166
191
|
onChange: (event) => setBudgetView(event.target.checked ? "Monthly" /* MONTHLY */ : "Annual" /* ANNUAL */)
|
|
167
192
|
}
|
|
168
|
-
)), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Monthly")), reportsAggregatedAndMerged !== void 0 ? reportsAggregatedAndMerged.map((report) => /* @__PURE__ */ React__default.createElement(Grid, { item: true, key: `${report.id}-grid`, xs: 4 }, /* @__PURE__ */ React__default.createElement(BudgetChart, { provider: report.id, monthlyCosts:
|
|
193
|
+
)), /* @__PURE__ */ React__default.createElement(Grid, { item: true }, "Monthly")), reportsAggregatedAndMerged !== void 0 ? reportsAggregatedAndMerged.map((report) => /* @__PURE__ */ React__default.createElement(Grid, { item: true, key: `${report.id}-grid`, xs: 4 }, /* @__PURE__ */ React__default.createElement(BudgetChart, { provider: report.id, monthlyCosts: report.reports, view: budgetView }))) : /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
169
194
|
Paper,
|
|
170
195
|
{
|
|
171
196
|
sx: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Budgets.esm.js","sources":["../../../src/components/Budgets/Budgets.tsx"],"sourcesContent":["import { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport FormControl from '@mui/material/FormControl';\nimport Grid from '@mui/material/Grid';\nimport Input from '@mui/material/Input';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport InputLabel from '@mui/material/InputLabel';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport { useTheme } from '@mui/material/styles';\nimport Switch from '@mui/material/Switch';\nimport Typography from '@mui/material/Typography';\nimport {\n BarPlot,\n ChartContainer,\n ChartsAxisHighlight,\n ChartsGrid,\n ChartsReferenceLine,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n useDrawingArea,\n} from '@mui/x-charts';\nimport { max } from 'lodash';\nimport moment from 'moment';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport { aggregateCostReports, formatCurrency, mergeCostReports } from '../../api/functions';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { Budget, Report } from '../../api/types';\nimport { colorList } from '../constants';\nimport { getProviderIcon } from '../ProviderIcons';\nimport { BudgetsProps } from '../types';\n\nconst enum BUDGET_VIEW {\n MONTHLY = 'Monthly',\n ANNUAL = 'Annual',\n}\n\ninterface BudgetChartProps {\n provider: string;\n monthlyCosts: number[];\n view: string;\n}\n\nfunction BudgetChart(props: Readonly<BudgetChartProps>) {\n const { width, height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view } = props;\n\n const [annualBudget, setAnnualBudget] = useState<Budget | undefined>(undefined);\n const [openManageBudget, setOpenManageBudget] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(false);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n\n useEffect(() => {\n const fetchBudget = async () => {\n const response = await infraWalletApi.getBudget('default', provider);\n const updatedBudget = response.data?.find(a => a.provider.toLowerCase() === provider.toLowerCase());\n setAnnualBudget(updatedBudget);\n };\n\n fetchBudget();\n }, [refreshTrigger, provider, infraWalletApi]);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const name = annualBudget?.name || `${provider} annual budget`;\n const amount = formData.get('amount');\n const newAnnualBudget: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: name,\n amount: amount ? Number(amount) : 0,\n };\n await infraWalletApi.updateBudget('default', newAnnualBudget);\n setRefreshTrigger(prev => !prev);\n setOpenManageBudget(false);\n };\n\n const accumulatedCosts: number[] = [];\n for (let i = 0; i < monthlyCosts.length; i++) {\n if (i === 0) {\n accumulatedCosts[i] = monthlyCosts[i];\n } else {\n accumulatedCosts[i] = accumulatedCosts[i - 1] + monthlyCosts[i];\n }\n }\n\n let budgetAmount = annualBudget?.amount || 0;\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n }\n\n return (\n <Paper sx={{ padding: 2 }}>\n <ChartContainer\n width={width + 20}\n height={height}\n series={[\n {\n data: view === BUDGET_VIEW.ANNUAL ? accumulatedCosts : monthlyCosts,\n type: view === BUDGET_VIEW.ANNUAL ? 'line' : 'bar',\n valueFormatter: (value: number | null) => {\n return formatCurrency(value || 0);\n },\n showMark: false,\n },\n ]}\n xAxis={[\n {\n data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n min: 0,\n max:\n view === BUDGET_VIEW.ANNUAL\n ? max([...accumulatedCosts, budgetAmount])\n : max([...monthlyCosts, budgetAmount]),\n valueFormatter: value => {\n return formatCurrency(value || 0);\n },\n colorMap: {\n type: 'piecewise',\n thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],\n colors: [colorList[0], theme.palette.error.main],\n },\n },\n ]}\n >\n <ChartsGrid horizontal />\n <ChartsAxisHighlight x={view === BUDGET_VIEW.ANNUAL ? 'line' : 'band'} />\n <LinePlot />\n <BarPlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsReferenceLine\n y={budgetAmount}\n label={budgetAmount ? formatCurrency(budgetAmount) : undefined}\n labelAlign=\"end\"\n lineStyle={{\n stroke: budgetAmount ? theme.palette.error.main : 'transparent',\n strokeDasharray: '5 5',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.error.main, fontSize: '0.9em' }}\n />\n <ChartsXAxis />\n <ChartsYAxis />\n <ChartsTooltip />\n </ChartContainer>\n <div style={{ textAlign: 'center', fontWeight: 'bold' }}>\n {getProviderIcon(provider)} {provider}\n </div>\n <div style={{ textAlign: 'center' }}>\n <Button onClick={() => setOpenManageBudget(true)}>Manage budget</Button>\n <Dialog fullWidth maxWidth=\"sm\" open={openManageBudget} onClose={() => setOpenManageBudget(false)}>\n <form onSubmit={updateBudget}>\n <DialogTitle>Manage Budget</DialogTitle>\n <DialogContent>\n <DialogContentText>Please enter your {props.provider} annual budget here.</DialogContentText>\n <Box sx={{ display: 'flex', alignItems: 'flex-end' }}>\n {getProviderIcon(provider)} \n <FormControl variant=\"standard\">\n <InputLabel>Amount</InputLabel>\n <Input\n id=\"amount\"\n name=\"amount\"\n type=\"number\"\n startAdornment={<InputAdornment position=\"start\">$</InputAdornment>}\n defaultValue={annualBudget?.amount}\n />\n </FormControl>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button type=\"submit\" variant=\"contained\">\n Submit\n </Button>\n <Button onClick={() => setOpenManageBudget(false)}>Cancel</Button>\n </DialogActions>\n </form>\n </Dialog>\n </div>\n </Paper>\n );\n}\n\nexport const Budgets: FC<BudgetsProps> = ({ providerErrorsSetter }) => {\n const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState<Report[] | undefined>(undefined);\n const [budgetView, setBudgetView] = useState(BUDGET_VIEW.ANNUAL);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n const alertApi = useApi(alertApiRef);\n\n const fetchCosts = useCallback(async () => {\n await infraWalletApi\n .getCostReports('', [], '', 'monthly', moment().startOf('y').toDate(), moment().endOf('d').toDate())\n .then(reportsResponse => {\n if (reportsResponse.data) {\n const aggregatedReports = aggregateCostReports(reportsResponse.data, 'provider');\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n providerErrorsSetter(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [alertApi, infraWalletApi, providerErrorsSetter]);\n\n useEffect(() => {\n fetchCosts();\n }, [fetchCosts]);\n\n return (\n <Grid container spacing={3}>\n <Grid item>\n <Typography variant=\"h5\">\n {moment().year()} {budgetView} Budgets\n </Typography>\n </Grid>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Annual</Grid>\n <Grid item>\n <Switch\n size=\"small\"\n onChange={event => setBudgetView(event.target.checked ? BUDGET_VIEW.MONTHLY : BUDGET_VIEW.ANNUAL)}\n />\n </Grid>\n <Grid item>Monthly</Grid>\n </Grid>\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart provider={report.id} monthlyCosts={Object.values(report.reports)} view={budgetView} />\n </Grid>\n ))\n ) : (\n <Grid item xs={12}>\n <Paper\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: 500,\n backgroundColor: 'transparent',\n boxShadow: 'none',\n }}\n >\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n </Paper>\n </Grid>\n )}\n </Grid>\n );\n};\n"],"names":["React"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Budgets.esm.js","sources":["../../../src/components/Budgets/Budgets.tsx"],"sourcesContent":["import { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport FormControl from '@mui/material/FormControl';\nimport Grid from '@mui/material/Grid';\nimport Input from '@mui/material/Input';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport InputLabel from '@mui/material/InputLabel';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport { useTheme } from '@mui/material/styles';\nimport Switch from '@mui/material/Switch';\nimport Typography from '@mui/material/Typography';\nimport {\n BarPlot,\n ChartContainer,\n ChartsAxisHighlight,\n ChartsGrid,\n ChartsReferenceLine,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n useDrawingArea,\n} from '@mui/x-charts';\nimport { max } from 'lodash';\nimport moment from 'moment';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport { aggregateCostReports, formatCurrency, mergeCostReports } from '../../api/functions';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { Budget, Report } from '../../api/types';\nimport { colorList } from '../constants';\nimport { getProviderIcon } from '../ProviderIcons';\nimport { BudgetsProps } from '../types';\n\nconst enum BUDGET_VIEW {\n MONTHLY = 'Monthly',\n ANNUAL = 'Annual',\n}\n\nconst monthList = {\n '01': 'Jan',\n '02': 'Feb',\n '03': 'Mar',\n '04': 'Apr',\n '05': 'May',\n '06': 'Jun',\n '07': 'Jul',\n '08': 'Aug',\n '09': 'Sep',\n '10': 'Oct',\n '11': 'Nov',\n '12': 'Dec',\n};\n\ninterface BudgetChartProps {\n provider: string;\n monthlyCosts: Record<string, number>;\n view: string;\n}\n\nfunction BudgetChart(props: Readonly<BudgetChartProps>) {\n const { width, height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view } = props;\n\n const [annualBudget, setAnnualBudget] = useState<Budget | undefined>(undefined);\n const [openManageBudget, setOpenManageBudget] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(false);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n\n useEffect(() => {\n const fetchBudget = async () => {\n const response = await infraWalletApi.getBudget('default', provider);\n const updatedBudget = response.data?.find(a => a.provider.toLowerCase() === provider.toLowerCase());\n setAnnualBudget(updatedBudget);\n };\n\n fetchBudget();\n }, [refreshTrigger, provider, infraWalletApi]);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const name = annualBudget?.name || `${provider} annual budget`;\n const amount = formData.get('amount');\n const newAnnualBudget: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: name,\n amount: amount ? Number(amount) : 0,\n };\n await infraWalletApi.updateBudget('default', newAnnualBudget);\n setRefreshTrigger(prev => !prev);\n setOpenManageBudget(false);\n };\n\n const nonAccumulatedCosts: number[] = [];\n const accumulatedCosts: number[] = [];\n for (const month of Object.keys(monthList).sort((a, b) => Number(a) - Number(b))) {\n const yearMonth = `${moment().year()}-${month}`;\n\n let cost;\n if (yearMonth in monthlyCosts) {\n cost = monthlyCosts[yearMonth];\n } else if (Number(month) < moment().month()) {\n cost = 0;\n } else {\n break;\n }\n\n nonAccumulatedCosts.push(cost);\n if (month === '01') {\n accumulatedCosts.push(cost);\n } else {\n accumulatedCosts.push(accumulatedCosts[accumulatedCosts.length - 1] + cost);\n }\n }\n\n let budgetAmount = annualBudget?.amount || 0;\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n }\n\n return (\n <Paper sx={{ padding: 2 }}>\n <ChartContainer\n width={width + 20}\n height={height}\n series={[\n {\n data: view === BUDGET_VIEW.ANNUAL ? accumulatedCosts : nonAccumulatedCosts,\n type: view === BUDGET_VIEW.ANNUAL ? 'line' : 'bar',\n valueFormatter: (value: number | null) => {\n return formatCurrency(value || 0);\n },\n showMark: false,\n },\n ]}\n xAxis={[\n {\n data: Object.entries(monthList)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([_, value]) => value),\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n min: 0,\n max:\n view === BUDGET_VIEW.ANNUAL\n ? max([...accumulatedCosts, budgetAmount])\n : max([...nonAccumulatedCosts, budgetAmount]),\n valueFormatter: value => {\n return formatCurrency(value || 0);\n },\n colorMap: {\n type: 'piecewise',\n thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],\n colors: [colorList[0], theme.palette.error.main],\n },\n },\n ]}\n >\n <ChartsGrid horizontal />\n <ChartsAxisHighlight x={view === BUDGET_VIEW.ANNUAL ? 'line' : 'band'} />\n <LinePlot />\n <BarPlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsReferenceLine\n y={budgetAmount}\n label={budgetAmount ? formatCurrency(budgetAmount) : undefined}\n labelAlign=\"end\"\n lineStyle={{\n stroke: budgetAmount ? theme.palette.error.main : 'transparent',\n strokeDasharray: '5 5',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.error.main, fontSize: '0.9em' }}\n />\n <ChartsXAxis />\n <ChartsYAxis />\n <ChartsTooltip />\n </ChartContainer>\n <div style={{ textAlign: 'center', fontWeight: 'bold' }}>\n {getProviderIcon(provider)} {provider}\n </div>\n <div style={{ textAlign: 'center' }}>\n <Button onClick={() => setOpenManageBudget(true)}>Manage budget</Button>\n <Dialog fullWidth maxWidth=\"sm\" open={openManageBudget} onClose={() => setOpenManageBudget(false)}>\n <form onSubmit={updateBudget}>\n <DialogTitle>Manage Budget</DialogTitle>\n <DialogContent>\n <DialogContentText>Please enter your {props.provider} annual budget here.</DialogContentText>\n <Box sx={{ display: 'flex', alignItems: 'flex-end' }}>\n {getProviderIcon(provider)} \n <FormControl variant=\"standard\">\n <InputLabel>Amount</InputLabel>\n <Input\n id=\"amount\"\n name=\"amount\"\n type=\"number\"\n startAdornment={<InputAdornment position=\"start\">$</InputAdornment>}\n defaultValue={annualBudget?.amount}\n />\n </FormControl>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button type=\"submit\" variant=\"contained\">\n Submit\n </Button>\n <Button onClick={() => setOpenManageBudget(false)}>Cancel</Button>\n </DialogActions>\n </form>\n </Dialog>\n </div>\n </Paper>\n );\n}\n\nexport const Budgets: FC<BudgetsProps> = ({ providerErrorsSetter }) => {\n const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState<Report[] | undefined>(undefined);\n const [budgetView, setBudgetView] = useState(BUDGET_VIEW.ANNUAL);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n const alertApi = useApi(alertApiRef);\n\n const fetchCosts = useCallback(async () => {\n await infraWalletApi\n .getCostReports('', [], '', 'monthly', moment().startOf('y').toDate(), moment().endOf('d').toDate())\n .then(reportsResponse => {\n if (reportsResponse.data) {\n const aggregatedReports = aggregateCostReports(reportsResponse.data, 'provider');\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n providerErrorsSetter(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [alertApi, infraWalletApi, providerErrorsSetter]);\n\n useEffect(() => {\n fetchCosts();\n }, [fetchCosts]);\n\n return (\n <Grid container spacing={3}>\n <Grid item>\n <Typography variant=\"h5\">\n {moment().year()} {budgetView} Budgets\n </Typography>\n </Grid>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Annual</Grid>\n <Grid item>\n <Switch\n size=\"small\"\n onChange={event => setBudgetView(event.target.checked ? BUDGET_VIEW.MONTHLY : BUDGET_VIEW.ANNUAL)}\n />\n </Grid>\n <Grid item>Monthly</Grid>\n </Grid>\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart provider={report.id} monthlyCosts={report.reports} view={budgetView} />\n </Grid>\n ))\n ) : (\n <Grid item xs={12}>\n <Paper\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: 500,\n backgroundColor: 'transparent',\n boxShadow: 'none',\n }}\n >\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n </Paper>\n </Grid>\n )}\n </Grid>\n );\n};\n"],"names":["React"],"mappings":"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"}
|