@electrolux-oss/plugin-infrawallet 1.1.0-20251202133007-1005ef9 → 1.1.0-20251204084511-e456a8a

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.
@@ -205,6 +205,7 @@ const calculateBudgetAnalytics = (monthlyCosts, annualBudget) => {
205
205
  const daysIntoCurrentMonth = moment().date();
206
206
  const daysInCurrentMonth = moment().daysInMonth();
207
207
  let yearToDateSpent = 0;
208
+ let projectedCurrentMonthCost = 0;
208
209
  const monthlySpending = [];
209
210
  for (let month = 1; month <= currentMonth; month++) {
210
211
  const monthKey = `${currentYear}-${month.toString().padStart(2, "0")}`;
@@ -213,7 +214,7 @@ const calculateBudgetAnalytics = (monthlyCosts, annualBudget) => {
213
214
  yearToDateSpent += monthCost;
214
215
  monthlySpending.push(monthCost);
215
216
  } else if (month === currentMonth) {
216
- const projectedCurrentMonthCost = monthCost / daysIntoCurrentMonth * daysInCurrentMonth;
217
+ projectedCurrentMonthCost = monthCost / daysIntoCurrentMonth * daysInCurrentMonth;
217
218
  yearToDateSpent += monthCost;
218
219
  monthlySpending.push(projectedCurrentMonthCost);
219
220
  }
@@ -243,6 +244,7 @@ const calculateBudgetAnalytics = (monthlyCosts, annualBudget) => {
243
244
  yearToDateSpent,
244
245
  monthlyRunRate,
245
246
  projectedAnnualSpending,
247
+ projectedCurrentMonthCost,
246
248
  budgetHealthStatus,
247
249
  budgetUtilizationPercent,
248
250
  targetMonthlySpending,
@@ -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 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 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 const 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 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): 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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"}
@@ -19,7 +19,7 @@ import Skeleton from '@mui/material/Skeleton';
19
19
  import { useTheme } from '@mui/material/styles';
20
20
  import Switch from '@mui/material/Switch';
21
21
  import Typography from '@mui/material/Typography';
22
- import { useDrawingArea, ChartContainer, ChartsGrid, ChartsAxisHighlight, LinePlot, BarPlot, MarkPlot, LineHighlightPlot, ChartsReferenceLine, ChartsXAxis, ChartsYAxis, ChartsTooltip } from '@mui/x-charts';
22
+ import { useDrawingArea, ResponsiveChartContainer, ChartsGrid, ChartsAxisHighlight, LinePlot, BarPlot, MarkPlot, LineHighlightPlot, ChartsReferenceLine, ChartsXAxis, ChartsYAxis, ChartsTooltip } from '@mui/x-charts';
23
23
  import { max } from 'lodash';
24
24
  import moment from 'moment';
25
25
  import React, { useState, useCallback, useEffect } from 'react';
@@ -97,35 +97,34 @@ function getRecommendationColor(type) {
97
97
  return "info";
98
98
  }
99
99
  function BudgetChart(props) {
100
- const { width, height } = useDrawingArea();
100
+ const { height } = useDrawingArea();
101
101
  const theme = useTheme();
102
- const { provider, monthlyCosts, view } = props;
103
- const [annualBudget, setAnnualBudget] = useState(void 0);
104
- const [openManageBudget, setOpenManageBudget] = useState(false);
105
- const [refreshTrigger, setRefreshTrigger] = useState(false);
102
+ const { provider, monthlyCosts, view, budgets, setBudgets } = props;
106
103
  const infraWalletApi = useApi(infraWalletApiRef);
107
- const budgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudget?.amount || 0);
108
- useEffect(() => {
109
- const fetchBudget = async () => {
110
- const response = await infraWalletApi.getBudget("default", provider);
111
- const updatedBudget = response.data?.find((a) => a.provider.toLowerCase() === provider.toLowerCase());
112
- setAnnualBudget(updatedBudget);
113
- };
114
- fetchBudget();
115
- }, [refreshTrigger, provider, infraWalletApi]);
104
+ const annualBudget = budgets.find((b) => b.provider.toLowerCase() === provider.toLowerCase());
105
+ const annualBudgetAmount = annualBudget?.amount || 0;
106
+ const [openManageBudget, setOpenManageBudget] = useState(false);
107
+ const budgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudgetAmount);
116
108
  const updateBudget = async (event) => {
117
109
  event.preventDefault();
118
110
  const formData = new FormData(event.currentTarget);
119
- const name = annualBudget?.name || `${provider} annual budget`;
120
- const amount = formData.get("amount");
121
- const newAnnualBudget = {
111
+ const amount = Number(formData.get("amount") || 0);
112
+ const updated = {
122
113
  id: annualBudget?.id,
123
114
  provider,
124
- name,
125
- amount: amount ? Number(amount) : 0
115
+ name: annualBudget?.name || `${provider} annual budget`,
116
+ amount
126
117
  };
127
- await infraWalletApi.updateBudget("default", newAnnualBudget);
128
- setRefreshTrigger((prev) => !prev);
118
+ await infraWalletApi.updateBudget("default", updated);
119
+ setBudgets((prev) => {
120
+ const index = prev.findIndex((b) => b.provider.toLowerCase() === provider.toLowerCase());
121
+ if (index >= 0) {
122
+ const copy = [...prev];
123
+ copy[index] = { ...copy[index], ...updated };
124
+ return copy;
125
+ }
126
+ return [...prev, updated];
127
+ });
129
128
  setOpenManageBudget(false);
130
129
  };
131
130
  const nonAccumulatedCosts = [];
@@ -147,25 +146,102 @@ function BudgetChart(props) {
147
146
  accumulatedCosts.push(accumulatedCosts[accumulatedCosts.length - 1] + cost);
148
147
  }
149
148
  }
150
- let budgetAmount = annualBudget?.amount || 0;
149
+ let budgetAmount = annualBudgetAmount;
150
+ let chartSeries;
151
+ let yAxis;
151
152
  if (view === "Monthly" /* MONTHLY */) {
152
153
  budgetAmount = budgetAmount / 12;
154
+ const lastIndex = nonAccumulatedCosts.length - 1;
155
+ const lastActualCost = lastIndex >= 0 ? nonAccumulatedCosts[lastIndex] : 0;
156
+ const projectedCurrentMonthCost = budgetAnalytics.projectedCurrentMonthCost;
157
+ const projectedDelta = projectedCurrentMonthCost - lastActualCost;
158
+ const monthlyMax = max([...nonAccumulatedCosts, budgetAmount]) || 0;
159
+ chartSeries = [
160
+ {
161
+ id: "actual-spend",
162
+ yAxisKey: "spendAxis",
163
+ data: nonAccumulatedCosts,
164
+ type: "bar",
165
+ stack: "combined",
166
+ label: "Actual Spend",
167
+ color: colorList[0],
168
+ valueFormatter: (value) => formatCurrency(value || 0)
169
+ }
170
+ ];
171
+ if (lastIndex >= 0 && projectedDelta > 0) {
172
+ const deltaData = nonAccumulatedCosts.map((_, i) => i === lastIndex ? projectedDelta : 0);
173
+ chartSeries.push({
174
+ id: "projected-delta",
175
+ yAxisKey: "deltaAxis",
176
+ data: deltaData,
177
+ type: "bar",
178
+ stack: "combined",
179
+ label: "Projected Delta",
180
+ color: theme.palette.warning.main,
181
+ valueFormatter: (value) => {
182
+ if (value === 0) return null;
183
+ return formatCurrency(value || 0);
184
+ }
185
+ });
186
+ }
187
+ yAxis = [
188
+ {
189
+ id: "spendAxis",
190
+ min: 0,
191
+ max: monthlyMax,
192
+ valueFormatter: (value) => formatCurrency(value || 0),
193
+ colorMap: {
194
+ type: "piecewise",
195
+ thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],
196
+ colors: [colorList[0], theme.palette.error.main]
197
+ }
198
+ },
199
+ {
200
+ id: "deltaAxis",
201
+ min: 0,
202
+ max: monthlyMax
203
+ }
204
+ ];
205
+ } else {
206
+ chartSeries = [
207
+ {
208
+ id: "yearAxis",
209
+ yAxisKey: "spendAxis",
210
+ data: accumulatedCosts,
211
+ type: "line",
212
+ showMark: false,
213
+ valueFormatter: (value) => formatCurrency(value || 0)
214
+ }
215
+ ];
216
+ yAxis = [
217
+ {
218
+ id: "spendAxis",
219
+ min: 0,
220
+ max: max([...accumulatedCosts, budgetAmount, budgetAnalytics.confidenceRange.high]),
221
+ valueFormatter: (value) => formatCurrency(value || 0),
222
+ colorMap: {
223
+ type: "piecewise",
224
+ thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],
225
+ colors: [colorList[0], theme.palette.error.main]
226
+ }
227
+ }
228
+ ];
153
229
  }
