/**
* Consumption Sparkline Component
* SVG-based visualization for meter consumption deltas
* Adapted from RSSISparkline pattern
*/
const ConsumptionSparkline = (function() {
'use strict';
// Default configuration
const DEFAULT_CONFIG = {
width: 100,
height: 28,
maxSamples: 20,
strokeWidth: 1.5,
showGradient: true,
barMode: true // Use bars instead of line for consumption
};
// Color thresholds for consumption deltas
// Green = normal/expected, Yellow = elevated, Red = spike
const DELTA_COLORS = {
normal: '#22c55e', // Green
elevated: '#eab308', // Yellow
spike: '#ef4444' // Red
};
/**
* Classify a delta value relative to the average
* @param {number} delta - The delta value
* @param {number} avgDelta - Average delta for comparison
* @returns {string} - 'normal', 'elevated', or 'spike'
*/
function classifyDelta(delta, avgDelta) {
if (avgDelta === 0 || isNaN(avgDelta)) {
return delta === 0 ? 'normal' : 'elevated';
}
const ratio = Math.abs(delta) / Math.abs(avgDelta);
if (ratio <= 1.5) return 'normal';
if (ratio <= 3) return 'elevated';
return 'spike';
}
/**
* Get color for a delta value
*/
function getDeltaColor(delta, avgDelta) {
const classification = classifyDelta(delta, avgDelta);
return DELTA_COLORS[classification];
}
/**
* Create sparkline SVG for consumption deltas
* @param {Array<{timestamp, delta}>} deltas - Array of delta objects
* @param {Object} config - Configuration options
* @returns {string} - SVG HTML string
*/
function createSparklineSvg(deltas, config = {}) {
const cfg = { ...DEFAULT_CONFIG, ...config };
const { width, height, strokeWidth, showGradient, barMode } = cfg;
if (!deltas || deltas.length < 1) {
return createEmptySparkline(width, height);
}
// Extract just the delta values
const values = deltas.map(d => d.delta);
// Calculate statistics for color classification
const avgDelta = values.reduce((a, b) => a + b, 0) / values.length;
const maxDelta = Math.max(...values.map(Math.abs), 1);
if (barMode) {
return createBarSparkline(values, avgDelta, maxDelta, cfg);
}
return createLineSparkline(values, avgDelta, maxDelta, cfg);
}
/**
* Create bar-style sparkline (better for discrete readings)
*/
function createBarSparkline(values, avgDelta, maxDelta, cfg) {
const { width, height } = cfg;
const barCount = Math.min(values.length, cfg.maxSamples);
const displayValues = values.slice(-barCount);
const barWidth = Math.max(3, (width / barCount) - 1);
const barGap = 1;
let bars = '';
displayValues.forEach((val, i) => {
const normalizedHeight = (Math.abs(val) / maxDelta) * (height - 4);
const barHeight = Math.max(2, normalizedHeight);
const x = i * (barWidth + barGap);
const y = height - barHeight - 2;
const color = getDeltaColor(val, avgDelta);
bars += `