diff --git a/frontend/src/pages/WeeklyActual.tsx b/frontend/src/pages/WeeklyActual.tsx index 2580ae62..3c04da3d 100644 --- a/frontend/src/pages/WeeklyActual.tsx +++ b/frontend/src/pages/WeeklyActual.tsx @@ -10,7 +10,7 @@ import { CartesianGrid, } from 'recharts' import { getWeeklyActual } from '../api' -import type { WeeklyActualData, WeekSummaryRow, MonthSplitEntry, MonthDailyEntry, UtilityConsumptionBasis, UtilityTrend } from '../types' +import type { WeeklyActualData, WeekSummaryRow, MonthSplitEntry, MonthDailyEntry, UtilityConsumptionBasis, UtilityTrend, UtilityTrendMonth, UtilityEstimate } from '../types' // ── Formatters ───────────────────────────────────────────────────────────── @@ -487,12 +487,32 @@ function fmtBasis(status: UtilityConsumptionBasis, asOfDate: string | null): str return asOfDate ? `As of ${fmtShortDate(asOfDate)}` : 'Partial' } -function UtilitiesCostTrendChart({ trend }: { trend: UtilityTrend }) { - const chartData = trend.this_year.map((ty, i) => ({ - label: ty.label, - 'This Year': ty.total_pence / 100, - 'Last Year': (trend.last_year[i]?.total_pence ?? 0) / 100, - })) +// Builds the "This Year" series, swapping the in-progress month's partial +// actuals for the utilities estimate. The projected month is emitted on a +// separate dashed series (anchored on the previous month's actual so the line +// is continuous) and left null on the solid one. +function withProjection( + trend: UtilityTrend, + estimate: UtilityEstimate | null, + pick: (m: UtilityTrendMonth, est: UtilityEstimate | null) => number, +) { + const lastIdx = trend.this_year.length - 1 + const last = trend.this_year[lastIdx] + const projecting = !!estimate && !!last && last.year === estimate.period.year && last.month === estimate.period.month + return trend.this_year.map((ty, i) => { + const isProjected = projecting && i === lastIdx + const value = pick(ty, isProjected ? estimate : null) + return { + label: ty.label, + 'This Year': isProjected ? null : value, + 'Projected': isProjected ? value : (projecting && i === lastIdx - 1 ? value : null), + 'Last Year': pick(trend.last_year[i], null), + } + }) +} + +function UtilitiesCostTrendChart({ trend, estimate }: { trend: UtilityTrend; estimate: UtilityEstimate | null }) { + const chartData = withProjection(trend, estimate, (m, est) => (est ? est.totals.total_pence : (m?.total_pence ?? 0)) / 100) return (