154
- return /* @__PURE__ */ React.createElement(Paper, { sx: { padding: 2 } }, /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2 } }, /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(ProviderIcon, { provider }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", sx: { ml: 1, fontWeight: "bold" } }, provider)), annualBudget?.amount && /* @__PURE__ */ React.createElement(
230
+ return /* @__PURE__ */ React.createElement(Paper, { sx: { padding: 2 } }, /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2 } }, /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(ProviderIcon, { provider }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", sx: { ml: 1, fontWeight: "bold" } }, provider)), annualBudgetAmount > 0 && /* @__PURE__ */ React.createElement(
155
231
  BudgetHealthIndicator,
156
232
  {
157
233
  status: budgetAnalytics.budgetHealthStatus,
158
234
  utilizationPercent: budgetAnalytics.budgetUtilizationPercent
159
235
  }
160
- )), annualBudget?.amount && /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 1, sx: { mb: 2 } }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "YTD Spent"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", fontWeight: "bold" }, formatCurrency(budgetAnalytics.yearToDateSpent)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "of ", formatCurrency(annualBudget.amount))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Projected Annual"), /* @__PURE__ */ React.createElement(
236
+ )), annualBudgetAmount > 0 && /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 1, sx: { mb: 2 } }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "YTD Spent"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", fontWeight: "bold" }, formatCurrency(budgetAnalytics.yearToDateSpent)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "of ", formatCurrency(annualBudgetAmount))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Projected Annual"), /* @__PURE__ */ React.createElement(
161
237
  Typography,
162
238
  {
163
239
  variant: "body2",
164
240
  fontWeight: "bold",
165
- color: budgetAnalytics.projectedAnnualSpending > (annualBudget?.amount || 0) ? "error" : "inherit"
241
+ color: budgetAnalytics.projectedAnnualSpending > annualBudgetAmount ? "error" : "inherit"
166
242
  },
167
243
  formatCurrency(budgetAnalytics.projectedAnnualSpending)
168
- ), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, budgetAnalytics.projectedAnnualSpending > (annualBudget?.amount || 0) ? `+${formatCurrency(budgetAnalytics.projectedAnnualSpending - annualBudget.amount)} over` : `${formatCurrency(annualBudget.amount - budgetAnalytics.projectedAnnualSpending)} under`)))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Monthly Run Rate"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", fontWeight: "bold" }, formatCurrency(budgetAnalytics.monthlyRunRate)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: getSpendingVelocityColor(budgetAnalytics.spendingVelocity) }, getSpendingVelocityIcon(budgetAnalytics.spendingVelocity), Math.abs(budgetAnalytics.spendingVelocity).toFixed(1), "% MoM")))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Target Monthly"), /* @__PURE__ */ React.createElement(
244
+ ), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, budgetAnalytics.projectedAnnualSpending > annualBudgetAmount ? `+${formatCurrency(budgetAnalytics.projectedAnnualSpending - annualBudgetAmount)} over` : `${formatCurrency(annualBudgetAmount - budgetAnalytics.projectedAnnualSpending)} under`)))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Monthly Run Rate"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", fontWeight: "bold" }, formatCurrency(budgetAnalytics.monthlyRunRate)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: getSpendingVelocityColor(budgetAnalytics.spendingVelocity) }, getSpendingVelocityIcon(budgetAnalytics.spendingVelocity), Math.abs(budgetAnalytics.spendingVelocity).toFixed(1), "% MoM")))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Card, { variant: "outlined", sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(CardContent, { sx: { py: 1, "&:last-child": { pb: 1 } } }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Target Monthly"), /* @__PURE__ */ React.createElement(
169
245
  Typography,
170
246
  {
171
247
  variant: "body2",
@@ -174,40 +250,17 @@ function BudgetChart(props) {
174
250
  },
175
251
  formatCurrency(budgetAnalytics.targetMonthlySpending)
176
252
  ), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, budgetAnalytics.monthsRemaining.toFixed(1), " months left"))))), /* @__PURE__ */ React.createElement(
177
- ChartContainer,
253
+ ResponsiveChartContainer,
178
254
  {
179
- width: width + 20,
180
255
  height,
181
- series: [
182
- {
183
- data: view === "Annual" /* ANNUAL */ ? accumulatedCosts : nonAccumulatedCosts,
184
- type: view === "Annual" /* ANNUAL */ ? "line" : "bar",
185
- valueFormatter: (value) => {
186
- return formatCurrency(value || 0);
187
- },
188
- showMark: false
189
- }
190
- ],
256
+ series: chartSeries,
191
257
  xAxis: [
192
258
  {
193
259
  data: Object.entries(monthList).sort(([a], [b]) => a.localeCompare(b)).map(([_, value]) => value),
194
260
  scaleType: "band"
195
261
  }
196
262
  ],
197
- yAxis: [
198
- {
199
- min: 0,
200
- max: view === "Annual" /* ANNUAL */ ? max([...accumulatedCosts, budgetAmount, budgetAnalytics.confidenceRange.high]) : max([...nonAccumulatedCosts, budgetAmount]),
201
- valueFormatter: (value) => {
202
- return formatCurrency(value || 0);
203
- },
204
- colorMap: {
205
- type: "piecewise",
206
- thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],
207
- colors: [colorList[0], theme.palette.error.main]
208
- }
209
- }
210
- ]
263
+ yAxis
211
264
  },
