@electrolux-oss/plugin-infrawallet 1.1.0-20251218080406-fbaf733 → 1.1.0-20260112100533-68a81f8
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 \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 \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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"}
|