fix(charts): persist the range-tab pick like the overlay toggles

rangePoints lived in plain useState(30), so every chart reset to the
30 d tab on mount while the sibling trend/comparison/target-band
toggles on the same card persisted per chart. Add rangePoints to
ChartOverlayPrefs (server-validated against the closed 0/7/30/90 set)
and hydrate it once the prefs resolve; a range-tab tap now persists
through the same PUT the toggles already use.
This commit is contained in:
Marc-André Bombeck committed 2026-07-18 03:52:32 +02:00
1 parent a66a346d0d
commit 3c468b5e00
4 files changed
+96 -1

No files matched your search

+36
View File
@@ -632,6 +632,32 @@ export function HealthChart({
? overlayPrefs.prefs.comparisonBaseline
: compareBaseline;
// v1.30.1 M2 — hydrate the range tab from the persisted prefs once
// they arrive, mirroring the overlay toggles above. A plain
// `useState(30)` initializer can't see the persisted value because
// the dashboard-layout query resolves after mount; `rangePoints` also
// drives several derived memos below by identity, so it stays local
// state (unlike the toggles, which read `overlayPrefs.prefs` live)
// and is synced in exactly once. `windowOverride` charts (mini mode)
// are pinned and never adopt a persisted range. Adjusted during
// render (React's documented "adjust state during render" escape
// hatch, not an effect — the lint rule that bans ref reads during
// render is why this is a second piece of state rather than a ref)
// so the very first render that sees the resolved prefs already
// reflects them; `rangeHydrated` guards against re-running past the
// first hit, so a later cache refetch can't stomp a range the user
// already picked this session.
const [rangeHydrated, setRangeHydrated] = useState(false);
if (
!windowOverride &&
chartKey &&
!rangeHydrated &&
typeof overlayPrefs.prefs.rangePoints === "number"
) {
setRangeHydrated(true);
setRangePoints(overlayPrefs.prefs.rangePoints);
}
// v1.4.19 A2 — viewport-aware tick density. Recharts' default is one
// tick per data point; the helper caps that at 4-10 visible labels
// depending on viewport width so the X-axis reads consistently across
@@ -1674,7 +1700,17 @@ export function HealthChart({
// v1.12.8 — a range-tab change re-slices `chartData`; the
// visible-range stats memo recomputes off the new slice and
// the stat strip follows automatically.
setRangeHydrated(true);
setRangePoints(r.points);
// v1.30.1 M2 — persist the pick per chart, same model
// as the overlay toggles, so the range survives a
// remount instead of resetting to 30 d every visit.
if (chartKey && !windowOverride) {
overlayPrefs.setPrefs({
...overlayPrefs.prefs,
rangePoints: r.points,
});
}
}}
title={t(r.titleKey)}
data-slot="chart-range-tab"
+31 -1
View File
@@ -323,6 +323,25 @@ export function MoodChart({
const effectiveCompareBaseline =
!mini && chartKey ? overlayPrefs.prefs.comparisonBaseline : compareBaseline;
// v1.30.1 M2 — hydrate the range tab from the persisted prefs once
// they arrive; see health-chart.tsx for the full rationale (the
// dashboard-layout query resolves after mount, and `rangePoints`
// stays local state because several memos depend on it by identity).
// Adjusted during render, not an effect — React's documented "adjust
// state during render" pattern (a second state flag rather than a
// ref, since the lint rule bans ref reads during render).
const [rangeHydrated, setRangeHydrated] = useState(false);
if (
!mini &&
!windowOverride &&
chartKey &&
!rangeHydrated &&
typeof overlayPrefs.prefs.rangePoints === "number"
) {
setRangeHydrated(true);
setRangePoints(overlayPrefs.prefs.rangePoints);
}
// v1.4.19 A2 — viewport-aware tick density helper.
const viewportWidth = useViewportWidth();
@@ -1047,7 +1066,18 @@ export function MoodChart({
aria-pressed={rangePoints === r.points}
size="sm"
className="min-h-11 px-2 text-xs sm:px-3"
onClick={() => setRangePoints(r.points)}
onClick={() => {
setRangeHydrated(true);
setRangePoints(r.points);
// v1.30.1 M2 — persist the pick per chart, same
// model as the overlay toggles.
if (chartKey && !windowOverride) {
overlayPrefs.setPrefs({
...overlayPrefs.prefs,
rangePoints: r.points,
});
}
}}
title={t(r.titleKey)}
data-slot="chart-range-tab"
>