212
265
  /* @__PURE__ */ React.createElement(ChartsGrid, { horizontal: true }),
213
266
  /* @__PURE__ */ React.createElement(ChartsAxisHighlight, { x: view === "Annual" /* ANNUAL */ ? "line" : "band" }),
@@ -230,7 +283,7 @@ function BudgetChart(props) {
230
283
  labelStyle: { fill: theme.palette.error.main, fontSize: "0.9em" }
231
284
  }
232
285
  ),
233
- view === "Annual" /* ANNUAL */ && annualBudget?.amount && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
286
+ view === "Annual" /* ANNUAL */ && annualBudgetAmount > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
234
287
  ChartsReferenceLine,
235
288
  {
236
289
  y: budgetAnalytics.projectedAnnualSpending,
@@ -283,23 +336,11 @@ function BudgetChart(props) {
283
336
  name: "amount",
284
337
  type: "number",
285
338
  startAdornment: /* @__PURE__ */ React.createElement(InputAdornment, { position: "start" }, "$"),
286
- defaultValue: annualBudget?.amount
339
+ defaultValue: annualBudgetAmount
287
340
  }
288
341
  )))), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { type: "submit", variant: "contained" }, "Submit"), /* @__PURE__ */ React.createElement(Button, { onClick: () => setOpenManageBudget(false) }, "Cancel"))))));
