@electrolux-oss/plugin-infrawallet 1.1.0-20251218080406-fbaf733 → 1.1.0-20260115072023-d6f33e2

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.
@@ -199,7 +199,7 @@ const formatCurrency = (number, currency) => {
199
199
  notation: "compact"
200
200
  }).format(number);
201
201
  };
202
- const calculateBudgetAnalytics = (monthlyCosts, annualBudget) => {
202
+ const calculateBudgetAnalytics = (monthlyCosts, annualBudget, forecast) => {
203
203
  const currentMonth = moment().month() + 1;
204
204
  const currentYear = moment().year();
205
205
  const daysIntoCurrentMonth = moment().date();
@@ -214,7 +214,7 @@ const calculateBudgetAnalytics = (monthlyCosts, annualBudget) => {
214
214
  yearToDateSpent += monthCost;
215
215
  monthlySpending.push(monthCost);
216
216
  } else if (month === currentMonth) {
217
- projectedCurrentMonthCost = monthCost / daysIntoCurrentMonth * daysInCurrentMonth;
217
+ projectedCurrentMonthCost = forecast ?? monthCost / daysIntoCurrentMonth * daysInCurrentMonth;
218
218
  yearToDateSpent += monthCost;
219
219
  monthlySpending.push(projectedCurrentMonthCost);
220
220
  }
@@ -1 +1 @@
1
- {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subDays, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Filters, Report, Tag } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold?: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = totalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'Others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports).sort((a, b) => a.id.localeCompare(b.id));\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0) {\n const reportValue = report[key] as string | undefined;\n\n // If the report doesn't have this field at all, it doesn't match\n if (reportValue === undefined) {\n match = false;\n return;\n }\n\n // Check if the report value matches any of the filter values\n const valueMatches = filters[key].some(filterValue => {\n // For exact matches\n if (reportValue === filterValue) {\n return true;\n }\n\n // For account field: match with or without account ID suffix\n // e.g., \"AWS/aws-staging-mock\" should match \"AWS/aws-staging-mock (012345678901)\"\n if (key === 'account') {\n // Extract the base account name (everything before the parentheses)\n const baseAccountName = reportValue.split(' (')[0];\n const filterAccountName = filterValue.split(' (')[0];\n if (baseAccountName === filterAccountName) {\n return true;\n }\n }\n\n // For service field: handle provider prefix matching\n // e.g., \"AWS/Lambda\" in filter should match \"AWS/Lambda\" in report\n if (key === 'service' && reportValue.includes('/') && filterValue.includes('/')) {\n return reportValue === filterValue;\n }\n\n return false;\n });\n\n if (!valueMatches) {\n match = false;\n }\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n provider: report.provider,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[] | undefined): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports?.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n }\n\n keyValueSets[key].add(report[key] as string);\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n keyValues[key].sort((a, b) => a.localeCompare(b));\n });\n return keyValues;\n};\n\nexport const extractProvider = (input: string): string | undefined => {\n let provider = undefined;\n if (input && input.indexOf('/') !== -1) {\n provider = input.split('/')[0];\n }\n\n return provider;\n};\n\nexport const extractAccountInfo = (input: string): { accountName: string; accountId?: string } => {\n // try to match format: accountName (accountId), e.g. aws-dev (123456789012)\n const regex = /^(.*?)\\s*\\(([^)]+)\\)$/;\n const match = input.match(regex);\n\n if (match) {\n const accountName = match[1];\n const accountId = match[2];\n return { accountName: accountName, accountId: accountId };\n }\n\n return { accountName: input };\n};\n\n// check if targetTag exists in tags\nexport function tagExists(tags: Tag[], targetTag: Tag): boolean {\n return tags.some(\n tag => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value,\n );\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport const tagsToString = (tags: Tag[]): string => {\n if (tags.length === 0) {\n return '()';\n }\n\n const keyValuePairs = tags.map(tag => `${tag.provider}:${tag.key}=${tag.value}`);\n return `(${keyValuePairs.join(' OR ')})`;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'account', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPreviousDay = (day: string): string => {\n const date = parse(day, 'yyyy-MM-dd', new Date());\n const previousDay = subDays(date, 1);\n return format(previousDay, 'yyyy-MM-dd');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n\nexport const formatCurrency = (number: number, currency?: string): string => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n notation: 'compact',\n }).format(number);\n};\n\nexport interface BudgetAnalytics {\n yearToDateSpent: number;\n monthlyRunRate: number;\n projectedAnnualSpending: number;\n projectedCurrentMonthCost: number;\n budgetHealthStatus: 'healthy' | 'warning' | 'critical';\n budgetUtilizationPercent: number;\n targetMonthlySpending: number;\n monthsRemaining: number;\n averageMonthlySpending: number;\n spendingVelocity: number;\n confidenceRange: {\n low: number;\n high: number;\n };\n}\n\nexport const calculateBudgetAnalytics = (\n monthlyCosts: Record<string, number>,\n annualBudget: number,\n): BudgetAnalytics => {\n const currentMonth = moment().month() + 1;\n const currentYear = moment().year();\n const daysIntoCurrentMonth = moment().date();\n const daysInCurrentMonth = moment().daysInMonth();\n\n let yearToDateSpent = 0;\n let projectedCurrentMonthCost = 0;\n const monthlySpending: number[] = [];\n\n for (let month = 1; month <= currentMonth; month++) {\n const monthKey = `${currentYear}-${month.toString().padStart(2, '0')}`;\n const monthCost = monthlyCosts[monthKey] || 0;\n\n if (month < currentMonth) {\n yearToDateSpent += monthCost;\n monthlySpending.push(monthCost);\n } else if (month === currentMonth) {\n projectedCurrentMonthCost = (monthCost / daysIntoCurrentMonth) * daysInCurrentMonth;\n yearToDateSpent += monthCost;\n monthlySpending.push(projectedCurrentMonthCost);\n }\n }\n\n const monthsRemaining = 12 - currentMonth + (1 - daysIntoCurrentMonth / daysInCurrentMonth);\n const averageMonthlySpending =\n monthlySpending.length > 0 ? monthlySpending.reduce((sum, cost) => sum + cost, 0) / monthlySpending.length : 0;\n\n const monthlyRunRate = monthlySpending.length > 0 ? monthlySpending[monthlySpending.length - 1] || 0 : 0;\n\n const projectedAnnualSpending = yearToDateSpent + averageMonthlySpending * monthsRemaining;\n\n const budgetUtilizationPercent = annualBudget > 0 ? (yearToDateSpent / annualBudget) * 100 : 0;\n const expectedUtilizationPercent = ((currentMonth - 1 + daysIntoCurrentMonth / daysInCurrentMonth) / 12) * 100;\n\n let budgetHealthStatus: 'healthy' | 'warning' | 'critical' = 'healthy';\n if (budgetUtilizationPercent > expectedUtilizationPercent + 20) {\n budgetHealthStatus = 'critical';\n } else if (budgetUtilizationPercent > expectedUtilizationPercent + 10) {\n budgetHealthStatus = 'warning';\n }\n\n const targetMonthlySpending = monthsRemaining > 0 ? (annualBudget - yearToDateSpent) / monthsRemaining : 0;\n\n const spendingVariance =\n monthlySpending.length > 1\n ? Math.sqrt(\n monthlySpending.reduce((sum, cost) => sum + Math.pow(cost - averageMonthlySpending, 2), 0) /\n (monthlySpending.length - 1),\n )\n : 0;\n\n const spendingVelocity =\n monthlySpending.length >= 2\n ? ((monthlySpending[monthlySpending.length - 1] - monthlySpending[monthlySpending.length - 2]) /\n monthlySpending[monthlySpending.length - 2]) *\n 100\n : 0;\n\n const confidenceRange = {\n low: Math.max(0, projectedAnnualSpending - spendingVariance * 2 * Math.sqrt(monthsRemaining)),\n high: projectedAnnualSpending + spendingVariance * 2 * Math.sqrt(monthsRemaining),\n };\n\n return {\n yearToDateSpent,\n monthlyRunRate,\n projectedAnnualSpending,\n projectedCurrentMonthCost,\n budgetHealthStatus,\n budgetUtilizationPercent,\n targetMonthlySpending,\n monthsRemaining,\n averageMonthlySpending,\n spendingVelocity,\n confidenceRange,\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subDays, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Filters, Report, Tag } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold?: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = totalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'Others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports).sort((a, b) => a.id.localeCompare(b.id));\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0) {\n const reportValue = report[key] as string | undefined;\n\n // If the report doesn't have this field at all, it doesn't match\n if (reportValue === undefined) {\n match = false;\n return;\n }\n\n // Check if the report value matches any of the filter values\n const valueMatches = filters[key].some(filterValue => {\n // For exact matches\n if (reportValue === filterValue) {\n return true;\n }\n\n // For account field: match with or without account ID suffix\n // e.g., \"AWS/aws-staging-mock\" should match \"AWS/aws-staging-mock (012345678901)\"\n if (key === 'account') {\n // Extract the base account name (everything before the parentheses)\n const baseAccountName = reportValue.split(' (')[0];\n const filterAccountName = filterValue.split(' (')[0];\n if (baseAccountName === filterAccountName) {\n return true;\n }\n }\n\n // For service field: handle provider prefix matching\n // e.g., \"AWS/Lambda\" in filter should match \"AWS/Lambda\" in report\n if (key === 'service' && reportValue.includes('/') && filterValue.includes('/')) {\n return reportValue === filterValue;\n }\n\n return false;\n });\n\n if (!valueMatches) {\n match = false;\n }\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n provider: report.provider,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[] | undefined): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports?.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n }\n\n keyValueSets[key].add(report[key] as string);\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n keyValues[key].sort((a, b) => a.localeCompare(b));\n });\n return keyValues;\n};\n\nexport const extractProvider = (input: string): string | undefined => {\n let provider = undefined;\n if (input && input.indexOf('/') !== -1) {\n provider = input.split('/')[0];\n }\n\n return provider;\n};\n\nexport const extractAccountInfo = (input: string): { accountName: string; accountId?: string } => {\n // try to match format: accountName (accountId), e.g. aws-dev (123456789012)\n const regex = /^(.*?)\\s*\\(([^)]+)\\)$/;\n const match = input.match(regex);\n\n if (match) {\n const accountName = match[1];\n const accountId = match[2];\n return { accountName: accountName, accountId: accountId };\n }\n\n return { accountName: input };\n};\n\n// check if targetTag exists in tags\nexport function tagExists(tags: Tag[], targetTag: Tag): boolean {\n return tags.some(\n tag => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value,\n );\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport const tagsToString = (tags: Tag[]): string => {\n if (tags.length === 0) {\n return '()';\n }\n\n const keyValuePairs = tags.map(tag => `${tag.provider}:${tag.key}=${tag.value}`);\n return `(${keyValuePairs.join(' OR ')})`;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'account', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPreviousDay = (day: string): string => {\n const date = parse(day, 'yyyy-MM-dd', new Date());\n const previousDay = subDays(date, 1);\n return format(previousDay, 'yyyy-MM-dd');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n\nexport const formatCurrency = (number: number, currency?: string): string => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n notation: 'compact',\n }).format(number);\n};\n\nexport interface BudgetAnalytics {\n yearToDateSpent: number;\n monthlyRunRate: number;\n projectedAnnualSpending: number;\n projectedCurrentMonthCost: number;\n budgetHealthStatus: 'healthy' | 'warning' | 'critical';\n budgetUtilizationPercent: number;\n targetMonthlySpending: number;\n monthsRemaining: number;\n averageMonthlySpending: number;\n spendingVelocity: number;\n confidenceRange: {\n low: number;\n high: number;\n };\n}\n\nexport const calculateBudgetAnalytics = (\n monthlyCosts: Record<string, number>,\n annualBudget: number,\n forecast?: number,\n): BudgetAnalytics => {\n const currentMonth = moment().month() + 1;\n const currentYear = moment().year();\n const daysIntoCurrentMonth = moment().date();\n const daysInCurrentMonth = moment().daysInMonth();\n let yearToDateSpent = 0;\n let projectedCurrentMonthCost = 0;\n const monthlySpending: number[] = [];\n for (let month = 1; month <= currentMonth; month++) {\n const monthKey = `${currentYear}-${month.toString().padStart(2, '0')}`;\n const monthCost = monthlyCosts[monthKey] || 0;\n\n if (month < currentMonth) {\n yearToDateSpent += monthCost;\n monthlySpending.push(monthCost);\n } else if (month === currentMonth) {\n projectedCurrentMonthCost = forecast ?? (monthCost / daysIntoCurrentMonth) * daysInCurrentMonth;\n yearToDateSpent += monthCost;\n monthlySpending.push(projectedCurrentMonthCost);\n }\n }\n\n const monthsRemaining = 12 - currentMonth + (1 - daysIntoCurrentMonth / daysInCurrentMonth);\n const averageMonthlySpending =\n monthlySpending.length > 0 ? monthlySpending.reduce((sum, cost) => sum + cost, 0) / monthlySpending.length : 0;\n\n const monthlyRunRate = monthlySpending.length > 0 ? monthlySpending[monthlySpending.length - 1] || 0 : 0;\n\n const projectedAnnualSpending = yearToDateSpent + averageMonthlySpending * monthsRemaining;\n\n const budgetUtilizationPercent = annualBudget > 0 ? (yearToDateSpent / annualBudget) * 100 : 0;\n const expectedUtilizationPercent = ((currentMonth - 1 + daysIntoCurrentMonth / daysInCurrentMonth) / 12) * 100;\n\n let budgetHealthStatus: 'healthy' | 'warning' | 'critical' = 'healthy';\n if (budgetUtilizationPercent > expectedUtilizationPercent + 20) {\n budgetHealthStatus = 'critical';\n } else if (budgetUtilizationPercent > expectedUtilizationPercent + 10) {\n budgetHealthStatus = 'warning';\n }\n\n const targetMonthlySpending = monthsRemaining > 0 ? (annualBudget - yearToDateSpent) / monthsRemaining : 0;\n\n const spendingVariance =\n monthlySpending.length > 1\n ? Math.sqrt(\n monthlySpending.reduce((sum, cost) => sum + Math.pow(cost - averageMonthlySpending, 2), 0) /\n (monthlySpending.length - 1),\n )\n : 0;\n\n const spendingVelocity =\n monthlySpending.length >= 2\n ? ((monthlySpending[monthlySpending.length - 1] - monthlySpending[monthlySpending.length - 2]) /\n monthlySpending[monthlySpending.length - 2]) *\n 100\n : 0;\n\n const confidenceRange = {\n low: Math.max(0, projectedAnnualSpending - spendingVariance * 2 * Math.sqrt(monthsRemaining)),\n high: projectedAnnualSpending + spendingVariance * 2 * Math.sqrt(monthsRemaining),\n };\n\n return {\n yearToDateSpent,\n monthlyRunRate,\n projectedAnnualSpending,\n projectedCurrentMonthCost,\n budgetHealthStatus,\n budgetUtilizationPercent,\n targetMonthlySpending,\n monthsRemaining,\n averageMonthlySpending,\n spendingVelocity,\n confidenceRange,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -99,12 +99,12 @@ function getRecommendationColor(type) {
99
99
  function BudgetChart(props) {
100
100
  const { height } = useDrawingArea();
101
101
  const theme = useTheme();
102
- const { provider, monthlyCosts, view, budgets, setBudgets } = props;
102
+ const { provider, monthlyCosts, view, budgets, setBudgets, forecast } = props;
103
103
  const infraWalletApi = useApi(infraWalletApiRef);
104
104
  const annualBudget = budgets.find((b) => b.provider.toLowerCase() === provider.toLowerCase());
105
105
  const annualBudgetAmount = annualBudget?.amount || 0;
106
106
  const [openManageBudget, setOpenManageBudget] = useState(false);
107
- const budgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudgetAmount);
107
+ const budgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudgetAmount, forecast);
108
108
  const updateBudget = async (event) => {
109
109
  event.preventDefault();
110
110
  const formData = new FormData(event.currentTarget);
@@ -155,7 +155,7 @@ function BudgetChart(props) {
155
155
  const lastActualCost = lastIndex >= 0 ? nonAccumulatedCosts[lastIndex] : 0;
156
156
  const projectedCurrentMonthCost = budgetAnalytics.projectedCurrentMonthCost;
157
157
  const projectedDelta = projectedCurrentMonthCost - lastActualCost;
158
- const monthlyMax = max([...nonAccumulatedCosts, budgetAmount]) || 0;
158
+ const monthlyMax = max([...nonAccumulatedCosts, budgetAmount, projectedCurrentMonthCost]) || 0;
159
159
  chartSeries = [
160
160
  {
161
161
  id: "actual-spend",
@@ -398,6 +398,7 @@ const Budgets = ({ providerErrorsSetter }) => {
398
398
  const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState(void 0);
399
399
  const [budgetView, setBudgetView] = useState("Annual" /* ANNUAL */);
400
400
  const [budgets, setBudgets] = useState([]);
401
+ const [forecasts, setForecasts] = useState({});
401
402
  const infraWalletApi = useApi(infraWalletApiRef);
402
403
  const alertApi = useApi(alertApiRef);
403
404
  const fetchCosts = useCallback(async () => {
@@ -406,6 +407,7 @@ const Budgets = ({ providerErrorsSetter }) => {
406
407
  const aggregatedReports = aggregateCostReports(reportsResponse.data, "provider");
407
408
  const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);
408
409
  setReportsAggregatedAndMerged(aggregatedAndMergedReports);
410
+ setForecasts(reportsResponse.forecasts || {});
409
411
  }
410
412
  if (reportsResponse.status === 207 && reportsResponse.errors) {
411
413
  providerErrorsSetter(reportsResponse.errors);
@@ -438,7 +440,8 @@ const Budgets = ({ providerErrorsSetter }) => {
438
440
  monthlyCosts: report.reports,
439
441
  view: budgetView,
440
442
  budgets,
441
- setBudgets
443
+ setBudgets,
444
+ forecast: forecasts[report.id]
442
445
  }
443
446
  ))) : /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
444
447
  Paper,
@@ -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 Card from '@mui/material/Card';\nimport CardContent from '@mui/material/CardContent';\nimport Chip from '@mui/material/Chip';\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 ResponsiveChartContainer,\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 {\n aggregateCostReports,\n formatCurrency,\n mergeCostReports,\n calculateBudgetAnalytics,\n BudgetAnalytics,\n} from '../../api/functions';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { Budget, Report } from '../../api/types';\nimport { colorList } from '../constants';\nimport { ProviderIcon } from '../ProviderIcon';\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 BudgetHealthIndicatorProps {\n status: 'healthy' | 'warning' | 'critical';\n utilizationPercent: number;\n}\n\nfunction BudgetHealthIndicator({ status, utilizationPercent }: BudgetHealthIndicatorProps) {\n const getStatusColor = () => {\n switch (status) {\n case 'healthy':\n return '#4caf50';\n case 'warning':\n return '#ff9800';\n case 'critical':\n return '#f44336';\n default:\n return '#9e9e9e';\n }\n };\n\n const getStatusLabel = () => {\n switch (status) {\n case 'healthy':\n return 'On Track';\n case 'warning':\n return 'At Risk';\n case 'critical':\n return 'Over Budget';\n default:\n return 'Unknown';\n }\n };\n\n return (\n <Chip\n label={`${getStatusLabel()} (${utilizationPercent.toFixed(1)}%)`}\n size=\"small\"\n sx={{\n backgroundColor: getStatusColor(),\n color: 'white',\n fontWeight: 'bold',\n '& .MuiChip-label': { fontWeight: 'bold' },\n }}\n />\n );\n}\n\ninterface BudgetChartProps {\n provider: string;\n monthlyCosts: Record<string, number>;\n view: string;\n budgets: Budget[];\n setBudgets: React.Dispatch<React.SetStateAction<Budget[]>>;\n}\n\nfunction getSpendingVelocityColor(velocity: number) {\n if (velocity > 10) return 'error';\n if (velocity < -10) return 'success.main';\n return 'textSecondary';\n}\n\nfunction getSpendingVelocityIcon(velocity: number) {\n if (velocity > 0) return '↑';\n if (velocity < 0) return '↓';\n return '→';\n}\n\nfunction getRecommendationColor(type: string) {\n if (type === 'critical') return 'error';\n if (type === 'warning') return 'warning';\n return 'info';\n}\n\nfunction BudgetChart(props: Readonly<BudgetChartProps>) {\n const { height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view, budgets, setBudgets } = props;\n const infraWalletApi = useApi(infraWalletApiRef);\n\n const annualBudget = budgets.find(b => b.provider.toLowerCase() === provider.toLowerCase());\n const annualBudgetAmount = annualBudget?.amount || 0;\n\n const [openManageBudget, setOpenManageBudget] = useState(false);\n\n const budgetAnalytics: BudgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudgetAmount);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const amount = Number(formData.get('amount') || 0);\n const updated: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: annualBudget?.name || `${provider} annual budget`,\n amount: amount,\n };\n await infraWalletApi.updateBudget('default', updated);\n setBudgets(prev => {\n const index = prev.findIndex(b => b.provider.toLowerCase() === provider.toLowerCase());\n if (index >= 0) {\n const copy = [...prev];\n copy[index] = { ...copy[index], ...updated };\n return copy;\n }\n return [...prev, updated];\n });\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 = annualBudgetAmount;\n\n let chartSeries: any[];\n let yAxis: any[];\n\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n\n const lastIndex = nonAccumulatedCosts.length - 1;\n const lastActualCost = lastIndex >= 0 ? nonAccumulatedCosts[lastIndex] : 0;\n const projectedCurrentMonthCost = budgetAnalytics.projectedCurrentMonthCost;\n const projectedDelta = projectedCurrentMonthCost - lastActualCost;\n const monthlyMax = max([...nonAccumulatedCosts, budgetAmount]) || 0;\n\n chartSeries = [\n {\n id: 'actual-spend',\n yAxisKey: 'spendAxis',\n data: nonAccumulatedCosts,\n type: 'bar',\n stack: 'combined',\n label: 'Actual Spend',\n color: colorList[0],\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\n },\n ];\n\n if (lastIndex >= 0 && projectedDelta > 0) {\n const deltaData = nonAccumulatedCosts.map((_, i) => (i === lastIndex ? projectedDelta : 0));\n\n chartSeries.push({\n id: 'projected-delta',\n yAxisKey: 'deltaAxis',\n data: deltaData,\n type: 'bar',\n stack: 'combined',\n label: 'Projected Delta',\n color: theme.palette.warning.main,\n valueFormatter: (value: number | null) => {\n if (value === 0) return null;\n return formatCurrency(value || 0);\n },\n });\n }\n\n yAxis = [\n {\n id: 'spendAxis',\n min: 0,\n max: monthlyMax,\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\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 id: 'deltaAxis',\n min: 0,\n max: monthlyMax,\n },\n ];\n } else {\n chartSeries = [\n {\n id: 'yearAxis',\n yAxisKey: 'spendAxis',\n data: accumulatedCosts,\n type: 'line',\n showMark: false,\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\n },\n ];\n\n yAxis = [\n {\n id: 'spendAxis',\n min: 0,\n max: max([...accumulatedCosts, budgetAmount, budgetAnalytics.confidenceRange.high]),\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\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\n return (\n <Paper sx={{ padding: 2 }}>\n {/* Budget Health Header */}\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <ProviderIcon provider={provider} />\n <Typography variant=\"h6\" sx={{ ml: 1, fontWeight: 'bold' }}>\n {provider}\n </Typography>\n </Box>\n {annualBudgetAmount > 0 && (\n <BudgetHealthIndicator\n status={budgetAnalytics.budgetHealthStatus}\n utilizationPercent={budgetAnalytics.budgetUtilizationPercent}\n />\n )}\n </Box>\n\n {/* Budget Metrics Cards */}\n {annualBudgetAmount > 0 && (\n <Grid container spacing={1} sx={{ mb: 2 }}>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n YTD Spent\n </Typography>\n <Typography variant=\"body2\" fontWeight=\"bold\">\n {formatCurrency(budgetAnalytics.yearToDateSpent)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n of {formatCurrency(annualBudgetAmount)}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Projected Annual\n </Typography>\n <Typography\n variant=\"body2\"\n fontWeight=\"bold\"\n color={budgetAnalytics.projectedAnnualSpending > annualBudgetAmount ? 'error' : 'inherit'}\n >\n {formatCurrency(budgetAnalytics.projectedAnnualSpending)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {budgetAnalytics.projectedAnnualSpending > annualBudgetAmount\n ? `+${formatCurrency(budgetAnalytics.projectedAnnualSpending - annualBudgetAmount)} over`\n : `${formatCurrency(annualBudgetAmount - budgetAnalytics.projectedAnnualSpending)} under`}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Monthly Run Rate\n </Typography>\n <Typography variant=\"body2\" fontWeight=\"bold\">\n {formatCurrency(budgetAnalytics.monthlyRunRate)}\n </Typography>\n <Typography variant=\"caption\" color={getSpendingVelocityColor(budgetAnalytics.spendingVelocity)}>\n {getSpendingVelocityIcon(budgetAnalytics.spendingVelocity)}\n {Math.abs(budgetAnalytics.spendingVelocity).toFixed(1)}% MoM\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Target Monthly\n </Typography>\n <Typography\n variant=\"body2\"\n fontWeight=\"bold\"\n color={\n budgetAnalytics.targetMonthlySpending < budgetAnalytics.monthlyRunRate ? 'error' : 'success.main'\n }\n >\n {formatCurrency(budgetAnalytics.targetMonthlySpending)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {budgetAnalytics.monthsRemaining.toFixed(1)} months left\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n </Grid>\n )}\n\n <ResponsiveChartContainer\n height={height}\n series={chartSeries}\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={yAxis}\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 {/* Add projection line for annual view */}\n {view === BUDGET_VIEW.ANNUAL && annualBudgetAmount > 0 && (\n <>\n <ChartsReferenceLine\n y={budgetAnalytics.projectedAnnualSpending}\n label={`Projected: ${formatCurrency(budgetAnalytics.projectedAnnualSpending)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.warning.main,\n strokeDasharray: '3 3',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.warning.main, fontSize: '0.8em' }}\n />\n {/* Confidence Range - High */}\n <ChartsReferenceLine\n y={budgetAnalytics.confidenceRange.high}\n label={`High: ${formatCurrency(budgetAnalytics.confidenceRange.high)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.grey[400],\n strokeDasharray: '2 2',\n strokeWidth: 1,\n strokeOpacity: 0.5,\n }}\n labelStyle={{ fill: theme.palette.grey[600], fontSize: '0.7em' }}\n />\n {/* Confidence Range - Low */}\n <ChartsReferenceLine\n y={budgetAnalytics.confidenceRange.low}\n label={`Low: ${formatCurrency(budgetAnalytics.confidenceRange.low)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.grey[400],\n strokeDasharray: '2 2',\n strokeWidth: 1,\n strokeOpacity: 0.5,\n }}\n labelStyle={{ fill: theme.palette.grey[600], fontSize: '0.7em' }}\n />\n </>\n )}\n <ChartsXAxis />\n <ChartsYAxis />\n <ChartsTooltip />\n </ResponsiveChartContainer>\n\n <Box sx={{ 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 <ProviderIcon provider={provider} />\n &nbsp;&nbsp;\n <FormControl variant=\"standard\">\n <InputLabel>Amount</InputLabel>\n <Input\n id=\"amount\"\n name=\"amount\"\n type=\"number\"\n startAdornment={<InputAdornment position=\"start\">$</InputAdornment>}\n defaultValue={annualBudgetAmount}\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 </Box>\n </Paper>\n );\n}\n\ninterface BudgetInsightsProps {\n reports: Report[];\n budgets: Budget[];\n}\n\nfunction BudgetInsights({ reports, budgets }: BudgetInsightsProps) {\n const insights = reports\n .map(report => {\n const budget = budgets.find(b => b.provider.toLowerCase() === report.id.toLowerCase());\n if (!budget?.amount) return null;\n\n const analytics = calculateBudgetAnalytics(report.reports, budget.amount);\n return { provider: report.id, budget, analytics };\n })\n .filter(Boolean);\n\n const totalBudget = budgets.reduce((sum, b) => sum + (b.amount || 0), 0);\n const totalProjected = insights.reduce((sum, i) => sum + (i?.analytics.projectedAnnualSpending || 0), 0);\n const overBudgetProviders = insights.filter(i => i?.analytics.budgetHealthStatus === 'critical');\n const atRiskProviders = insights.filter(i => i?.analytics.budgetHealthStatus === 'warning');\n\n const getRecommendations = () => {\n const recommendations = [];\n\n if (overBudgetProviders.length > 0) {\n recommendations.push({\n type: 'critical',\n message: `${overBudgetProviders.length} provider(s) are over budget: ${overBudgetProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n if (atRiskProviders.length > 0) {\n recommendations.push({\n type: 'warning',\n message: `${atRiskProviders.length} provider(s) at risk: ${atRiskProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n if (totalProjected > totalBudget) {\n recommendations.push({\n type: 'info',\n message: `Total projected spending (${formatCurrency(totalProjected)}) exceeds total budget (${formatCurrency(totalBudget)}) by ${formatCurrency(totalProjected - totalBudget)}`,\n });\n }\n\n const highVelocityProviders = insights.filter(i => i?.analytics && i.analytics.spendingVelocity > 20);\n if (highVelocityProviders.length > 0) {\n recommendations.push({\n type: 'warning',\n message: `High spending acceleration detected in: ${highVelocityProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n return recommendations;\n };\n\n const recommendations = getRecommendations();\n\n if (insights.length === 0) return null;\n\n return (\n <Card sx={{ mb: 3 }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Budget Insights\n </Typography>\n\n {/* Overview Stats */}\n <Grid container spacing={2} sx={{ mb: 2 }}>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color=\"primary\">\n {insights.length}\n </Typography>\n <Typography variant=\"caption\">Providers</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={overBudgetProviders.length > 0 ? 'error' : 'success.main'}>\n {overBudgetProviders.length}\n </Typography>\n <Typography variant=\"caption\">Over Budget</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={atRiskProviders.length > 0 ? 'warning.main' : 'success.main'}>\n {atRiskProviders.length}\n </Typography>\n <Typography variant=\"caption\">At Risk</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={totalProjected > totalBudget ? 'error' : 'success.main'}>\n {((totalProjected / totalBudget) * 100).toFixed(0)}%\n </Typography>\n <Typography variant=\"caption\">Projected vs Budget</Typography>\n </Box>\n </Grid>\n </Grid>\n\n {/* Recommendations */}\n {recommendations.length > 0 && (\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom>\n Recommendations\n </Typography>\n {recommendations.map(rec => (\n <Chip\n key={`${rec.type}-${rec.message}`}\n label={rec.message}\n size=\"small\"\n color={getRecommendationColor(rec.type)}\n variant=\"outlined\"\n sx={{ mr: 1, mb: 1, maxWidth: '100%' }}\n />\n ))}\n </Box>\n )}\n </CardContent>\n </Card>\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 const [budgets, setBudgets] = useState<Budget[]>([]);\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 useEffect(() => {\n const fetchBudgets = async () => {\n try {\n const response = await infraWalletApi.getBudgets('default');\n setBudgets(response.data || []);\n } catch (error) {\n // Failed to fetch budgets - silent error handling\n }\n };\n fetchBudgets();\n }, [infraWalletApi]);\n\n return (\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography variant=\"h5\">\n {moment().year()} {budgetView} Budgets\n </Typography>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <Typography variant=\"body2\">Annual</Typography>\n <Switch\n size=\"small\"\n onChange={event => setBudgetView(event.target.checked ? BUDGET_VIEW.MONTHLY : BUDGET_VIEW.ANNUAL)}\n />\n <Typography variant=\"body2\">Monthly</Typography>\n </Box>\n </Box>\n </Grid>\n\n {/* Budget Insights Panel */}\n {reportsAggregatedAndMerged && (\n <Grid item xs={12}>\n <BudgetInsights reports={reportsAggregatedAndMerged} budgets={budgets} />\n </Grid>\n )}\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart\n provider={report.id}\n monthlyCosts={report.reports}\n view={budgetView}\n budgets={budgets}\n setBudgets={setBudgets}\n />\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":["recommendations"],"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 Card from '@mui/material/Card';\nimport CardContent from '@mui/material/CardContent';\nimport Chip from '@mui/material/Chip';\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 ResponsiveChartContainer,\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 {\n aggregateCostReports,\n formatCurrency,\n mergeCostReports,\n calculateBudgetAnalytics,\n BudgetAnalytics,\n} from '../../api/functions';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport { Budget, Report } from '../../api/types';\nimport { colorList } from '../constants';\nimport { ProviderIcon } from '../ProviderIcon';\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 BudgetHealthIndicatorProps {\n status: 'healthy' | 'warning' | 'critical';\n utilizationPercent: number;\n}\n\nfunction BudgetHealthIndicator({ status, utilizationPercent }: BudgetHealthIndicatorProps) {\n const getStatusColor = () => {\n switch (status) {\n case 'healthy':\n return '#4caf50';\n case 'warning':\n return '#ff9800';\n case 'critical':\n return '#f44336';\n default:\n return '#9e9e9e';\n }\n };\n\n const getStatusLabel = () => {\n switch (status) {\n case 'healthy':\n return 'On Track';\n case 'warning':\n return 'At Risk';\n case 'critical':\n return 'Over Budget';\n default:\n return 'Unknown';\n }\n };\n\n return (\n <Chip\n label={`${getStatusLabel()} (${utilizationPercent.toFixed(1)}%)`}\n size=\"small\"\n sx={{\n backgroundColor: getStatusColor(),\n color: 'white',\n fontWeight: 'bold',\n '& .MuiChip-label': { fontWeight: 'bold' },\n }}\n />\n );\n}\n\ninterface BudgetChartProps {\n provider: string;\n monthlyCosts: Record<string, number>;\n view: string;\n budgets: Budget[];\n forecast?: number;\n setBudgets: React.Dispatch<React.SetStateAction<Budget[]>>;\n}\n\nfunction getSpendingVelocityColor(velocity: number) {\n if (velocity > 10) return 'error';\n if (velocity < -10) return 'success.main';\n return 'textSecondary';\n}\n\nfunction getSpendingVelocityIcon(velocity: number) {\n if (velocity > 0) return '↑';\n if (velocity < 0) return '↓';\n return '→';\n}\n\nfunction getRecommendationColor(type: string) {\n if (type === 'critical') return 'error';\n if (type === 'warning') return 'warning';\n return 'info';\n}\n\nfunction BudgetChart(props: Readonly<BudgetChartProps>) {\n const { height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view, budgets, setBudgets, forecast } = props;\n const infraWalletApi = useApi(infraWalletApiRef);\n\n const annualBudget = budgets.find(b => b.provider.toLowerCase() === provider.toLowerCase());\n const annualBudgetAmount = annualBudget?.amount || 0;\n\n const [openManageBudget, setOpenManageBudget] = useState(false);\n\n const budgetAnalytics: BudgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudgetAmount, forecast);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const amount = Number(formData.get('amount') || 0);\n const updated: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: annualBudget?.name || `${provider} annual budget`,\n amount: amount,\n };\n await infraWalletApi.updateBudget('default', updated);\n setBudgets(prev => {\n const index = prev.findIndex(b => b.provider.toLowerCase() === provider.toLowerCase());\n if (index >= 0) {\n const copy = [...prev];\n copy[index] = { ...copy[index], ...updated };\n return copy;\n }\n return [...prev, updated];\n });\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 = annualBudgetAmount;\n\n let chartSeries: any[];\n let yAxis: any[];\n\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n\n const lastIndex = nonAccumulatedCosts.length - 1;\n const lastActualCost = lastIndex >= 0 ? nonAccumulatedCosts[lastIndex] : 0;\n const projectedCurrentMonthCost = budgetAnalytics.projectedCurrentMonthCost;\n const projectedDelta = projectedCurrentMonthCost - lastActualCost;\n const monthlyMax = max([...nonAccumulatedCosts, budgetAmount, projectedCurrentMonthCost]) || 0;\n\n chartSeries = [\n {\n id: 'actual-spend',\n yAxisKey: 'spendAxis',\n data: nonAccumulatedCosts,\n type: 'bar',\n stack: 'combined',\n label: 'Actual Spend',\n color: colorList[0],\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\n },\n ];\n\n if (lastIndex >= 0 && projectedDelta > 0) {\n const deltaData = nonAccumulatedCosts.map((_, i) => (i === lastIndex ? projectedDelta : 0));\n\n chartSeries.push({\n id: 'projected-delta',\n yAxisKey: 'deltaAxis',\n data: deltaData,\n type: 'bar',\n stack: 'combined',\n label: 'Projected Delta',\n color: theme.palette.warning.main,\n valueFormatter: (value: number | null) => {\n if (value === 0) return null;\n return formatCurrency(value || 0);\n },\n });\n }\n\n yAxis = [\n {\n id: 'spendAxis',\n min: 0,\n max: monthlyMax,\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\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 id: 'deltaAxis',\n min: 0,\n max: monthlyMax,\n },\n ];\n } else {\n chartSeries = [\n {\n id: 'yearAxis',\n yAxisKey: 'spendAxis',\n data: accumulatedCosts,\n type: 'line',\n showMark: false,\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\n },\n ];\n\n yAxis = [\n {\n id: 'spendAxis',\n min: 0,\n max: max([...accumulatedCosts, budgetAmount, budgetAnalytics.confidenceRange.high]),\n valueFormatter: (value: number | null) => formatCurrency(value || 0),\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\n return (\n <Paper sx={{ padding: 2 }}>\n {/* Budget Health Header */}\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <ProviderIcon provider={provider} />\n <Typography variant=\"h6\" sx={{ ml: 1, fontWeight: 'bold' }}>\n {provider}\n </Typography>\n </Box>\n {annualBudgetAmount > 0 && (\n <BudgetHealthIndicator\n status={budgetAnalytics.budgetHealthStatus}\n utilizationPercent={budgetAnalytics.budgetUtilizationPercent}\n />\n )}\n </Box>\n\n {/* Budget Metrics Cards */}\n {annualBudgetAmount > 0 && (\n <Grid container spacing={1} sx={{ mb: 2 }}>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n YTD Spent\n </Typography>\n <Typography variant=\"body2\" fontWeight=\"bold\">\n {formatCurrency(budgetAnalytics.yearToDateSpent)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n of {formatCurrency(annualBudgetAmount)}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Projected Annual\n </Typography>\n <Typography\n variant=\"body2\"\n fontWeight=\"bold\"\n color={budgetAnalytics.projectedAnnualSpending > annualBudgetAmount ? 'error' : 'inherit'}\n >\n {formatCurrency(budgetAnalytics.projectedAnnualSpending)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {budgetAnalytics.projectedAnnualSpending > annualBudgetAmount\n ? `+${formatCurrency(budgetAnalytics.projectedAnnualSpending - annualBudgetAmount)} over`\n : `${formatCurrency(annualBudgetAmount - budgetAnalytics.projectedAnnualSpending)} under`}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Monthly Run Rate\n </Typography>\n <Typography variant=\"body2\" fontWeight=\"bold\">\n {formatCurrency(budgetAnalytics.monthlyRunRate)}\n </Typography>\n <Typography variant=\"caption\" color={getSpendingVelocityColor(budgetAnalytics.spendingVelocity)}>\n {getSpendingVelocityIcon(budgetAnalytics.spendingVelocity)}\n {Math.abs(budgetAnalytics.spendingVelocity).toFixed(1)}% MoM\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={6}>\n <Card variant=\"outlined\" sx={{ textAlign: 'center' }}>\n <CardContent sx={{ py: 1, '&:last-child': { pb: 1 } }}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Target Monthly\n </Typography>\n <Typography\n variant=\"body2\"\n fontWeight=\"bold\"\n color={\n budgetAnalytics.targetMonthlySpending < budgetAnalytics.monthlyRunRate ? 'error' : 'success.main'\n }\n >\n {formatCurrency(budgetAnalytics.targetMonthlySpending)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {budgetAnalytics.monthsRemaining.toFixed(1)} months left\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n </Grid>\n )}\n\n <ResponsiveChartContainer\n height={height}\n series={chartSeries}\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={yAxis}\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 {/* Add projection line for annual view */}\n {view === BUDGET_VIEW.ANNUAL && annualBudgetAmount > 0 && (\n <>\n <ChartsReferenceLine\n y={budgetAnalytics.projectedAnnualSpending}\n label={`Projected: ${formatCurrency(budgetAnalytics.projectedAnnualSpending)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.warning.main,\n strokeDasharray: '3 3',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.warning.main, fontSize: '0.8em' }}\n />\n {/* Confidence Range - High */}\n <ChartsReferenceLine\n y={budgetAnalytics.confidenceRange.high}\n label={`High: ${formatCurrency(budgetAnalytics.confidenceRange.high)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.grey[400],\n strokeDasharray: '2 2',\n strokeWidth: 1,\n strokeOpacity: 0.5,\n }}\n labelStyle={{ fill: theme.palette.grey[600], fontSize: '0.7em' }}\n />\n {/* Confidence Range - Low */}\n <ChartsReferenceLine\n y={budgetAnalytics.confidenceRange.low}\n label={`Low: ${formatCurrency(budgetAnalytics.confidenceRange.low)}`}\n labelAlign=\"start\"\n lineStyle={{\n stroke: theme.palette.grey[400],\n strokeDasharray: '2 2',\n strokeWidth: 1,\n strokeOpacity: 0.5,\n }}\n labelStyle={{ fill: theme.palette.grey[600], fontSize: '0.7em' }}\n />\n </>\n )}\n <ChartsXAxis />\n <ChartsYAxis />\n <ChartsTooltip />\n </ResponsiveChartContainer>\n\n <Box sx={{ 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 <ProviderIcon provider={provider} />\n &nbsp;&nbsp;\n <FormControl variant=\"standard\">\n <InputLabel>Amount</InputLabel>\n <Input\n id=\"amount\"\n name=\"amount\"\n type=\"number\"\n startAdornment={<InputAdornment position=\"start\">$</InputAdornment>}\n defaultValue={annualBudgetAmount}\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 </Box>\n </Paper>\n );\n}\n\ninterface BudgetInsightsProps {\n reports: Report[];\n budgets: Budget[];\n}\n\nfunction BudgetInsights({ reports, budgets }: BudgetInsightsProps) {\n const insights = reports\n .map(report => {\n const budget = budgets.find(b => b.provider.toLowerCase() === report.id.toLowerCase());\n if (!budget?.amount) return null;\n\n const analytics = calculateBudgetAnalytics(report.reports, budget.amount);\n return { provider: report.id, budget, analytics };\n })\n .filter(Boolean);\n\n const totalBudget = budgets.reduce((sum, b) => sum + (b.amount || 0), 0);\n const totalProjected = insights.reduce((sum, i) => sum + (i?.analytics.projectedAnnualSpending || 0), 0);\n const overBudgetProviders = insights.filter(i => i?.analytics.budgetHealthStatus === 'critical');\n const atRiskProviders = insights.filter(i => i?.analytics.budgetHealthStatus === 'warning');\n\n const getRecommendations = () => {\n const recommendations = [];\n\n if (overBudgetProviders.length > 0) {\n recommendations.push({\n type: 'critical',\n message: `${overBudgetProviders.length} provider(s) are over budget: ${overBudgetProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n if (atRiskProviders.length > 0) {\n recommendations.push({\n type: 'warning',\n message: `${atRiskProviders.length} provider(s) at risk: ${atRiskProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n if (totalProjected > totalBudget) {\n recommendations.push({\n type: 'info',\n message: `Total projected spending (${formatCurrency(totalProjected)}) exceeds total budget (${formatCurrency(totalBudget)}) by ${formatCurrency(totalProjected - totalBudget)}`,\n });\n }\n\n const highVelocityProviders = insights.filter(i => i?.analytics && i.analytics.spendingVelocity > 20);\n if (highVelocityProviders.length > 0) {\n recommendations.push({\n type: 'warning',\n message: `High spending acceleration detected in: ${highVelocityProviders.map(p => p?.provider).join(', ')}`,\n });\n }\n\n return recommendations;\n };\n\n const recommendations = getRecommendations();\n\n if (insights.length === 0) return null;\n\n return (\n <Card sx={{ mb: 3 }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Budget Insights\n </Typography>\n\n {/* Overview Stats */}\n <Grid container spacing={2} sx={{ mb: 2 }}>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color=\"primary\">\n {insights.length}\n </Typography>\n <Typography variant=\"caption\">Providers</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={overBudgetProviders.length > 0 ? 'error' : 'success.main'}>\n {overBudgetProviders.length}\n </Typography>\n <Typography variant=\"caption\">Over Budget</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={atRiskProviders.length > 0 ? 'warning.main' : 'success.main'}>\n {atRiskProviders.length}\n </Typography>\n <Typography variant=\"caption\">At Risk</Typography>\n </Box>\n </Grid>\n <Grid item xs={3}>\n <Box textAlign=\"center\">\n <Typography variant=\"h4\" color={totalProjected > totalBudget ? 'error' : 'success.main'}>\n {((totalProjected / totalBudget) * 100).toFixed(0)}%\n </Typography>\n <Typography variant=\"caption\">Projected vs Budget</Typography>\n </Box>\n </Grid>\n </Grid>\n\n {/* Recommendations */}\n {recommendations.length > 0 && (\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom>\n Recommendations\n </Typography>\n {recommendations.map(rec => (\n <Chip\n key={`${rec.type}-${rec.message}`}\n label={rec.message}\n size=\"small\"\n color={getRecommendationColor(rec.type)}\n variant=\"outlined\"\n sx={{ mr: 1, mb: 1, maxWidth: '100%' }}\n />\n ))}\n </Box>\n )}\n </CardContent>\n </Card>\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 const [budgets, setBudgets] = useState<Budget[]>([]);\n const [forecasts, setForecasts] = useState<Record<string, number>>({});\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 setForecasts(reportsResponse.forecasts || {});\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 useEffect(() => {\n const fetchBudgets = async () => {\n try {\n const response = await infraWalletApi.getBudgets('default');\n setBudgets(response.data || []);\n } catch (error) {\n // Failed to fetch budgets - silent error handling\n }\n };\n fetchBudgets();\n }, [infraWalletApi]);\n\n return (\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography variant=\"h5\">\n {moment().year()} {budgetView} Budgets\n </Typography>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <Typography variant=\"body2\">Annual</Typography>\n <Switch\n size=\"small\"\n onChange={event => setBudgetView(event.target.checked ? BUDGET_VIEW.MONTHLY : BUDGET_VIEW.ANNUAL)}\n />\n <Typography variant=\"body2\">Monthly</Typography>\n </Box>\n </Box>\n </Grid>\n\n {/* Budget Insights Panel */}\n {reportsAggregatedAndMerged && (\n <Grid item xs={12}>\n <BudgetInsights reports={reportsAggregatedAndMerged} budgets={budgets} />\n </Grid>\n )}\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart\n provider={report.id}\n monthlyCosts={report.reports}\n view={budgetView}\n budgets={budgets}\n setBudgets={setBudgets}\n forecast={forecasts[report.id]}\n />\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":["recommendations"],"mappings":"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"}
@@ -2,9 +2,10 @@ import Grid from '@mui/material/Grid';
2
2
  import Paper from '@mui/material/Paper';
3
3
  import Skeleton from '@mui/material/Skeleton';
4
4
  import Switch from '@mui/material/Switch';
5
+ import { useTheme } from '@mui/material/styles';
5
6
  import { ResponsiveChartContainer, ChartsGrid, BarPlot, LinePlot, MarkPlot, LineHighlightPlot, ChartsXAxis, ChartsYAxis, ChartsTooltip, ChartsLegend, DefaultChartsLegend } from '@mui/x-charts';
6
7
  import React, { useState, useCallback, useEffect } from 'react';
7
- import { formatCurrency } from '../../api/functions.esm.js';
8
+ import { formatCurrency, calculateBudgetAnalytics } from '../../api/functions.esm.js';
8
9
  import { colorList } from '../constants.esm.js';
9
10
 
10
11
  const ColumnsChartComponent = ({
@@ -13,62 +14,143 @@ const ColumnsChartComponent = ({
13
14
  periods,
14
15
  costs,
15
16
  metrics,
17
+ budgets,
18
+ forecasts,
16
19
  height,
17
20
  highlightedItem,
18
21
  highlightedItemSetter
19
22
  }) => {
23
+ const theme = useTheme();
20
24
  const [costsSeries, setCostsSeries] = useState(void 0);
21
25
  const [metricsSeries, setMetricsSeries] = useState(void 0);
22
26
  const [showMetrics, setShowMetrics] = useState(false);
23
27
  const [maxCostsYaxis, setMaxCostsYaxis] = useState(void 0);
28
+ const calculateCostSums = useCallback((costsData) => {
29
+ const sums = [];
30
+ for (const s of costsData) {
31
+ for (let i = 0; i < s.data.length; i++) {
32
+ if (sums[i] === void 0) {
33
+ sums[i] = 0;
34
+ }
35
+ sums[i] += s.data[i];
36
+ }
37
+ }
38
+ return sums;
39
+ }, []);
40
+ const createBaseCostsSeries = useCallback((costsData) => {
41
+ return costsData.map((s) => ({
42
+ id: s.name,
43
+ data: s.data,
44
+ type: "bar",
45
+ label: `${s.name} Actual Spend`,
46
+ yAxisId: "costsAxis",
47
+ valueFormatter: (value) => formatCurrency(value || 0),
48
+ highlightScope: { highlight: "series", fade: "global" },
49
+ stack: "stack1",
50
+ stackOrder: "descending"
51
+ }));
52
+ }, []);
53
+ const buildMonthlyCosts = useCallback(
54
+ (seriesData) => {
55
+ const monthlyCosts = {};
56
+ periods.forEach((period, index) => {
57
+ monthlyCosts[period] = seriesData[index] || 0;
58
+ });
59
+ return monthlyCosts;
60
+ },
61
+ [periods]
62
+ );
63
+ const createProjectedSeries = useCallback(
64
+ (series, budget, forecast) => {
65
+ const effectiveBudget = budget?.amount ? budget : { amount: 1e5, provider: series.name };
66
+ if (!effectiveBudget?.amount) return null;
67
+ const monthlyCosts = buildMonthlyCosts(series.data);
68
+ const analytics = calculateBudgetAnalytics(monthlyCosts, effectiveBudget.amount, forecast);
69
+ const lastIndex = series.data.length - 1;
70
+ const lastActualCost = lastIndex >= 0 ? series.data[lastIndex] : 0;
71
+ const projectedDelta = analytics.projectedCurrentMonthCost - lastActualCost;
72
+ if (lastIndex < 0 || projectedDelta <= 0) return null;
73
+ const deltaData = series.data.map((_, i) => i === lastIndex ? projectedDelta : 0);
74
+ return {
75
+ id: `${series.name}-projected`,
76
+ data: deltaData,
77
+ type: "bar",
78
+ label: `${series.name} Forecast`,
79
+ yAxisId: "costsAxis",
80
+ color: theme.palette.warning.main,
81
+ valueFormatter: (value) => {
82
+ if (value === 0) return null;
83
+ return `${formatCurrency(projectedDelta)}`;
84
+ },
85
+ highlightScope: { highlight: "series", fade: "global" },
86
+ stack: "stack1",
87
+ stackOrder: "descending"
88
+ };
89
+ },
90
+ [buildMonthlyCosts, theme.palette.warning.main]
91
+ );
92
+ const createProjectedDeltaSeries = useCallback(
93
+ (costsData) => {
94
+ if (granularity !== "monthly") return [];
95
+ const projectedSeries = [];
96
+ for (const s of costsData) {
97
+ const budget = budgets?.find((b) => b.provider.toLowerCase() === s.name.toLowerCase());
98
+ const forecast = forecasts?.[s.name];
99
+ const projectedData = createProjectedSeries(s, budget, forecast);
100
+ if (projectedData) {
101
+ projectedSeries.push(projectedData);
102
+ }
103
+ }
104
+ return projectedSeries;
105
+ },
106
+ [granularity, budgets, forecasts, createProjectedSeries]
107
+ );
108
+ const calculateMaxYAxis = useCallback((baseSums, projectedSeries) => {
109
+ const allSums = [...baseSums];
110
+ for (const deltaSeries of projectedSeries) {
111
+ for (let i = 0; i < deltaSeries.data.length; i++) {
112
+ if (deltaSeries.data[i] > 0) {
113
+ allSums[i] = (allSums[i] || 0) + deltaSeries.data[i];
114
+ }
115
+ }
116
+ }
117
+ return Math.max(...allSums);
118
+ }, []);
119
+ const createMetricsSeriesData = useCallback(() => {
120
+ if (!metrics || !showMetrics) return void 0;
121
+ return metrics.map((s) => ({
122
+ data: s.data,
123
+ type: "line",
124
+ label: s.name,
125
+ curve: "natural",
126
+ yAxisId: "metricsAxis",
127
+ highlightScope: { highlight: "series", fade: "global" },
128
+ showMark: granularity === "monthly"
129
+ }));
130
+ }, [metrics, showMetrics, granularity]);
24
131
  const initChartCallback = useCallback(async () => {
25
132
  setCostsSeries(void 0);
26
133
  setMetricsSeries(void 0);
27
134
  if (costs) {
28
- const sums = [];
29
- for (const s of costs) {
30
- for (let i = 0; i < s.data.length; i++) {
31
- if (sums[i] === void 0) {
32
- sums[i] = 0;
33
- }
34
- sums[i] += s.data[i];
35
- }
36
- }
37
- setMaxCostsYaxis(Math.max(...sums));
38
- setCostsSeries(
39
- costs.map((s) => {
40
- return {
41
- id: s.name,
42
- data: s.data,
43
- type: "bar",
44
- label: s.name,
45
- yAxisId: "costsAxis",
46
- valueFormatter: (value) => {
47
- return formatCurrency(value ? value : 0);
48
- },
49
- highlightScope: { highlight: "series", fade: "global" },
50
- stack: "total",
51
- stackOrder: "descending"
52
- };
53
- })
54
- );
135
+ const sums = calculateCostSums(costs);
136
+ const baseCostsSeries = createBaseCostsSeries(costs);
137
+ const projectedDeltaSeries = createProjectedDeltaSeries(costs);
138
+ const maxYValue = calculateMaxYAxis(sums, projectedDeltaSeries);
139
+ setMaxCostsYaxis(maxYValue);
140
+ setCostsSeries([...baseCostsSeries, ...projectedDeltaSeries]);
55
141
  }
56
- if (metrics && showMetrics) {
57
- setMetricsSeries(
58
- metrics.map((s) => {
59
- return {
60
- data: s.data,
61
- type: "line",
62
- label: s.name,
63
- curve: "natural",
64
- yAxisId: "metricsAxis",
65
- highlightScope: { highlight: "series", fade: "global" },
66
- showMark: granularity === "monthly"
67
- };
68
- })
69
- );
142
+ const metricsData = createMetricsSeriesData();
143
+ if (metricsData) {
144
+ setMetricsSeries(metricsData);
70
145
  }
71
- }, [costs, metrics, showMetrics, granularity]);
146
+ }, [
147
+ costs,
148
+ calculateCostSums,
149
+ createBaseCostsSeries,
150
+ createProjectedDeltaSeries,
151
+ calculateMaxYAxis,
152
+ createMetricsSeriesData
153
+ ]);
72
154
  useEffect(() => {
73
155
  initChartCallback();
74
156
  }, [initChartCallback]);
@@ -1 +1 @@
1
- {"version":3,"file":"ColumnsChartComponent.esm.js","sources":["../../../src/components/ColumnsChartComponent/ColumnsChartComponent.tsx"],"sourcesContent":["import Grid from '@mui/material/Grid';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport Switch from '@mui/material/Switch';\nimport {\n BarPlot,\n ChartsGrid,\n ChartsLegend,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n DefaultChartsLegend,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n ResponsiveChartContainer,\n} from '@mui/x-charts';\nimport { FC, default as React, useCallback, useEffect, useState } from 'react';\nimport { formatCurrency } from '../../api/functions';\nimport { colorList } from '../constants';\nimport { ColumnsChartComponentProps } from '../types';\n\nexport const ColumnsChartComponent: FC<ColumnsChartComponentProps> = ({\n granularity,\n granularitySetter,\n periods,\n costs,\n metrics,\n height,\n highlightedItem,\n highlightedItemSetter,\n}) => {\n const [costsSeries, setCostsSeries] = useState<any[] | undefined>(undefined);\n const [metricsSeries, setMetricsSeries] = useState<any[] | undefined>(undefined);\n const [showMetrics, setShowMetrics] = useState<boolean>(false);\n const [maxCostsYaxis, setMaxCostsYaxis] = useState<number | undefined>(undefined);\n\n const initChartCallback = useCallback(async () => {\n setCostsSeries(undefined);\n setMetricsSeries(undefined);\n\n if (costs) {\n const sums = [];\n for (const s of costs) {\n for (let i = 0; i < s.data.length; i++) {\n if (sums[i] === undefined) {\n sums[i] = 0;\n }\n sums[i] += s.data[i];\n }\n }\n setMaxCostsYaxis(Math.max(...sums));\n\n setCostsSeries(\n costs.map(s => {\n return {\n id: s.name,\n data: s.data,\n type: 'bar',\n label: s.name,\n yAxisId: 'costsAxis',\n valueFormatter: (value: number) => {\n return formatCurrency(value ? value : 0);\n },\n highlightScope: { highlight: 'series', fade: 'global' },\n stack: 'total',\n stackOrder: 'descending',\n };\n }),\n );\n }\n\n if (metrics && showMetrics) {\n setMetricsSeries(\n metrics.map(s => {\n return {\n data: s.data,\n type: 'line',\n label: s.name,\n curve: 'natural',\n yAxisId: 'metricsAxis',\n highlightScope: { highlight: 'series', fade: 'global' },\n showMark: granularity === 'monthly',\n };\n }),\n );\n }\n }, [costs, metrics, showMetrics, granularity]);\n\n useEffect(() => {\n initChartCallback();\n }, [initChartCallback]);\n\n return (\n <Paper\n sx={{\n padding: '16px',\n display: 'flex',\n flexDirection: 'column',\n height: height ? height : 300,\n }}\n >\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Monthly</Grid>\n <Grid item>\n <Switch size=\"small\" onChange={event => granularitySetter(event.target.checked ? 'daily' : 'monthly')} />\n </Grid>\n <Grid item>Daily</Grid>\n <Grid item> | </Grid>\n <Grid item>\n <Switch size=\"small\" checked={showMetrics} onChange={_ => setShowMetrics((ori: boolean) => !ori)} />\n </Grid>\n <Grid item>Show Metrics</Grid>\n </Grid>\n {costs !== undefined && costsSeries !== undefined ? (\n <ResponsiveChartContainer\n margin={{\n bottom: 6 * costsSeries.length + 80,\n }}\n series={[...costsSeries, ...(metricsSeries ? metricsSeries : [])]}\n xAxis={[\n {\n data: periods,\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n id: 'costsAxis',\n max: maxCostsYaxis,\n valueFormatter: value => formatCurrency(value),\n },\n {\n id: 'metricsAxis',\n },\n ]}\n colors={colorList}\n highlightedItem={highlightedItem ? { seriesId: highlightedItem } : null}\n onHighlightChange={highlighted => {\n highlightedItemSetter(highlighted?.seriesId);\n }}\n >\n <ChartsGrid horizontal />\n <BarPlot />\n <LinePlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsXAxis\n tickLabelStyle={{\n angle: periods.length > 12 ? -45 : 0,\n textAnchor: periods.length > 12 ? 'end' : 'middle',\n }}\n />\n <ChartsYAxis />\n <ChartsTooltip trigger=\"item\" />\n <ChartsLegend\n slots={{\n legend: props => {\n // keep legend items for costs only (from bar chart)\n const seriesToDisplay = [];\n for (const s of props.seriesToDisplay) {\n if (props.series.bar?.seriesOrder && props.series.bar.seriesOrder.indexOf(s.id) !== -1) {\n seriesToDisplay.push(s);\n }\n }\n\n return (\n <DefaultChartsLegend\n series={props.series}\n seriesToDisplay={seriesToDisplay}\n drawingArea={props.drawingArea}\n direction=\"row\"\n position={{ vertical: 'bottom', horizontal: 'middle' }}\n itemMarkHeight={10}\n itemMarkWidth={10}\n itemGap={costsSeries.length > 5 ? 5 : 10}\n labelStyle={{\n fontSize: '0.9em',\n }}\n onItemClick={(_, legendItem) => {\n // highlight the clicked item\n const clickedLegend = legendItem.seriesId;\n if (clickedLegend === highlightedItem) {\n highlightedItemSetter(undefined);\n } else {\n highlightedItemSetter(clickedLegend);\n }\n }}\n />\n );\n },\n }}\n />\n </ResponsiveChartContainer>\n ) : (\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n )}\n </Paper>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ColumnsChartComponent.esm.js","sources":["../../../src/components/ColumnsChartComponent/ColumnsChartComponent.tsx"],"sourcesContent":["import Grid from '@mui/material/Grid';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport Switch from '@mui/material/Switch';\nimport { useTheme } from '@mui/material/styles';\nimport {\n BarPlot,\n ChartsGrid,\n ChartsLegend,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n DefaultChartsLegend,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n ResponsiveChartContainer,\n} from '@mui/x-charts';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport { formatCurrency, calculateBudgetAnalytics } from '../../api/functions';\nimport { colorList } from '../constants';\nimport { ColumnsChartComponentProps } from '../types';\n\nexport const ColumnsChartComponent: FC<ColumnsChartComponentProps> = ({\n granularity,\n granularitySetter,\n periods,\n costs,\n metrics,\n budgets,\n forecasts,\n height,\n highlightedItem,\n highlightedItemSetter,\n}) => {\n const theme = useTheme();\n const [costsSeries, setCostsSeries] = useState<any[] | undefined>(undefined);\n const [metricsSeries, setMetricsSeries] = useState<any[] | undefined>(undefined);\n const [showMetrics, setShowMetrics] = useState<boolean>(false);\n const [maxCostsYaxis, setMaxCostsYaxis] = useState<number | undefined>(undefined);\n\n // Helper: Calculate cost sums across all series\n const calculateCostSums = useCallback((costsData: any[]) => {\n const sums = [];\n for (const s of costsData) {\n for (let i = 0; i < s.data.length; i++) {\n if (sums[i] === undefined) {\n sums[i] = 0;\n }\n sums[i] += s.data[i];\n }\n }\n return sums;\n }, []);\n\n // Helper: Create base costs series\n const createBaseCostsSeries = useCallback((costsData: any[]) => {\n return costsData.map(s => ({\n id: s.name,\n data: s.data,\n type: 'bar' as const,\n label: `${s.name} Actual Spend`,\n yAxisId: 'costsAxis',\n valueFormatter: (value: number) => formatCurrency(value || 0),\n highlightScope: { highlight: 'series', fade: 'global' } as const,\n stack: 'stack1',\n stackOrder: 'descending' as const,\n }));\n }, []);\n\n // Helper: Build monthly costs lookup\n const buildMonthlyCosts = useCallback(\n (seriesData: number[]) => {\n const monthlyCosts: Record<string, number> = {};\n periods.forEach((period, index) => {\n monthlyCosts[period] = seriesData[index] || 0;\n });\n return monthlyCosts;\n },\n [periods],\n );\n\n // Helper: Create single projected series\n const createProjectedSeries = useCallback(\n (series: any, budget: any, forecast: any) => {\n const effectiveBudget = budget?.amount ? budget : { amount: 100000, provider: series.name };\n if (!effectiveBudget?.amount) return null;\n\n const monthlyCosts = buildMonthlyCosts(series.data);\n const analytics = calculateBudgetAnalytics(monthlyCosts, effectiveBudget.amount, forecast);\n\n const lastIndex = series.data.length - 1;\n const lastActualCost = lastIndex >= 0 ? series.data[lastIndex] : 0;\n const projectedDelta = analytics.projectedCurrentMonthCost - lastActualCost;\n\n if (lastIndex < 0 || projectedDelta <= 0) return null;\n\n const deltaData = series.data.map((_: any, i: number) => (i === lastIndex ? projectedDelta : 0));\n\n return {\n id: `${series.name}-projected`,\n data: deltaData,\n type: 'bar' as const,\n label: `${series.name} Forecast`,\n yAxisId: 'costsAxis',\n color: theme.palette.warning.main,\n valueFormatter: (value: number) => {\n if (value === 0) return null;\n return `${formatCurrency(projectedDelta)}`;\n },\n highlightScope: { highlight: 'series', fade: 'global' } as const,\n stack: 'stack1',\n stackOrder: 'descending' as const,\n };\n },\n [buildMonthlyCosts, theme.palette.warning.main],\n );\n\n // Helper: Create all projected delta series\n const createProjectedDeltaSeries = useCallback(\n (costsData: any[]) => {\n if (granularity !== 'monthly') return [];\n\n const projectedSeries: any[] = [];\n for (const s of costsData) {\n const budget = budgets?.find(b => b.provider.toLowerCase() === s.name.toLowerCase());\n const forecast = forecasts?.[s.name];\n const projectedData = createProjectedSeries(s, budget, forecast);\n\n if (projectedData) {\n projectedSeries.push(projectedData);\n }\n }\n return projectedSeries;\n },\n [granularity, budgets, forecasts, createProjectedSeries],\n );\n\n // Helper: Calculate maximum Y-axis value\n const calculateMaxYAxis = useCallback((baseSums: number[], projectedSeries: any[]) => {\n const allSums = [...baseSums];\n for (const deltaSeries of projectedSeries) {\n for (let i = 0; i < deltaSeries.data.length; i++) {\n if (deltaSeries.data[i] > 0) {\n allSums[i] = (allSums[i] || 0) + deltaSeries.data[i];\n }\n }\n }\n return Math.max(...allSums);\n }, []);\n\n // Helper: Create metrics series\n const createMetricsSeriesData = useCallback(() => {\n if (!metrics || !showMetrics) return undefined;\n\n return metrics.map(s => ({\n data: s.data,\n type: 'line' as const,\n label: s.name,\n curve: 'natural' as const,\n yAxisId: 'metricsAxis',\n highlightScope: { highlight: 'series', fade: 'global' } as const,\n showMark: granularity === 'monthly',\n }));\n }, [metrics, showMetrics, granularity]);\n\n const initChartCallback = useCallback(async () => {\n setCostsSeries(undefined);\n setMetricsSeries(undefined);\n\n if (costs) {\n const sums = calculateCostSums(costs);\n const baseCostsSeries = createBaseCostsSeries(costs);\n const projectedDeltaSeries = createProjectedDeltaSeries(costs);\n const maxYValue = calculateMaxYAxis(sums, projectedDeltaSeries);\n\n setMaxCostsYaxis(maxYValue);\n setCostsSeries([...baseCostsSeries, ...projectedDeltaSeries]);\n }\n\n const metricsData = createMetricsSeriesData();\n if (metricsData) {\n setMetricsSeries(metricsData);\n }\n }, [\n costs,\n calculateCostSums,\n createBaseCostsSeries,\n createProjectedDeltaSeries,\n calculateMaxYAxis,\n createMetricsSeriesData,\n ]);\n\n useEffect(() => {\n initChartCallback();\n }, [initChartCallback]);\n\n return (\n <Paper\n sx={{\n padding: '16px',\n display: 'flex',\n flexDirection: 'column',\n height: height ? height : 300,\n }}\n >\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Monthly</Grid>\n <Grid item>\n <Switch size=\"small\" onChange={event => granularitySetter(event.target.checked ? 'daily' : 'monthly')} />\n </Grid>\n <Grid item>Daily</Grid>\n <Grid item> | </Grid>\n <Grid item>\n <Switch size=\"small\" checked={showMetrics} onChange={_ => setShowMetrics((ori: boolean) => !ori)} />\n </Grid>\n <Grid item>Show Metrics</Grid>\n </Grid>\n {costs !== undefined && costsSeries !== undefined ? (\n <ResponsiveChartContainer\n margin={{\n bottom: 6 * costsSeries.length + 80,\n }}\n series={[...costsSeries, ...(metricsSeries ? metricsSeries : [])]}\n xAxis={[\n {\n data: periods,\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n id: 'costsAxis',\n max: maxCostsYaxis,\n valueFormatter: value => formatCurrency(value),\n },\n {\n id: 'metricsAxis',\n },\n ]}\n colors={colorList}\n highlightedItem={highlightedItem ? { seriesId: highlightedItem } : null}\n onHighlightChange={highlighted => {\n highlightedItemSetter(highlighted?.seriesId);\n }}\n >\n <ChartsGrid horizontal />\n <BarPlot />\n <LinePlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsXAxis\n tickLabelStyle={{\n angle: periods.length > 12 ? -45 : 0,\n textAnchor: periods.length > 12 ? 'end' : 'middle',\n }}\n />\n <ChartsYAxis />\n <ChartsTooltip trigger=\"item\" />\n <ChartsLegend\n slots={{\n legend: props => {\n // keep legend items for costs only (from bar chart)\n const seriesToDisplay = [];\n for (const s of props.seriesToDisplay) {\n if (props.series.bar?.seriesOrder && props.series.bar.seriesOrder.indexOf(s.id) !== -1) {\n seriesToDisplay.push(s);\n }\n }\n\n return (\n <DefaultChartsLegend\n series={props.series}\n seriesToDisplay={seriesToDisplay}\n drawingArea={props.drawingArea}\n direction=\"row\"\n position={{ vertical: 'bottom', horizontal: 'middle' }}\n itemMarkHeight={10}\n itemMarkWidth={10}\n itemGap={costsSeries.length > 5 ? 5 : 10}\n labelStyle={{\n fontSize: '0.9em',\n }}\n onItemClick={(_, legendItem) => {\n // highlight the clicked item\n const clickedLegend = legendItem.seriesId;\n if (clickedLegend === highlightedItem) {\n highlightedItemSetter(undefined);\n } else {\n highlightedItemSetter(clickedLegend);\n }\n }}\n />\n );\n },\n }}\n />\n </ResponsiveChartContainer>\n ) : (\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n )}\n </Paper>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -89,6 +89,8 @@ const ReportsComponent = (props) => {
89
89
  const [selectedTags, setSelectedTags] = useState(initialState.selectedTags);
90
90
  const [reports, setReports] = useState(void 0);
91
91
  const [metrics, setMetrics] = useState([]);
92
+ const [budgets, setBudgets] = useState([]);
93
+ const [forecasts, setForecasts] = useState({});
92
94
  const [filters, setFilters] = useState(initialState.filters);
93
95
  const [cloudProviderErrors, setCloudProviderErrors] = useState([]);
94
96
  const [reportsAggregated, setReportsAggregated] = useState(void 0);
@@ -109,6 +111,7 @@ const ReportsComponent = (props) => {
109
111
  if (reportsResponse.data) {
110
112
  setReports(reportsResponse.data);
111
113
  setPeriods(getPeriodStrings(granularity, monthRange.startMonth, monthRange.endMonth));
114
+ setForecasts(reportsResponse.forecasts || {});
112
115
  }
113
116
  if (reportsResponse.status === 207 && reportsResponse.errors) {
114
117
  setCloudProviderErrors(reportsResponse.errors);
@@ -140,6 +143,17 @@ const ReportsComponent = (props) => {
140
143
  fetchCostReportsCallback();
141
144
  fetchMetricsCallback();
142
145
  }, [fetchCostReportsCallback, fetchMetricsCallback]);
146
+ useEffect(() => {
147
+ const fetchBudgets = async () => {
148
+ try {
149
+ const response = await infraWalletApi.getBudgets(params.name ?? "default");
150
+ setBudgets(response.data || []);
151
+ } catch (error) {
152
+ alertApi.post({ message: `Error fetching budgets: ${error}`, severity: "error" });
153
+ }
154
+ };
155
+ fetchBudgets();
156
+ }, [infraWalletApi, params.name, alertApi]);
143
157
  useEffect(() => {
144
158
  if (params.selectedView) {
145
159
  switch (params.selectedView) {
@@ -239,6 +253,9 @@ const ReportsComponent = (props) => {
239
253
  group: item.group,
240
254
  data: rearrangeData(item, periods)
241
255
  })),
256
+ budgets,
257
+ forecasts,
258
+ monthRange,
242
259
  height: 450,
243
260
  highlightedItem,
244
261
  highlightedItemSetter: setHighlightedItem
@@ -1 +1 @@
1
- {"version":3,"file":"ReportsComponent.esm.js","sources":["../../../src/components/ReportsComponent/ReportsComponent.tsx"],"sourcesContent":["import { Content, Header, HeaderTabs, Page } from '@backstage/core-components';\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\nimport Accordion from '@mui/material/Accordion';\nimport AccordionDetails from '@mui/material/AccordionDetails';\nimport AccordionSummary from '@mui/material/AccordionSummary';\nimport Chip from '@mui/material/Chip';\nimport Grid from '@mui/material/Grid';\nimport Typography from '@mui/material/Typography';\nimport { addMonths, endOfMonth, startOfMonth } from 'date-fns';\nimport { default as React, useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport {\n aggregateCostReports,\n filterCostReports,\n getAllReportTags,\n getPeriodStrings,\n mergeCostReports,\n} from '../../api/functions';\nimport { CloudProviderError, Filters, Metric, Report, Tag } from '../../api/types';\nimport { useInfraWalletLuceneParams } from '../../hooks/useInfraWalletLuceneParams';\nimport { Budgets } from '../Budgets';\nimport { ColumnsChartComponent } from '../ColumnsChartComponent';\nimport { CostReportsTableComponent } from '../CostReportsTableComponent';\nimport { CustomCostsComponent } from '../CustomCostsComponent';\nimport { ErrorsAlertComponent } from '../ErrorsAlertComponent';\nimport { FiltersComponent } from '../FiltersComponent';\nimport { PieChartComponent } from '../PieChartComponent';\nimport { SettingsComponent } from '../SettingsComponent';\nimport { TopbarComponent } from '../TopbarComponent';\nimport { MonthRange } from '../types';\n\nexport interface ReportsComponentProps {\n title?: string;\n subTitle?: string;\n}\n\nconst getTotalCost = (report: Report): number => {\n let total = 0;\n Object.keys(report.reports).forEach((s: string) => {\n total += report.reports[s];\n });\n return total;\n};\n\nconst rearrangeData = (report: Report, periods: string[]): any[] => {\n const costs: any[] = [];\n periods.forEach((s: string) => {\n if (report.reports[s] !== undefined) {\n costs.push(report.reports[s]);\n } else {\n costs.push(null);\n }\n });\n return costs;\n};\n\nconst checkIfFiltersActivated = (filters: Filters): boolean => {\n let activated = false;\n Object.keys(filters).forEach((key: string) => {\n if (filters[key].length > 0) {\n activated = true;\n }\n });\n return activated;\n};\n\nexport const ReportsComponent = (props: ReportsComponentProps) => {\n const { title, subTitle } = props;\n const configApi = useApi(configApiRef);\n const params = useParams();\n\n let defaultGroupBy = configApi.getOptionalString('infraWallet.settings.defaultGroupBy') ?? 'none';\n const budgetsEnabled = configApi.getOptionalBoolean('infraWallet.settings.budgets.enabled') ?? true;\n const customCostsEnabled = configApi.getOptionalBoolean('infraWallet.settings.customCosts.enabled') ?? true;\n const businessMetricsEnabled = configApi.getOptionalBoolean('infraWallet.settings.businessMetrics.enabled') ?? true;\n const tabsToShow = ['Overview'];\n if (budgetsEnabled) {\n tabsToShow.push('Budgets');\n }\n if (customCostsEnabled) {\n tabsToShow.push('Custom Costs');\n }\n if (businessMetricsEnabled) {\n tabsToShow.push('Business Metrics');\n }\n // \"name\" is renamed to \"account\", make it backward compatibility\n if (defaultGroupBy === 'name') {\n defaultGroupBy = 'account';\n }\n\n const defaultShowLastXMonths = configApi.getOptionalNumber('infraWallet.settings.defaultShowLastXMonths') ?? 3;\n\n const MERGE_THRESHOLD = 8;\n\n // Initialize URL search params hook with defaults\n const defaultMonthRange: MonthRange = {\n startMonth: startOfMonth(addMonths(new Date(), defaultShowLastXMonths * -1 + 1)),\n endMonth: endOfMonth(new Date()),\n };\n\n const { getInitialState, updateUrlState, isInitialMount } = useInfraWalletLuceneParams({\n defaultFilters: {},\n defaultTags: [],\n defaultMonthRange,\n defaultGranularity: 'monthly',\n defaultAggregatedBy: defaultGroupBy,\n });\n\n // Get initial state from URL or defaults\n const initialState = getInitialState();\n\n const [selectedView, setSelectedView] = useState<string>('Overview');\n\n const [selectedTags, setSelectedTags] = useState<Tag[]>(initialState.selectedTags);\n const [reports, setReports] = useState<Report[] | undefined>(undefined);\n const [metrics, setMetrics] = useState<Metric[]>([]);\n const [filters, setFilters] = useState<Filters>(initialState.filters);\n const [cloudProviderErrors, setCloudProviderErrors] = useState<CloudProviderError[]>([]);\n const [reportsAggregated, setReportsAggregated] = useState<Report[] | undefined>(undefined);\n const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState<Report[] | undefined>(undefined);\n const [reportTags, setReportTags] = useState<string[]>([]);\n const [granularity, setGranularity] = useState<string>(initialState.granularity);\n const [aggregatedBy, setAggregatedBy] = useState<string>(initialState.aggregatedBy);\n const [groups] = useState<string>('');\n const [monthRange, setMonthRange] = useState<MonthRange>(initialState.monthRange);\n const [periods, setPeriods] = useState<string[]>([]);\n const [highlightedItem, setHighlightedItem] = useState<string | undefined>(undefined);\n\n const alertApi = useApi(alertApiRef);\n const infraWalletApi = useApi(infraWalletApiRef);\n\n const fetchCostReportsCallback = useCallback(async () => {\n setReportsAggregated(undefined);\n setReportsAggregatedAndMerged(undefined);\n await infraWalletApi\n .getCostReports('', selectedTags, groups, granularity, monthRange.startMonth, monthRange.endMonth)\n .then(reportsResponse => {\n if (reportsResponse.data) {\n setReports(reportsResponse.data);\n setPeriods(getPeriodStrings(granularity, monthRange.startMonth, monthRange.endMonth));\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n setCloudProviderErrors(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [groups, monthRange, granularity, selectedTags, infraWalletApi, alertApi]);\n\n const fetchMetricsCallback = useCallback(async () => {\n await infraWalletApi\n .getMetrics(params.name ?? 'default', granularity, monthRange.startMonth, monthRange.endMonth)\n .then(metricsResponse => {\n if (metricsResponse.data && metricsResponse.data.length > 0) {\n setMetrics(metricsResponse.data);\n }\n if (metricsResponse.status === 207 && metricsResponse.errors) {\n setCloudProviderErrors(metricsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [params.name, monthRange, granularity, infraWalletApi, alertApi]);\n\n useEffect(() => {\n if (reports !== undefined) {\n const filteredReports = filterCostReports(reports, filters);\n const aggregatedReports = aggregateCostReports(filteredReports, aggregatedBy);\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports, MERGE_THRESHOLD);\n const allTags = getAllReportTags(reports);\n setReportsAggregated(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n setReportTags(allTags);\n }\n }, [filters, reports, aggregatedBy, granularity, monthRange]);\n\n useEffect(() => {\n fetchCostReportsCallback();\n fetchMetricsCallback();\n }, [fetchCostReportsCallback, fetchMetricsCallback]);\n\n // provide a way for users to access these tabs, if they are configfured to be hidden\n useEffect(() => {\n if (params.selectedView) {\n switch (params.selectedView) {\n case 'budgets': {\n setSelectedView('Budgets');\n break;\n }\n case 'custom_costs': {\n setSelectedView('Custom Costs');\n break;\n }\n case 'business_metrics': {\n setSelectedView('Business Metrics');\n break;\n }\n default: {\n setSelectedView('Overview');\n break;\n }\n }\n }\n }, [params]);\n\n // Sync state changes to URL (skip initial mount to avoid overwriting URL params)\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ filters });\n }\n }, [filters, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ selectedTags });\n }\n }, [selectedTags, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ monthRange });\n }\n }, [monthRange, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ granularity });\n }\n }, [granularity, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ aggregatedBy });\n }\n }, [aggregatedBy, updateUrlState, isInitialMount]);\n\n return (\n <Page themeId=\"tool\">\n <Header title={title ?? 'InfraWallet'} subtitle={subTitle ?? ''} />\n <HeaderTabs\n tabs={tabsToShow.map(tab => {\n return { id: tab, label: tab };\n })}\n onChange={index => setSelectedView(tabsToShow[index])}\n />\n <Content>\n <Grid container spacing={3}>\n {cloudProviderErrors.length > 0 && (\n <Grid item xs={12}>\n <ErrorsAlertComponent errors={cloudProviderErrors} />\n </Grid>\n )}\n {selectedView === 'Overview' && (\n <>\n <Grid item xs={12}>\n <TopbarComponent\n aggregatedBy={aggregatedBy}\n aggregatedBySetter={setAggregatedBy}\n tags={reportTags}\n monthRange={monthRange}\n monthRangeSetter={setMonthRange}\n />\n </Grid>\n <Grid item xs={12}>\n <Accordion>\n <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls=\"filters-content\" id=\"filters-header\">\n <Typography>\n Filters {checkIfFiltersActivated(filters) && <Chip size=\"small\" label=\"active\" color=\"primary\" />}\n </Typography>\n </AccordionSummary>\n <AccordionDetails>\n <FiltersComponent\n reports={reports}\n filters={filters}\n monthRange={monthRange}\n filtersSetter={setFilters}\n selectedTags={selectedTags}\n selectedTagsSetter={setSelectedTags}\n providerErrorsSetter={setCloudProviderErrors}\n />\n </AccordionDetails>\n </Accordion>\n </Grid>\n <Grid item xs={12} md={4} lg={3}>\n <PieChartComponent\n categories={\n reportsAggregatedAndMerged ? reportsAggregatedAndMerged.map((item: any) => item.id) : undefined\n }\n series={\n reportsAggregatedAndMerged\n ? reportsAggregatedAndMerged.map((item: any) => getTotalCost(item))\n : undefined\n }\n height={450}\n highlightedItem={highlightedItem}\n highlightedItemSetter={setHighlightedItem}\n />\n </Grid>\n <Grid item xs={12} md={8} lg={9}>\n <ColumnsChartComponent\n granularity={granularity}\n granularitySetter={setGranularity}\n periods={periods}\n costs={\n reportsAggregatedAndMerged\n ? reportsAggregatedAndMerged.map((item: any) => ({\n name: item.id,\n data: rearrangeData(item, periods),\n }))\n : undefined\n }\n metrics={metrics.map((item: any) => ({\n name: item.name,\n group: item.group,\n data: rearrangeData(item, periods),\n }))}\n height={450}\n highlightedItem={highlightedItem}\n highlightedItemSetter={setHighlightedItem}\n />\n </Grid>\n <Grid item xs={12}>\n <CostReportsTableComponent reports={reportsAggregated} aggregatedBy={aggregatedBy} periods={periods} />\n </Grid>\n </>\n )}\n {selectedView === 'Budgets' && (\n <Grid item xs={12}>\n <Budgets providerErrorsSetter={setCloudProviderErrors} />\n </Grid>\n )}\n {selectedView === 'Custom Costs' && (\n <Grid item xs={12}>\n <CustomCostsComponent />\n </Grid>\n )}\n {selectedView === 'Business Metrics' && (\n <Grid item xs={12}>\n <SettingsComponent />\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ReportsComponent.esm.js","sources":["../../../src/components/ReportsComponent/ReportsComponent.tsx"],"sourcesContent":["import { Content, Header, HeaderTabs, Page } from '@backstage/core-components';\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\nimport Accordion from '@mui/material/Accordion';\nimport AccordionDetails from '@mui/material/AccordionDetails';\nimport AccordionSummary from '@mui/material/AccordionSummary';\nimport Chip from '@mui/material/Chip';\nimport Grid from '@mui/material/Grid';\nimport Typography from '@mui/material/Typography';\nimport { addMonths, endOfMonth, startOfMonth } from 'date-fns';\nimport { default as React, useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { infraWalletApiRef } from '../../api/InfraWalletApi';\nimport {\n aggregateCostReports,\n filterCostReports,\n getAllReportTags,\n getPeriodStrings,\n mergeCostReports,\n} from '../../api/functions';\nimport { CloudProviderError, Filters, Metric, Report, Tag, Budget } from '../../api/types';\nimport { useInfraWalletLuceneParams } from '../../hooks/useInfraWalletLuceneParams';\nimport { Budgets } from '../Budgets';\nimport { ColumnsChartComponent } from '../ColumnsChartComponent';\nimport { CostReportsTableComponent } from '../CostReportsTableComponent';\nimport { CustomCostsComponent } from '../CustomCostsComponent';\nimport { ErrorsAlertComponent } from '../ErrorsAlertComponent';\nimport { FiltersComponent } from '../FiltersComponent';\nimport { PieChartComponent } from '../PieChartComponent';\nimport { SettingsComponent } from '../SettingsComponent';\nimport { TopbarComponent } from '../TopbarComponent';\nimport { MonthRange } from '../types';\n\nexport interface ReportsComponentProps {\n title?: string;\n subTitle?: string;\n}\n\nconst getTotalCost = (report: Report): number => {\n let total = 0;\n Object.keys(report.reports).forEach((s: string) => {\n total += report.reports[s];\n });\n return total;\n};\n\nconst rearrangeData = (report: Report, periods: string[]): any[] => {\n const costs: any[] = [];\n periods.forEach((s: string) => {\n if (report.reports[s] !== undefined) {\n costs.push(report.reports[s]);\n } else {\n costs.push(null);\n }\n });\n return costs;\n};\n\nconst checkIfFiltersActivated = (filters: Filters): boolean => {\n let activated = false;\n Object.keys(filters).forEach((key: string) => {\n if (filters[key].length > 0) {\n activated = true;\n }\n });\n return activated;\n};\n\nexport const ReportsComponent = (props: ReportsComponentProps) => {\n const { title, subTitle } = props;\n const configApi = useApi(configApiRef);\n const params = useParams();\n\n let defaultGroupBy = configApi.getOptionalString('infraWallet.settings.defaultGroupBy') ?? 'none';\n const budgetsEnabled = configApi.getOptionalBoolean('infraWallet.settings.budgets.enabled') ?? true;\n const customCostsEnabled = configApi.getOptionalBoolean('infraWallet.settings.customCosts.enabled') ?? true;\n const businessMetricsEnabled = configApi.getOptionalBoolean('infraWallet.settings.businessMetrics.enabled') ?? true;\n const tabsToShow = ['Overview'];\n if (budgetsEnabled) {\n tabsToShow.push('Budgets');\n }\n if (customCostsEnabled) {\n tabsToShow.push('Custom Costs');\n }\n if (businessMetricsEnabled) {\n tabsToShow.push('Business Metrics');\n }\n // \"name\" is renamed to \"account\", make it backward compatibility\n if (defaultGroupBy === 'name') {\n defaultGroupBy = 'account';\n }\n\n const defaultShowLastXMonths = configApi.getOptionalNumber('infraWallet.settings.defaultShowLastXMonths') ?? 3;\n\n const MERGE_THRESHOLD = 8;\n\n // Initialize URL search params hook with defaults\n const defaultMonthRange: MonthRange = {\n startMonth: startOfMonth(addMonths(new Date(), defaultShowLastXMonths * -1 + 1)),\n endMonth: endOfMonth(new Date()),\n };\n\n const { getInitialState, updateUrlState, isInitialMount } = useInfraWalletLuceneParams({\n defaultFilters: {},\n defaultTags: [],\n defaultMonthRange,\n defaultGranularity: 'monthly',\n defaultAggregatedBy: defaultGroupBy,\n });\n\n // Get initial state from URL or defaults\n const initialState = getInitialState();\n\n const [selectedView, setSelectedView] = useState<string>('Overview');\n\n const [selectedTags, setSelectedTags] = useState<Tag[]>(initialState.selectedTags);\n const [reports, setReports] = useState<Report[] | undefined>(undefined);\n const [metrics, setMetrics] = useState<Metric[]>([]);\n const [budgets, setBudgets] = useState<Budget[]>([]);\n const [forecasts, setForecasts] = useState<Record<string, number>>({});\n const [filters, setFilters] = useState<Filters>(initialState.filters);\n const [cloudProviderErrors, setCloudProviderErrors] = useState<CloudProviderError[]>([]);\n const [reportsAggregated, setReportsAggregated] = useState<Report[] | undefined>(undefined);\n const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState<Report[] | undefined>(undefined);\n const [reportTags, setReportTags] = useState<string[]>([]);\n const [granularity, setGranularity] = useState<string>(initialState.granularity);\n const [aggregatedBy, setAggregatedBy] = useState<string>(initialState.aggregatedBy);\n const [groups] = useState<string>('');\n const [monthRange, setMonthRange] = useState<MonthRange>(initialState.monthRange);\n const [periods, setPeriods] = useState<string[]>([]);\n const [highlightedItem, setHighlightedItem] = useState<string | undefined>(undefined);\n\n const alertApi = useApi(alertApiRef);\n const infraWalletApi = useApi(infraWalletApiRef);\n\n const fetchCostReportsCallback = useCallback(async () => {\n setReportsAggregated(undefined);\n setReportsAggregatedAndMerged(undefined);\n await infraWalletApi\n .getCostReports('', selectedTags, groups, granularity, monthRange.startMonth, monthRange.endMonth)\n .then(reportsResponse => {\n if (reportsResponse.data) {\n setReports(reportsResponse.data);\n setPeriods(getPeriodStrings(granularity, monthRange.startMonth, monthRange.endMonth));\n setForecasts(reportsResponse.forecasts || {});\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n setCloudProviderErrors(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [groups, monthRange, granularity, selectedTags, infraWalletApi, alertApi]);\n\n const fetchMetricsCallback = useCallback(async () => {\n await infraWalletApi\n .getMetrics(params.name ?? 'default', granularity, monthRange.startMonth, monthRange.endMonth)\n .then(metricsResponse => {\n if (metricsResponse.data && metricsResponse.data.length > 0) {\n setMetrics(metricsResponse.data);\n }\n if (metricsResponse.status === 207 && metricsResponse.errors) {\n setCloudProviderErrors(metricsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [params.name, monthRange, granularity, infraWalletApi, alertApi]);\n\n useEffect(() => {\n if (reports !== undefined) {\n const filteredReports = filterCostReports(reports, filters);\n const aggregatedReports = aggregateCostReports(filteredReports, aggregatedBy);\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports, MERGE_THRESHOLD);\n const allTags = getAllReportTags(reports);\n setReportsAggregated(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n setReportTags(allTags);\n }\n }, [filters, reports, aggregatedBy, granularity, monthRange]);\n\n useEffect(() => {\n fetchCostReportsCallback();\n fetchMetricsCallback();\n }, [fetchCostReportsCallback, fetchMetricsCallback]);\n\n useEffect(() => {\n const fetchBudgets = async () => {\n try {\n const response = await infraWalletApi.getBudgets(params.name ?? 'default');\n setBudgets(response.data || []);\n } catch (error) {\n alertApi.post({ message: `Error fetching budgets: ${error}`, severity: 'error' });\n }\n };\n fetchBudgets();\n }, [infraWalletApi, params.name, alertApi]);\n\n // provide a way for users to access these tabs, if they are configfured to be hidden\n useEffect(() => {\n if (params.selectedView) {\n switch (params.selectedView) {\n case 'budgets': {\n setSelectedView('Budgets');\n break;\n }\n case 'custom_costs': {\n setSelectedView('Custom Costs');\n break;\n }\n case 'business_metrics': {\n setSelectedView('Business Metrics');\n break;\n }\n default: {\n setSelectedView('Overview');\n break;\n }\n }\n }\n }, [params]);\n\n // Sync state changes to URL (skip initial mount to avoid overwriting URL params)\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ filters });\n }\n }, [filters, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ selectedTags });\n }\n }, [selectedTags, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ monthRange });\n }\n }, [monthRange, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ granularity });\n }\n }, [granularity, updateUrlState, isInitialMount]);\n\n useEffect(() => {\n if (!isInitialMount) {\n updateUrlState({ aggregatedBy });\n }\n }, [aggregatedBy, updateUrlState, isInitialMount]);\n\n return (\n <Page themeId=\"tool\">\n <Header title={title ?? 'InfraWallet'} subtitle={subTitle ?? ''} />\n <HeaderTabs\n tabs={tabsToShow.map(tab => {\n return { id: tab, label: tab };\n })}\n onChange={index => setSelectedView(tabsToShow[index])}\n />\n <Content>\n <Grid container spacing={3}>\n {cloudProviderErrors.length > 0 && (\n <Grid item xs={12}>\n <ErrorsAlertComponent errors={cloudProviderErrors} />\n </Grid>\n )}\n {selectedView === 'Overview' && (\n <>\n <Grid item xs={12}>\n <TopbarComponent\n aggregatedBy={aggregatedBy}\n aggregatedBySetter={setAggregatedBy}\n tags={reportTags}\n monthRange={monthRange}\n monthRangeSetter={setMonthRange}\n />\n </Grid>\n <Grid item xs={12}>\n <Accordion>\n <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls=\"filters-content\" id=\"filters-header\">\n <Typography>\n Filters {checkIfFiltersActivated(filters) && <Chip size=\"small\" label=\"active\" color=\"primary\" />}\n </Typography>\n </AccordionSummary>\n <AccordionDetails>\n <FiltersComponent\n reports={reports}\n filters={filters}\n monthRange={monthRange}\n filtersSetter={setFilters}\n selectedTags={selectedTags}\n selectedTagsSetter={setSelectedTags}\n providerErrorsSetter={setCloudProviderErrors}\n />\n </AccordionDetails>\n </Accordion>\n </Grid>\n <Grid item xs={12} md={4} lg={3}>\n <PieChartComponent\n categories={\n reportsAggregatedAndMerged ? reportsAggregatedAndMerged.map((item: any) => item.id) : undefined\n }\n series={\n reportsAggregatedAndMerged\n ? reportsAggregatedAndMerged.map((item: any) => getTotalCost(item))\n : undefined\n }\n height={450}\n highlightedItem={highlightedItem}\n highlightedItemSetter={setHighlightedItem}\n />\n </Grid>\n <Grid item xs={12} md={8} lg={9}>\n <ColumnsChartComponent\n granularity={granularity}\n granularitySetter={setGranularity}\n periods={periods}\n costs={\n reportsAggregatedAndMerged\n ? reportsAggregatedAndMerged.map((item: any) => ({\n name: item.id,\n data: rearrangeData(item, periods),\n }))\n : undefined\n }\n metrics={metrics.map((item: any) => ({\n name: item.name,\n group: item.group,\n data: rearrangeData(item, periods),\n }))}\n budgets={budgets}\n forecasts={forecasts}\n monthRange={monthRange}\n height={450}\n highlightedItem={highlightedItem}\n highlightedItemSetter={setHighlightedItem}\n />\n </Grid>\n <Grid item xs={12}>\n <CostReportsTableComponent reports={reportsAggregated} aggregatedBy={aggregatedBy} periods={periods} />\n </Grid>\n </>\n )}\n {selectedView === 'Budgets' && (\n <Grid item xs={12}>\n <Budgets providerErrorsSetter={setCloudProviderErrors} />\n </Grid>\n )}\n {selectedView === 'Custom Costs' && (\n <Grid item xs={12}>\n <CustomCostsComponent />\n </Grid>\n )}\n {selectedView === 'Business Metrics' && (\n <Grid item xs={12}>\n <SettingsComponent />\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAM,YAAA,GAAe,CAAC,MAAA,KAA2B;AAC/C,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,MAAA,CAAO,KAAK,MAAA,CAAO,OAAO,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA,KAAc;AACjD,IAAA,KAAA,IAAS,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,GAC1B,CAAA;AACD,EAAA,OAAO,KAAA;AACT,CAAA;AAEA,MAAM,aAAA,GAAgB,CAAC,MAAA,EAAgB,OAAA,KAA6B;AAClE,EAAA,MAAM,QAAe,EAAC;AACtB,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAc;AAC7B,IAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,KAAM,MAAA,EAAW;AACnC,MAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,KAC9B,MAAO;AACL,MAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA;AACjB,GACD,CAAA;AACD,EAAA,OAAO,KAAA;AACT,CAAA;AAEA,MAAM,uBAAA,GAA0B,CAAC,OAAA,KAA8B;AAC7D,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAgB;AAC5C,IAAA,IAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,MAAA,GAAS,CAAA,EAAG;AAC3B,MAAA,SAAA,GAAY,IAAA;AAAA;AACd,GACD,CAAA;AACD,EAAA,OAAO,SAAA;AACT,CAAA;AAEO,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAiC;AAChE,EAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI,KAAA;AAC5B,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,IAAI,cAAA,GAAiB,SAAA,CAAU,iBAAA,CAAkB,qCAAqC,CAAA,IAAK,MAAA;AAC3F,EAAA,MAAM,cAAA,GAAiB,SAAA,CAAU,kBAAA,CAAmB,sCAAsC,CAAA,IAAK,IAAA;AAC/F,EAAA,MAAM,kBAAA,GAAqB,SAAA,CAAU,kBAAA,CAAmB,0CAA0C,CAAA,IAAK,IAAA;AACvG,EAAA,MAAM,sBAAA,GAAyB,SAAA,CAAU,kBAAA,CAAmB,8CAA8C,CAAA,IAAK,IAAA;AAC/G,EAAA,MAAM,UAAA,GAAa,CAAC,UAAU,CAAA;AAC9B,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,UAAA,CAAW,KAAK,SAAS,CAAA;AAAA;AAE3B,EAAA,IAAI,kBAAA,EAAoB;AACtB,IAAA,UAAA,CAAW,KAAK,cAAc,CAAA;AAAA;AAEhC,EAAA,IAAI,sBAAA,EAAwB;AAC1B,IAAA,UAAA,CAAW,KAAK,kBAAkB,CAAA;AAAA;AAGpC,EAAA,IAAI,mBAAmB,MAAA,EAAQ;AAC7B,IAAA,cAAA,GAAiB,SAAA;AAAA;AAGnB,EAAA,MAAM,sBAAA,GAAyB,SAAA,CAAU,iBAAA,CAAkB,6CAA6C,CAAA,IAAK,CAAA;AAE7G,EAAA,MAAM,eAAA,GAAkB,CAAA;AAGxB,EAAA,MAAM,iBAAA,GAAgC;AAAA,IACpC,UAAA,EAAY,aAAa,SAAA,iBAAU,IAAI,MAAK,EAAG,sBAAA,GAAyB,EAAA,GAAK,CAAC,CAAC,CAAA;AAAA,IAC/E,QAAA,EAAU,UAAA,iBAAW,IAAI,IAAA,EAAM;AAAA,GACjC;AAEA,EAAA,MAAM,EAAE,eAAA,EAAiB,cAAA,EAAgB,cAAA,KAAmB,0BAAA,CAA2B;AAAA,IACrF,gBAAgB,EAAC;AAAA,IACjB,aAAa,EAAC;AAAA,IACd,iBAAA;AAAA,IACA,kBAAA,EAAoB,SAAA;AAAA,IACpB,mBAAA,EAAqB;AAAA,GACtB,CAAA;AAGD,EAAA,MAAM,eAAe,eAAA,EAAgB;AAErC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAiB,UAAU,CAAA;AAEnE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAgB,aAAa,YAAY,CAAA;AACjF,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA+B,MAAS,CAAA;AACtE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA,CAAiC,EAAE,CAAA;AACrE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAkB,aAAa,OAAO,CAAA;AACpE,EAAA,MAAM,CAAC,mBAAA,EAAqB,sBAAsB,CAAA,GAAI,QAAA,CAA+B,EAAE,CAAA;AACvF,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAA+B,MAAS,CAAA;AAC1F,EAAA,MAAM,CAAC,0BAAA,EAA4B,6BAA6B,CAAA,GAAI,SAA+B,MAAS,CAAA;AAC5G,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACzD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA,CAAiB,aAAa,WAAW,CAAA;AAC/E,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAiB,aAAa,YAAY,CAAA;AAClF,EAAA,MAAM,CAAC,MAAM,CAAA,GAAI,QAAA,CAAiB,EAAE,CAAA;AACpC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAqB,aAAa,UAAU,CAAA;AAChF,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAA6B,MAAS,CAAA;AAEpF,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,cAAA,GAAiB,OAAO,iBAAiB,CAAA;AAE/C,EAAA,MAAM,wBAAA,GAA2B,YAAY,YAAY;AACvD,IAAA,oBAAA,CAAqB,MAAS,CAAA;AAC9B,IAAA,6BAAA,CAA8B,MAAS,CAAA;AACvC,IAAA,MAAM,cAAA,CACH,cAAA,CAAe,EAAA,EAAI,YAAA,EAAc,MAAA,EAAQ,WAAA,EAAa,UAAA,CAAW,UAAA,EAAY,UAAA,CAAW,QAAQ,CAAA,CAChG,IAAA,CAAK,CAAA,eAAA,KAAmB;AACvB,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,QAAA,UAAA,CAAW,gBAAgB,IAAI,CAAA;AAC/B,QAAA,UAAA,CAAW,iBAAiB,WAAA,EAAa,UAAA,CAAW,UAAA,EAAY,UAAA,CAAW,QAAQ,CAAC,CAAA;AACpF,QAAA,YAAA,CAAa,eAAA,CAAgB,SAAA,IAAa,EAAE,CAAA;AAAA;AAE9C,MAAA,IAAI,eAAA,CAAgB,MAAA,KAAW,GAAA,IAAO,eAAA,CAAgB,MAAA,EAAQ;AAC5D,QAAA,sBAAA,CAAuB,gBAAgB,MAAM,CAAA;AAAA;AAC/C,KACD,CAAA,CACA,KAAA,CAAM,CAAA,CAAA,KAAK,SAAS,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,EAAE,OAAO,CAAA,CAAA,EAAI,QAAA,EAAU,OAAA,EAAS,CAAC,CAAA;AAAA,GAC7E,EAAG,CAAC,MAAA,EAAQ,UAAA,EAAY,aAAa,YAAA,EAAc,cAAA,EAAgB,QAAQ,CAAC,CAAA;AAE5E,EAAA,MAAM,oBAAA,GAAuB,YAAY,YAAY;AACnD,IAAA,MAAM,cAAA,CACH,UAAA,CAAW,MAAA,CAAO,IAAA,IAAQ,SAAA,EAAW,WAAA,EAAa,UAAA,CAAW,UAAA,EAAY,UAAA,CAAW,QAAQ,CAAA,CAC5F,IAAA,CAAK,CAAA,eAAA,KAAmB;AACvB,MAAA,IAAI,eAAA,CAAgB,IAAA,IAAQ,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA,EAAG;AAC3D,QAAA,UAAA,CAAW,gBAAgB,IAAI,CAAA;AAAA;AAEjC,MAAA,IAAI,eAAA,CAAgB,MAAA,KAAW,GAAA,IAAO,eAAA,CAAgB,MAAA,EAAQ;AAC5D,QAAA,sBAAA,CAAuB,gBAAgB,MAAM,CAAA;AAAA;AAC/C,KACD,CAAA,CACA,KAAA,CAAM,CAAA,CAAA,KAAK,SAAS,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,EAAE,OAAO,CAAA,CAAA,EAAI,QAAA,EAAU,OAAA,EAAS,CAAC,CAAA;AAAA,GAC7E,EAAG,CAAC,MAAA,CAAO,IAAA,EAAM,YAAY,WAAA,EAAa,cAAA,EAAgB,QAAQ,CAAC,CAAA;AAEnE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,MAAM,eAAA,GAAkB,iBAAA,CAAkB,OAAA,EAAS,OAAO,CAAA;AAC1D,MAAA,MAAM,iBAAA,GAAoB,oBAAA,CAAqB,eAAA,EAAiB,YAAY,CAAA;AAC5E,MAAA,MAAM,0BAAA,GAA6B,gBAAA,CAAiB,iBAAA,EAAmB,eAAe,CAAA;AACtF,MAAA,MAAM,OAAA,GAAU,iBAAiB,OAAO,CAAA;AACxC,MAAA,oBAAA,CAAqB,iBAAiB,CAAA;AACtC,MAAA,6BAAA,CAA8B,0BAA0B,CAAA;AACxD,MAAA,aAAA,CAAc,OAAO,CAAA;AAAA;AACvB,KACC,CAAC,OAAA,EAAS,SAAS,YAAA,EAAc,WAAA,EAAa,UAAU,CAAC,CAAA;AAE5D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,wBAAA,EAAyB;AACzB,IAAA,oBAAA,EAAqB;AAAA,GACvB,EAAG,CAAC,wBAAA,EAA0B,oBAAoB,CAAC,CAAA;AAEnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,eAAe,YAAY;AAC/B,MAAA,IAAI;AACF,QAAA,MAAM,WAAW,MAAM,cAAA,CAAe,UAAA,CAAW,MAAA,CAAO,QAAQ,SAAS,CAAA;AACzE,QAAA,UAAA,CAAW,QAAA,CAAS,IAAA,IAAQ,EAAE,CAAA;AAAA,eACvB,KAAA,EAAO;AACd,QAAA,QAAA,CAAS,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,wBAAA,EAA2B,KAAK,CAAA,CAAA,EAAI,QAAA,EAAU,SAAS,CAAA;AAAA;AAClF,KACF;AACA,IAAA,YAAA,EAAa;AAAA,KACZ,CAAC,cAAA,EAAgB,MAAA,CAAO,IAAA,EAAM,QAAQ,CAAC,CAAA;AAG1C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,QAAQ,OAAO,YAAA;AAAc,QAC3B,KAAK,SAAA,EAAW;AACd,UAAA,eAAA,CAAgB,SAAS,CAAA;AACzB,UAAA;AAAA;AACF,QACA,KAAK,cAAA,EAAgB;AACnB,UAAA,eAAA,CAAgB,cAAc,CAAA;AAC9B,UAAA;AAAA;AACF,QACA,KAAK,kBAAA,EAAoB;AACvB,UAAA,eAAA,CAAgB,kBAAkB,CAAA;AAClC,UAAA;AAAA;AACF,QACA,SAAS;AACP,UAAA,eAAA,CAAgB,UAAU,CAAA;AAC1B,UAAA;AAAA;AACF;AACF;AACF,GACF,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,cAAA,CAAe,EAAE,SAAS,CAAA;AAAA;AAC5B,GACF,EAAG,CAAC,OAAA,EAAS,cAAA,EAAgB,cAAc,CAAC,CAAA;AAE5C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,cAAA,CAAe,EAAE,cAAc,CAAA;AAAA;AACjC,GACF,EAAG,CAAC,YAAA,EAAc,cAAA,EAAgB,cAAc,CAAC,CAAA;AAEjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,cAAA,CAAe,EAAE,YAAY,CAAA;AAAA;AAC/B,GACF,EAAG,CAAC,UAAA,EAAY,cAAA,EAAgB,cAAc,CAAC,CAAA;AAE/C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,cAAA,CAAe,EAAE,aAAa,CAAA;AAAA;AAChC,GACF,EAAG,CAAC,WAAA,EAAa,cAAA,EAAgB,cAAc,CAAC,CAAA;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,cAAA,CAAe,EAAE,cAAc,CAAA;AAAA;AACjC,GACF,EAAG,CAAC,YAAA,EAAc,cAAA,EAAgB,cAAc,CAAC,CAAA;AAEjD,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EAAA,kBACZ,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,KAAA,EAAO,KAAA,IAAS,aAAA,EAAe,QAAA,EAAU,QAAA,IAAY,EAAA,EAAI,CAAA,kBACjE,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAM,UAAA,CAAW,GAAA,CAAI,CAAA,GAAA,KAAO;AAC1B,QAAA,OAAO,EAAE,EAAA,EAAI,GAAA,EAAK,KAAA,EAAO,GAAA,EAAI;AAAA,OAC9B,CAAA;AAAA,MACD,QAAA,EAAU,CAAA,KAAA,KAAS,eAAA,CAAgB,UAAA,CAAW,KAAK,CAAC;AAAA;AAAA,GACtD,kBACA,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,QAAK,SAAA,EAAS,IAAA,EAAC,OAAA,EAAS,CAAA,EAAA,EACtB,mBAAA,CAAoB,MAAA,GAAS,CAAA,oBAC5B,KAAA,CAAA,aAAA,CAAC,QAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,oBAAA,EAAA,EAAqB,MAAA,EAAQ,mBAAA,EAAqB,CACrD,CAAA,EAED,YAAA,KAAiB,UAAA,oBAChB,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBACE,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,IAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA;AAAA,IAAC,eAAA;AAAA,IAAA;AAAA,MACC,YAAA;AAAA,MACA,kBAAA,EAAoB,eAAA;AAAA,MACpB,IAAA,EAAM,UAAA;AAAA,MACN,UAAA;AAAA,MACA,gBAAA,EAAkB;AAAA;AAAA,GAEtB,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,MAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,sCACE,gBAAA,EAAA,EAAiB,UAAA,sCAAa,cAAA,EAAA,IAAe,CAAA,EAAI,iBAAc,iBAAA,EAAkB,EAAA,EAAG,gBAAA,EAAA,kBACnF,KAAA,CAAA,aAAA,CAAC,kBAAW,UAAA,EACD,uBAAA,CAAwB,OAAO,CAAA,wCAAM,IAAA,EAAA,EAAK,IAAA,EAAK,OAAA,EAAQ,KAAA,EAAM,UAAS,KAAA,EAAM,SAAA,EAAU,CACjG,CACF,CAAA,sCACC,gBAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,OAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA;AAAA,MACA,aAAA,EAAe,UAAA;AAAA,MACf,YAAA;AAAA,MACA,kBAAA,EAAoB,eAAA;AAAA,MACpB,oBAAA,EAAsB;AAAA;AAAA,GAE1B,CACF,CACF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,EAAA,kBAC5B,KAAA,CAAA,aAAA;AAAA,IAAC,iBAAA;AAAA,IAAA;AAAA,MACC,UAAA,EACE,6BAA6B,0BAAA,CAA2B,GAAA,CAAI,CAAC,IAAA,KAAc,IAAA,CAAK,EAAE,CAAA,GAAI,MAAA;AAAA,MAExF,MAAA,EACE,6BACI,0BAAA,CAA2B,GAAA,CAAI,CAAC,IAAA,KAAc,YAAA,CAAa,IAAI,CAAC,CAAA,GAChE,MAAA;AAAA,MAEN,MAAA,EAAQ,GAAA;AAAA,MACR,eAAA;AAAA,MACA,qBAAA,EAAuB;AAAA;AAAA,GAE3B,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAA,kBAC5B,KAAA,CAAA,aAAA;AAAA,IAAC,qBAAA;AAAA,IAAA;AAAA,MACC,WAAA;AAAA,MACA,iBAAA,EAAmB,cAAA;AAAA,MACnB,OAAA;AAAA,MACA,KAAA,EACE,0BAAA,GACI,0BAAA,CAA2B,GAAA,CAAI,CAAC,IAAA,MAAe;AAAA,QAC7C,MAAM,IAAA,CAAK,EAAA;AAAA,QACX,IAAA,EAAM,aAAA,CAAc,IAAA,EAAM,OAAO;AAAA,QACjC,CAAA,GACF,MAAA;AAAA,MAEN,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,MAAe;AAAA,QACnC,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,IAAA,EAAM,aAAA,CAAc,IAAA,EAAM,OAAO;AAAA,OACnC,CAAE,CAAA;AAAA,MACF,OAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA;AAAA,MACA,MAAA,EAAQ,GAAA;AAAA,MACR,eAAA;AAAA,MACA,qBAAA,EAAuB;AAAA;AAAA,GAE3B,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,yBAAA,EAAA,EAA0B,OAAA,EAAS,iBAAA,EAAmB,YAAA,EAA4B,SAAkB,CACvG,CACF,CAAA,EAED,YAAA,KAAiB,SAAA,oBAChB,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAQ,oBAAA,EAAsB,wBAAwB,CACzD,CAAA,EAED,YAAA,KAAiB,cAAA,oBAChB,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,oBAAA,EAAA,IAAqB,CACxB,CAAA,EAED,iBAAiB,kBAAA,oBAChB,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAA,kBACb,KAAA,CAAA,aAAA,CAAC,iBAAA,EAAA,IAAkB,CACrB,CAEJ,CACF,CACF,CAAA;AAEJ;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electrolux-oss/plugin-infrawallet",
3
- "version": "1.1.0-20251218080406-fbaf733",
3
+ "version": "1.1.0-20260115072023-d6f33e2",
4
4
  "backstage": {
5
5
  "role": "frontend-plugin",
6
6
  "pluginId": "infrawallet",