mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
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>
63 lines
2.4 KiB
TypeScript
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,
|
|
};
|
|
}
|