Files
HealthLog/src/hooks/use-chart-domain-stats.ts
T
Marc-André BombeckandClaude Opus 4.8 465c8b8e33 feat(insights): drive metric stat strip from the chart range tab; add chart tile header
Remove the draggable chart brush. The Min/Max/Median/Mittelwert strip now
always reflects the data visible under the chart's active range tab (7/30/90/
all): the chart reports per-type stats for its range-sliced series and the
sub-page threads them into the strip. No drag, no "selected range" pill — the
range tab is the single selector for both chart and strip. Blood pressure
recomputes both systolic and diastolic.

Give each metric chart card the canonical TileHeader (icon + title, same size
and spacing as the assessment/target/stat tiles) via an optional titleIcon;
the dashboard and mini mounts keep the compact heading.

Drop the now-unused charts.selectRange and insights.subPage.stats.selectedRange
keys from all six locales.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:53:32 +02:00

63 lines
2.4 KiB
TypeScript

"use client";
import { useCallback, useRef, useState } from "react";
import type { MetricWindowStats } from "@/lib/charts/window-stats";
/**
* v1.12.8 — chart-reactive metric statistics.
*
* A metric sub-page lifts this hook so its `<HealthChart>` and its
* `<MetricStatStrip>` read the same window. The chart reports the per-type
* Min / Max / Median / Mean for the data currently visible under its active
* range tab (the 7 / 30 / 90 / All selector) through `onVisibleStats`; the
* strip reads `statsByType[type]` and renders those windowed numbers.
*
* `null` (no data, or mini mode) means "show the full range" — the strip
* falls back to the precomputed summary. The chart owns the maths (it already
* holds the bucketed series), so the hook only stores what it is handed and
* exposes a stable callback.
*
* The callback is referentially stable across renders so it can sit in the
* chart's effect dependency list without re-firing. A shallow guard skips the
* state write when the chart reports the same window twice in a row, keeping a
* re-render that recomputes the same slice from thrashing the strip.
*/
export interface ChartDomainStats {
/**
* Per-type windowed stats, keyed by `MeasurementType`. Null means the chart
* has not reported a slice yet (or has no data). A type may be absent from
* the map when its series has no readings in the visible range — the strip
* falls back to the full-range summary for that series.
*/
statsByType: Record<string, MetricWindowStats> | null;
/** Stable callback handed to `<HealthChart onVisibleStats>`. */
onVisibleStats: (stats: Record<string, MetricWindowStats> | null) => void;
}
export function useChartDomainStats(): ChartDomainStats {
const [statsByType, setStatsByType] = useState<Record<
string,
MetricWindowStats
> | null>(null);
const lastRef = useRef<string>("null");
const onVisibleStats = useCallback(
(stats: Record<string, MetricWindowStats> | null) => {
// Cheap shallow signature so an identical window (a re-render that
// recomputes the same slice) doesn't re-set state and re-render the
// strip.
const signature = stats === null ? "null" : JSON.stringify(stats);
if (signature === lastRef.current) return;
lastRef.current = signature;
setStatsByType(stats);
},
[],
);
return {
statsByType,
onVisibleStats,
};
}