Files
HealthLog/src/hooks/use-insights-analytics.ts
T
Marc-André Bombeck 1f4ba5939d style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
2026-06-19 22:20:31 +02:00

95 lines
3.4 KiB
TypeScript

"use client";
import { useAuth } from "@/hooks/use-auth";
import {
hasMetricData,
type InsightMetric,
} from "@/lib/insights/metric-availability";
import { useAnalyticsQuery } from "@/lib/queries/use-analytics-query";
import type { SubPageAnalyticsData } from "@/types/analytics";
/**
* v1.4.28 R3d (BK-F-H1) — shared React-Query wrapper for the analytics
* payload behind every insights sub-page.
*
* The five hard-data sub-pages (`puls`, `blutdruck`, `gewicht`, `bmi`,
* `schlaf`) each used to declare:
*
* - a private `AnalyticsData` interface,
* - a `useQuery` block keyed on `["analytics"]` with a 60-second
* `staleTime`,
* - an `if (isAuthenticated && analytics && !hasMetricData(...)) { … }`
* short-circuit that rendered a sub-page-specific empty state.
*
* Those three are now collapsed onto this hook:
*
* - the query reuses the same `["analytics"]` cache key as the
* dashboard + the insights mother page, so navigating between
* surfaces is a free cache hit;
* - `isEmpty` runs `hasMetricData()` with the caller's metric so
* each sub-page checks the right gate without re-importing the
* helper;
* - the typed result keeps the existing `data` slot so any caller
* that still needs a richer field (today: none — the sub-pages
* only read `summaries`) can fall back to direct access.
*
* The mood and medication sub-pages still need richer payloads
* (`/api/insights/comprehensive`) so they don't consume this hook —
* those keep their bespoke fetch but adopt the same `<MetricEmptyState>`
* primitive for the empty-state render so the visual contract is one.
*/
export interface UseInsightsAnalyticsResult {
data: SubPageAnalyticsData | undefined;
isLoading: boolean;
/**
* True when the analytics payload arrived AND the metric has no
* observations. Sub-pages render the empty-state primitive when this
* flips true. False while the query is still in-flight (we don't
* want to flash the empty state before the data lands).
*/
isEmpty: boolean;
error: Error | null;
/**
* Re-issue the underlying `/api/analytics?slice=summaries` query. Wired
* to the error+retry control on the HealthKit sub-pages so a failed load
* recovers without a full reload.
*/
refetch: () => void;
}
export function useInsightsAnalytics(
metric: InsightMetric,
): UseInsightsAnalyticsResult {
const { isAuthenticated } = useAuth();
// v1.4.33 IW2 — sub-pages only read `summaries[METRIC].count` for the
// gating short-circuit, so they ride on the slim slice. The mother
// page keeps the default thick slice for `correlations` /
// `healthScore` / `bpInTargetPct*`; both consumers live in the same
// queryKey tree (root `["analytics"]`) but the slim slice has its own
// cache slot under `["analytics", "summaries"]`.
const query = useAnalyticsQuery({ slice: "summaries" });
const data = query.data as SubPageAnalyticsData | undefined;
const isEmpty =
Boolean(isAuthenticated) &&
data !== undefined &&
!hasMetricData(metric, {
summaries: data.summaries,
// Sub-pages that go through this hook are sensor-backed only
// (PULSE / WEIGHT / BMI / BLOOD_PRESSURE_SYS / SLEEP_DURATION).
// Mood + medication run their own gate.
hasMood: false,
hasMedication: false,
});
return {
data,
isLoading: query.isLoading,
isEmpty,
error: query.error as Error | null,
refetch: () => {
void query.refetch();
},
};
}