// Drinks bar-chart components — discrete daily units. // DrinksBars: 14-day mini for the Today card. // DrinksTrendChart: full panel for the Trends tab. function DrinksBars({ data, // array of daily unit values, oldest → newest width = 320, height = 38, fullWidth = false, threshold = 3, mutedColor, dangerColor, }) { const theme = window.HC_THEME.useHCTheme(); const p = theme.palette; const arr = (data || []).map((v) => Number(v) || 0); const days = arr.length; if (days === 0) return null; const max = Math.max(threshold + 1, ...arr) * 1.05; const muted = mutedColor || p.text3; const danger = dangerColor || p.warning; const padX = 2, padY = 4; const innerW = width - 2 * padX; const innerH = height - 2 * padY; const slot = innerW / days; const barW = Math.max(2.5, slot * 0.55); return ( {/* baseline */} {arr.map((v, i) => { if (v <= 0) return null; const h = (v / max) * innerH; const x = padX + slot * i + (slot - barW) / 2; const y = height - padY - h; const color = v >= threshold ? danger : muted; return ; })} ); } // Trends panel — uses the drinks series from SeriesContext (via buildSeries). // Phase overlays + spasm/event markers + dashed 3u threshold, matching the // hand-rolled SVG styling of TrendChart. function DrinksTrendChart({ width = 358, height = 100, range = 'full', windowDays = 60, phases = [], annotations = [], threshold = 3, }) { const theme = window.HC_THEME.useHCTheme(); const p = theme.palette; const SERIES = React.useContext(window.SeriesContext); const s = SERIES && SERIES.drinks; if (!s) return null; const total = s.raw.length; const days = range === '4w' ? Math.min(28, total) : Math.min(windowDays, total); const offset = total - days; const data = s.raw.slice(-days); const max = Math.max(threshold + 1, ...data) * 1.05; const muted = p.text3; const danger = p.warning; const padL = 32, padR = 12, padT = 10, padB = 22; const innerW = width - padL - padR; const innerH = height - padT - padB; const slot = innerW / days; const barW = Math.max(2, slot * 0.55); const yBase = padT + innerH; const y = (v) => padT + ((max - v) / max) * innerH; const ticks = [0, threshold, Math.ceil(max)]; // X-tick date labels match the other TrendChart panels by reading from // the shared HC_TREND_DATES array populated by TrendsTab. const dates = window.HC_TREND_DATES || []; return ( {phases.map((ph, i) => { const [a, b] = ph.range; const a2 = Math.max(0, a - offset); const b2 = Math.max(0, b - offset); if (b2 <= 0 || a2 >= days) return null; return ( ); })} {ticks.map((t, i) => ( ))} {ticks.map((t, i) => ( {t} ))} {/* dashed 3u threshold */} 3u {data.map((v, i) => { if (v <= 0) return null; const h = (v / max) * innerH; const x = padL + slot * i + (slot - barW) / 2; const color = v >= threshold ? danger : muted; return ; })} {annotations.map((a, i) => { const idx = a.idx - offset; if (idx < 0 || idx >= days) return null; const xc = padL + slot * idx + slot / 2; return ( {a.label} ); })} {[0, Math.floor(days / 2), days - 1].map((i) => { const iso = dates[offset + i]; let label = ''; if (iso) { const dt = new Date(iso + 'T00:00:00'); label = `${['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][dt.getMonth()]} ${dt.getDate()}`; } return ( {label} ); })} ); } window.DrinksBars = DrinksBars; window.DrinksTrendChart = DrinksTrendChart;