Merge remote-tracking branch 'origin/main' into feat/vaccinations-phase-1

This commit is contained in:
Marc-André Bombeck committed 2026-08-08 19:35:18 +02:00
commit 4f2dd51390
17 files changed
+146 -39

No files matched your search

+4 -3
View File
@@ -9,9 +9,10 @@ import { SlidersHorizontal, TrendingUp } from "lucide-react";
import { useAuth } from "@/hooks/use-auth";
import { queryKeys } from "@/lib/query-keys";
import { apiGet } from "@/lib/api/api-fetch";
// Pure constant — importing it here does NOT pull the dynamically-loaded ECG
// component into this bundle, which is exactly why it lives in the lib module.
import { ECG_OVERVIEW_LIMIT } from "@/lib/insights/ecg-classification";
// Bare number — its own React- and label-map-free module, so importing it here
// pulls neither the ECG label map/helpers nor the dynamically-loaded ECG
// component into this eager bundle.
import { ECG_OVERVIEW_LIMIT } from "@/lib/insights/ecg-overview-limit";
import { useFeatureFlags } from "@/hooks/use-feature-flags";
import { useInsightsLayoutQuery } from "@/hooks/use-insights-layout";
import { usePullToRefresh } from "@/hooks/use-pull-to-refresh";
@@ -12,6 +12,14 @@
* of the two; `documentDate` is the person's own filing date and is the
* fallback.
*
* Both DTO fields are date-only (`YYYY-MM-DD`), but the suggest endpoint takes
* a full instant — the same shape the two lab moments already hand it. So the
* date is lifted to noon UTC here, exactly as the OCR review does: the rule is
* a ±7-day window, so the time of day never changes the verdict, and noon keeps
* a viewer's local midnight from rounding the day either way. Passing the bare
* date instead is what made this suggestion silently never appear — the API
* answered 422 and the field rendered nothing.
*
* It renders nothing once the document already names a visit that this session
* did not choose: an offer to file something already filed is noise.
*/
@@ -31,9 +39,10 @@ export function DocumentEncounterSuggestion({
onChange: (encounterId: string | null) => void;
}) {
if (alreadyLinked) return null;
const instant = anchor ? `${anchor}T12:00:00.000Z` : null;
return (
<EncounterSuggestionField
anchor={anchor}
anchor={instant}
value={value}
onChange={onChange}
disabled={disabled}
@@ -0,0 +1,31 @@
import type { MetricProvenanceMeta } from "./standards";
/**
* The Health Score's own provenance entry, split out of the full
* `METRIC_PROVENANCE` map.
*
* The eager insights overview reaches this value through
* HeroStrip → HealthScoreCard, and it needs only this one metric's method key
* and cited standard. Importing the whole map there dragged every derived
* metric's citation (~30 entries of DOIs and key strings) into the overview's
* initial bundle. Isolating the entry keeps the eager page down to the number
* it renders; `METRIC_PROVENANCE.HEALTH_SCORE` re-exports this same value, so
* the lazy score surfaces still read one source.
*
* Cited for the risk-factor set the pillars are drawn from, and for nothing
* beyond it. HEARTS names the cardiometabolic factors worth following; it does
* not prescribe an equal-weighted average of them, and since v1.35.0 it could
* not, because which pillars enter that average is the account's own
* selection. Naming the package plainly, as this entry used to, reads as a
* standard endorsing whatever recipe the person happens to have chosen, which
* is a claim nobody can make. The pillar-level citations in the map are
* unaffected: each of those really does describe the band its pillar is graded
* against.
*/
export const HEALTH_SCORE_PROVENANCE: MetricProvenanceMeta = {
methodKey: "insights.healthScore.method",
standard: {
name: "WHO HEARTS technical package (risk-factor set)",
url: "https://www.who.int/publications/i/item/9789240001367",
},
};
+5 -17
View File
@@ -23,6 +23,7 @@
* covers has to live in `insights.healthScore.method` — the standard entry
* alone would say it to nobody.
*/
import { HEALTH_SCORE_PROVENANCE } from "./health-score-provenance";
import type { ProvenanceStandard } from "./provenance-explainer";
import type { DerivedMetricId } from "@/lib/insights/derived/registry";
@@ -198,21 +199,8 @@ export const METRIC_PROVENANCE: Record<DerivedMetricId, MetricProvenanceMeta> =
},
caveatKey: "insights.derived.composite.SAME_TIME_BASELINE.caveat",
},
HEALTH_SCORE: {
methodKey: "insights.healthScore.method",
standard: {
// Cited for the risk-factor set the pillars are drawn from, and for
// nothing beyond it. HEARTS names the cardiometabolic factors worth
// following; it does not prescribe an equal-weighted average of
// them, and since v1.35.0 it could not, because which pillars enter
// that average is the account's own selection. Naming the package
// plainly, as this entry used to, reads as a standard endorsing
// whatever recipe the person happens to have chosen, which is a
// claim nobody can make. The pillar-level citations elsewhere in
// this map are unaffected: each of those really does describe the
// band its pillar is graded against.
name: "WHO HEARTS technical package (risk-factor set)",
url: "https://www.who.int/publications/i/item/9789240001367",
},
},
// The HEALTH_SCORE entry lives in its own module so the eager insights
// overview can import just this metric's citation without pulling the whole
// map; the rationale for its wording is documented there.
HEALTH_SCORE: HEALTH_SCORE_PROVENANCE,
};
+1 -1
View File
@@ -12,9 +12,9 @@ import { cn } from "@/lib/utils";
import { SectionHeading } from "@/components/ui/section-heading";
import {
ecgResultLabel,
ECG_OVERVIEW_LIMIT,
type EcgClassification,
} from "@/lib/insights/ecg-classification";
import { ECG_OVERVIEW_LIMIT } from "@/lib/insights/ecg-overview-limit";
import { InsightSectionCard } from "./insight-section-card";
// Re-exported so the tests (and any teaser call site) can keep importing the
@@ -17,7 +17,7 @@ import {
} from "@/components/insights/derived/band-tokens";
import { CoverageMeter } from "@/components/insights/derived/coverage-meter";
import { ProvenanceExplainer } from "@/components/insights/derived/provenance-explainer";
import { METRIC_PROVENANCE } from "@/components/insights/derived/standards";
import { HEALTH_SCORE_PROVENANCE } from "@/components/insights/derived/health-score-provenance";
import {
pillarDetailLines,
pillarObservedText,
@@ -643,8 +643,8 @@ export function HealthScoreCard({
)}
<ProvenanceExplainer
provenance={composite.provenance}
method={t(METRIC_PROVENANCE.HEALTH_SCORE.methodKey)}
standard={METRIC_PROVENANCE.HEALTH_SCORE.standard}
method={t(HEALTH_SCORE_PROVENANCE.methodKey)}
standard={HEALTH_SCORE_PROVENANCE.standard}
className="block"
/>
{/* v1.35.0 — the one way into the surface that decides what
-9
View File
@@ -13,15 +13,6 @@
export type EcgClassification =
"IRREGULAR" | "NOT_DETECTED" | "INCONCLUSIVE" | null;
/**
* How many strips the OVERVIEW teaser paints. It is a glance, not a history;
* the dedicated `/insights/ecg` page passes no limit and shows every
* recording. The constant lives here — a pure value with no React import — so
* the `/insights` page can pass it without pulling the ECG component eagerly
* across its dynamic boundary.
*/
export const ECG_OVERVIEW_LIMIT = 5;
/** i18n key per device verdict, under `insights.ecg.result.*`. */
export const ECG_RESULT_LABEL_KEYS: Record<string, string> = {
IRREGULAR: "insights.ecg.result.irregular",
+11
View File
@@ -0,0 +1,11 @@
/**
* How many strips the OVERVIEW teaser paints. It is a glance, not a history;
* the dedicated `/insights/ecg` page passes no limit and shows every
* recording.
*
* The value lives in its own React- and label-map-free module so the
* `/insights` overview page can pass it without pulling the ECG label map,
* its helpers, or the recording component across the dynamic boundary — a
* bare number is all the eager page imports.
*/
export const ECG_OVERVIEW_LIMIT = 5;