function createConsumptionModel() { function normalizeConsumptionTrendItems(items) { const points = new Map(); (Array.isArray(items) ? items : []).forEach((item) => { const dateKey = String(item.fyDate || (Array.isArray(item.fiscalTime) ? item.fiscalTime.join("|") : "")); if (!dateKey) { return; } if (!points.has(dateKey)) { points.set(dateKey, { dateKey, date: parseConsumptionDate(item.fyDate), fiscalTime: Array.isArray(item.fiscalTime) ? item.fiscalTime.slice() : [], fiscalQtr: item.fiscalQtr || "", funded: 0, overage: 0, arr: 0, currencyCode: item.currencyCode || "USD" }); } const point = points.get(dateKey); const amount = toFiniteNumber(item.usedAmount); const category = String(item.usageCategory || "Funded Allocation").toUpperCase(); if (category === "OVERAGE") { point.overage += amount; } else { point.funded += amount; } point.arr = Math.max(point.arr, toFiniteNumber(item.arrCd)); }); return Array.from(points.values()) .map((point) => ({ ...point, total: point.funded + point.overage })) .sort((left, right) => left.dateKey.localeCompare(right.dateKey)); } function getConsumptionPointTotal(point, activeSeries) { const selectedSeries = activeSeries && typeof activeSeries.has === "function" ? activeSeries : new Set(["funded", "overage"]); return (selectedSeries.has("funded") ? point.funded : 0) + (selectedSeries.has("overage") ? point.overage : 0); } function calculateAverageConsumption(points, hasPartialLatestPoint, activeSeries) { const completePoints = hasPartialLatestPoint && points.length > 1 ? points.slice(0, -1) : points; return completePoints.reduce( (sum, point) => sum + getConsumptionPointTotal(point, activeSeries), 0 ) / Math.max(completePoints.length, 1); } function createNiceConsumptionMaximum(value) { if (!Number.isFinite(value) || value <= 0) { return 1; } const magnitude = 10 ** Math.floor(Math.log10(value)); const normalized = value / magnitude; const step = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10; return step * magnitude; } function formatConsumptionPointLabel(point, period) { if (period === "quarterly") { return point.fiscalTime[0] || point.fiscalQtr || point.dateKey; } if (period === "monthly") { return point.fiscalTime[1] || formatConsumptionDate(point.date, { month: "short" }); } return point.fiscalTime[0] || formatConsumptionDate(point.date, { day: "2-digit", month: "short", year: "2-digit" }); } function formatConsumptionRange(points, period) { if (points.length === 0) { return "No data"; } if (period === "quarterly") { return `${formatConsumptionPointLabel(points[0], period)} - ${formatConsumptionPointLabel(points[points.length - 1], period)}`; } if (period === "monthly") { const options = { month: "short", year: "numeric" }; return `${formatConsumptionDate(points[0].date, options)} - ${formatConsumptionDate(points[points.length - 1].date, options)}`; } const options = { day: "2-digit", month: "short" }; return `${formatConsumptionDate(points[0].date, options)} - ${formatConsumptionDate(points[points.length - 1].date, options)}`; } function parseConsumptionDate(value) { if (!value) { return null; } const parts = String(value).split("-").map(Number); return parts.length === 3 && parts.every(Number.isFinite) ? new Date(parts[0], parts[1] - 1, parts[2]) : null; } function formatConsumptionDate(date, options) { if (!(date instanceof Date) || Number.isNaN(date.getTime())) { return "-"; } return new Intl.DateTimeFormat("en-US", options).format(date).replace(",", ""); } function formatCompactConsumptionNumber(value) { const amount = toFiniteNumber(value); if (Math.abs(amount) >= 1000000) { return `${(amount / 1000000).toFixed(amount >= 10000000 ? 0 : 1).replace(".0", "")}M`; } if (Math.abs(amount) >= 1000) { return `${Math.round(amount / 1000)}K`; } return String(Math.round(amount)); } function formatConsumptionCurrency(value, currencyCode) { try { return new Intl.NumberFormat("en-US", { style: "currency", currency: currencyCode || "USD", maximumFractionDigits: 0 }).format(toFiniteNumber(value)); } catch (error) { return `${currencyCode || "USD"} ${Math.round(toFiniteNumber(value)).toLocaleString("en-US")}`; } } function toFiniteNumber(value) { const number = Number(value); return Number.isFinite(number) ? number : 0; } return { normalizeConsumptionTrendItems, getConsumptionPointTotal, calculateAverageConsumption, createNiceConsumptionMaximum, formatConsumptionPointLabel, formatConsumptionRange, parseConsumptionDate, formatConsumptionDate, formatCompactConsumptionNumber, formatConsumptionCurrency, toFiniteNumber }; }