289
342
  }
290
- function BudgetInsights({ reports }) {
291
- const [budgets, setBudgets] = useState([]);
292
- const infraWalletApi = useApi(infraWalletApiRef);
293
- useEffect(() => {
294
- const fetchBudgets = async () => {
295
- try {
296
- const response = await infraWalletApi.getBudgets("default");
297
- setBudgets(response.data || []);
298
- } catch (error) {
299
- }
300
- };
301
- fetchBudgets();
302
- }, [infraWalletApi]);
343
+ function BudgetInsights({ reports, budgets }) {
303
344
  const insights = reports.map((report) => {
304
345
  const budget = budgets.find((b) => b.provider.toLowerCase() === report.id.toLowerCase());
305
346
  if (!budget?.amount) return null;
@@ -356,6 +397,7 @@ function BudgetInsights({ reports }) {
356
397
  const Budgets = ({ providerErrorsSetter }) => {
357
398
  const [reportsAggregatedAndMerged, setReportsAggregatedAndMerged] = useState(void 0);
358
399
  const [budgetView, setBudgetView] = useState("Annual" /* ANNUAL */);
400
+ const [budgets, setBudgets] = useState([]);
359
401
  const infraWalletApi = useApi(infraWalletApiRef);
360
402
  const alertApi = useApi(alertApiRef);
361
403
  const fetchCosts = useCallback(async () => {
@@ -373,13 +415,32 @@ const Budgets = ({ providerErrorsSetter }) => {
373
415
  useEffect(() => {
374
416
  fetchCosts();
375
417
  }, [fetchCosts]);
418
+ useEffect(() => {
419
+ const fetchBudgets = async () => {
420
+ try {
421
+ const response = await infraWalletApi.getBudgets("default");
422
+ setBudgets(response.data || []);
423
+ } catch (error) {
424
+ }
425
+ };
426
+ fetchBudgets();
427
+ }, [infraWalletApi]);
376
428
  return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, moment().year(), " ", budgetView, " Budgets"), /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Annual"), /* @__PURE__ */ React.createElement(
377
429
  Switch,
378
430
  {
379
431
  size: "small",
380
432
  onChange: (event) => setBudgetView(event.target.checked ? "Monthly" /* MONTHLY */ : "Annual" /* ANNUAL */)
381
433
  }
382
- ), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Monthly")))), reportsAggregatedAndMerged && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(BudgetInsights, { reports: reportsAggregatedAndMerged })), reportsAggregatedAndMerged !== void 0 ? reportsAggregatedAndMerged.map((report) => /* @__PURE__ */ React.createElement(Grid, { item: true, key: `${report.id}-grid`, xs: 4 }, /* @__PURE__ */ React.createElement(BudgetChart, { provider: report.id, monthlyCosts: report.reports, view: budgetView }))) : /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
434
+ ), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Monthly")))), reportsAggregatedAndMerged && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(BudgetInsights, { reports: reportsAggregatedAndMerged, budgets })), reportsAggregatedAndMerged !== void 0 ? reportsAggregatedAndMerged.map((report) => /* @__PURE__ */ React.createElement(Grid, { item: true, key: `${report.id}-grid`, xs: 4 }, /* @__PURE__ */ React.createElement(
435
+ BudgetChart,
436
+ {
437
+ provider: report.id,
438
+ monthlyCosts: report.reports,
439
+ view: budgetView,
440
+ budgets,
441
+ setBudgets
442
+ }
443
+ ))) : /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
383
444
  Paper,
384
445
  {
385
446
  sx: {
@@ -1 +1 @@
1
- {"version":3,"file":"Budgets.esm.js","sources":["../../../src/components/Budgets/Budgets.tsx"],"sourcesContent":["import { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport 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 ChartContainer,\n ChartsAxisHighlight,\n ChartsGrid,\n ChartsReferenceLine,\n ChartsTooltip,\n ChartsXAxis,\n ChartsYAxis,\n LineHighlightPlot,\n LinePlot,\n MarkPlot,\n useDrawingArea,\n} from '@mui/x-charts';\nimport { max } from 'lodash';\nimport moment from 'moment';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport {\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}\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 { width, height } = useDrawingArea();\n const theme = useTheme();\n const { provider, monthlyCosts, view } = props;\n\n const [annualBudget, setAnnualBudget] = useState<Budget | undefined>(undefined);\n const [openManageBudget, setOpenManageBudget] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(false);\n\n const infraWalletApi = useApi(infraWalletApiRef);\n\n const budgetAnalytics: BudgetAnalytics = calculateBudgetAnalytics(monthlyCosts, annualBudget?.amount || 0);\n\n useEffect(() => {\n const fetchBudget = async () => {\n const response = await infraWalletApi.getBudget('default', provider);\n const updatedBudget = response.data?.find(a => a.provider.toLowerCase() === provider.toLowerCase());\n setAnnualBudget(updatedBudget);\n };\n\n fetchBudget();\n }, [refreshTrigger, provider, infraWalletApi]);\n\n const updateBudget = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const formData = new FormData(event.currentTarget);\n const name = annualBudget?.name || `${provider} annual budget`;\n const amount = formData.get('amount');\n const newAnnualBudget: Budget = {\n id: annualBudget?.id,\n provider: provider,\n name: name,\n amount: amount ? Number(amount) : 0,\n };\n await infraWalletApi.updateBudget('default', newAnnualBudget);\n setRefreshTrigger(prev => !prev);\n setOpenManageBudget(false);\n };\n\n const nonAccumulatedCosts: number[] = [];\n const accumulatedCosts: number[] = [];\n for (const month of Object.keys(monthList).sort((a, b) => Number(a) - Number(b))) {\n const yearMonth = `${moment().year()}-${month}`;\n\n let cost;\n if (yearMonth in monthlyCosts) {\n cost = monthlyCosts[yearMonth];\n } else if (Number(month) < moment().month()) {\n cost = 0;\n } else {\n break;\n }\n\n nonAccumulatedCosts.push(cost);\n if (month === '01') {\n accumulatedCosts.push(cost);\n } else {\n accumulatedCosts.push(accumulatedCosts[accumulatedCosts.length - 1] + cost);\n }\n }\n\n let budgetAmount = annualBudget?.amount || 0;\n if (view === BUDGET_VIEW.MONTHLY) {\n budgetAmount = budgetAmount / 12;\n }\n\n return (\n <Paper sx={{ padding: 2 }}>\n {/* 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 {annualBudget?.amount && (\n <BudgetHealthIndicator\n status={budgetAnalytics.budgetHealthStatus}\n utilizationPercent={budgetAnalytics.budgetUtilizationPercent}\n />\n )}\n </Box>\n\n {/* Budget Metrics Cards */}\n {annualBudget?.amount && (\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(annualBudget.amount)}\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 > (annualBudget?.amount || 0) ? 'error' : 'inherit'}\n >\n {formatCurrency(budgetAnalytics.projectedAnnualSpending)}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {budgetAnalytics.projectedAnnualSpending > (annualBudget?.amount || 0)\n ? `+${formatCurrency(budgetAnalytics.projectedAnnualSpending - annualBudget.amount)} over`\n : `${formatCurrency(annualBudget.amount - 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 <ChartContainer\n width={width + 20}\n height={height}\n series={[\n {\n data: view === BUDGET_VIEW.ANNUAL ? accumulatedCosts : nonAccumulatedCosts,\n type: view === BUDGET_VIEW.ANNUAL ? 'line' : 'bar',\n valueFormatter: (value: number | null) => {\n return formatCurrency(value || 0);\n },\n showMark: false,\n },\n ]}\n xAxis={[\n {\n data: Object.entries(monthList)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([_, value]) => value),\n scaleType: 'band',\n },\n ]}\n yAxis={[\n {\n min: 0,\n max:\n view === BUDGET_VIEW.ANNUAL\n ? max([...accumulatedCosts, budgetAmount, budgetAnalytics.confidenceRange.high])\n : max([...nonAccumulatedCosts, budgetAmount]),\n valueFormatter: value => {\n return formatCurrency(value || 0);\n },\n colorMap: {\n type: 'piecewise',\n thresholds: [budgetAmount > 0 ? budgetAmount : Number.MAX_SAFE_INTEGER],\n colors: [colorList[0], theme.palette.error.main],\n },\n },\n ]}\n >\n <ChartsGrid horizontal />\n <ChartsAxisHighlight x={view === BUDGET_VIEW.ANNUAL ? 'line' : 'band'} />\n <LinePlot />\n <BarPlot />\n <MarkPlot />\n <LineHighlightPlot />\n <ChartsReferenceLine\n y={budgetAmount}\n label={budgetAmount ? formatCurrency(budgetAmount) : undefined}\n labelAlign=\"end\"\n lineStyle={{\n stroke: budgetAmount ? theme.palette.error.main : 'transparent',\n strokeDasharray: '5 5',\n strokeWidth: 1.5,\n strokeOpacity: 0.8,\n }}\n labelStyle={{ fill: theme.palette.error.main, fontSize: '0.9em' }}\n />\n {/* Add projection line for annual view */}\n {view === BUDGET_VIEW.ANNUAL && annualBudget?.amount && (\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 </ChartContainer>\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={annualBudget?.amount}\n />\n </FormControl>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button type=\"submit\" variant=\"contained\">\n Submit\n </Button>\n <Button onClick={() => setOpenManageBudget(false)}>Cancel</Button>\n </DialogActions>\n </form>\n </Dialog>\n </Box>\n </Paper>\n );\n}\n\ninterface BudgetInsightsProps {\n reports: Report[];\n}\n\nfunction BudgetInsights({ reports }: BudgetInsightsProps) {\n const [budgets, setBudgets] = useState<Budget[]>([]);\n const infraWalletApi = useApi(infraWalletApiRef);\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 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\n const infraWalletApi = useApi(infraWalletApiRef);\n const alertApi = useApi(alertApiRef);\n\n const fetchCosts = useCallback(async () => {\n await infraWalletApi\n .getCostReports('', [], '', 'monthly', moment().startOf('y').toDate(), moment().endOf('d').toDate())\n .then(reportsResponse => {\n if (reportsResponse.data) {\n const aggregatedReports = aggregateCostReports(reportsResponse.data, 'provider');\n const aggregatedAndMergedReports = mergeCostReports(aggregatedReports);\n setReportsAggregatedAndMerged(aggregatedAndMergedReports);\n }\n if (reportsResponse.status === 207 && reportsResponse.errors) {\n providerErrorsSetter(reportsResponse.errors);\n }\n })\n .catch(e => alertApi.post({ message: `${e.message}`, severity: 'error' }));\n }, [alertApi, infraWalletApi, providerErrorsSetter]);\n\n useEffect(() => {\n fetchCosts();\n }, [fetchCosts]);\n\n return (\n <Grid container spacing={3}>\n <Grid item 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} />\n </Grid>\n )}\n {reportsAggregatedAndMerged !== undefined ? (\n reportsAggregatedAndMerged.map(report => (\n <Grid item key={`${report.id}-grid`} xs={4}>\n <BudgetChart provider={report.id} monthlyCosts={report.reports} view={budgetView} />\n </Grid>\n ))\n ) : (\n <Grid item xs={12}>\n <Paper\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: 500,\n backgroundColor: 'transparent',\n boxShadow: 'none',\n }}\n >\n <div style={{ width: '60%', margin: 'auto' }}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n </Paper>\n </Grid>\n )}\n </Grid>\n );\n};\n"],"names":["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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electrolux-oss/plugin-infrawallet",
3
- "version": "1.1.0-20251202133007-1005ef9",
3
+ "version": "1.1.0-20251204084511-e456a8a",
4
4
  "backstage": {
5
5
  "role": "frontend-plugin",
6
6
  "pluginId": "infrawallet",
@@ -38,14 +38,14 @@
38
38
  "test": "backstage-cli package test"
39
39
  },
40
40
  "dependencies": {
41
- "@backstage/catalog-model": "^1.7.4",
42
- "@backstage/config": "^1.3.2",
43
- "@backstage/core-components": "^0.17.3",
44
- "@backstage/core-plugin-api": "^1.10.8",
45
- "@backstage/plugin-catalog-react": "^1.19.0",
46
- "@backstage/theme": "^0.6.6",
41
+ "@backstage/catalog-model": "^1.7.6",
42
+ "@backstage/config": "^1.3.6",
43
+ "@backstage/core-components": "^0.18.3",
44
+ "@backstage/core-plugin-api": "^1.12.0",
45
+ "@backstage/plugin-catalog-react": "^1.21.3",
46
+ "@backstage/theme": "^0.7.0",
47
47
  "@mui/icons-material": "6.1.1",
48
- "@mui/material": "^5.16.7",
48
+ "@mui/material": "^6.1.1",
49
49
  "@mui/styles": "^5.16.7",
50
50
  "@mui/x-charts": "7.29.1",
51
51
  "@mui/x-data-grid": "8.4.0",
@@ -63,12 +63,12 @@
63
63
  "uuid": "10.0.0"
64
64
  },
65
65
  "devDependencies": {
66
- "@backstage/cli": "^0.33.0",
67
- "@backstage/core-app-api": "^1.17.1",
68
- "@backstage/dev-utils": "^1.1.11",
69
- "@backstage/test-utils": "^1.7.9",
66
+ "@backstage/cli": "^0.34.5",
67
+ "@backstage/core-app-api": "^1.19.2",
68
+ "@backstage/dev-utils": "^1.1.17",
69
+ "@backstage/test-utils": "^1.7.13",
70
70
  "@testing-library/jest-dom": "^6.0.0",
71
- "@testing-library/react": "^14.0.0",
71
+ "@testing-library/react": "^16.0.0",
72
72
  "@testing-library/user-event": "^14.0.0",
73
73
  "@types/lodash": "^4.14.151",
74
74
  "@types/lucene": "^2",