mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
Every place a lab unit is printed beside a value now goes through one helper, so a reading stored in "1" reads "3.6" and not "3.6 1": the list, history and detail, the chart and its tooltips, the reference bar and range notes, the trend card, the timeline, the day view, the clinician page, the doctor-report PDF and the changes card. Where the unit stands alone (a marker's subtitle, a picker row, the merge preview) it reads "No unit". The Coach's lab block, the get_labs history and the visit-prep summary hand the model an empty unit for such a value, so it never quotes the "1" either. Refs #1313
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
"use client";
|
||
|
||
import { formatReferenceRange } from "@/lib/labs/reference-range";
|
||
import { useTranslations } from "@/lib/i18n/context";
|
||
|
||
import type { LabResultDto } from "./types";
|
||
import { useLabNumber } from "./use-lab-format";
|
||
import { withLabUnit } from "@/lib/labs/dimensionless";
|
||
|
||
/**
|
||
* The reference window a reading's own report printed, shown wherever that
|
||
* reading is shown.
|
||
*
|
||
* Renders nothing for a reading with no printed window — most of them — so it
|
||
* costs no space on the common row. When the report DID state a window it
|
||
* names it, verbatim where the report gave words; and when that window and the
|
||
* catalog band disagree it names the catalog band too. A reader told only
|
||
* "3,9–5,4" and a reader told only "3,5–5,0" draw different conclusions from
|
||
* the same number, so a surface that shows one silently is showing a partial
|
||
* answer.
|
||
*
|
||
* A reading converted into its marker's unit carries bounds that were converted
|
||
* with it, but the report's own text is still in the unit it printed. The note
|
||
* then states the window in the marker's unit and adds the printed text as
|
||
* evidence, labelled with its unit, so no single statement mixes two units.
|
||
*
|
||
* Neutral by construction: no tint, no icon, muted text. The in/out verdict is
|
||
* the badge's job — this element states where the window came from, nothing
|
||
* about whether the value is good news.
|
||
*/
|
||
export function SourceRangeNote({
|
||
reading,
|
||
className,
|
||
}: {
|
||
reading: Pick<
|
||
LabResultDto,
|
||
| "unit"
|
||
| "referenceLow"
|
||
| "referenceHigh"
|
||
| "catalogReferenceLow"
|
||
| "catalogReferenceHigh"
|
||
| "sourceReferenceText"
|
||
| "printedReferenceText"
|
||
| "sourceUnit"
|
||
| "referenceOrigin"
|
||
| "referenceDivergesFromCatalog"
|
||
>;
|
||
className?: string;
|
||
}) {
|
||
const { t } = useTranslations();
|
||
const labNumber = useLabNumber();
|
||
if (reading.referenceOrigin !== "source") return null;
|
||
|
||
// `sourceReferenceText` is null for a converted reading, so the window is
|
||
// then stated from the converted bounds, in the marker's unit.
|
||
const inForce =
|
||
reading.sourceReferenceText ??
|
||
withLabUnit(
|
||
formatReferenceRange(
|
||
reading.referenceLow,
|
||
reading.referenceHigh,
|
||
labNumber,
|
||
),
|
||
reading.unit,
|
||
);
|
||
|
||
const catalog = formatReferenceRange(
|
||
reading.catalogReferenceLow,
|
||
reading.catalogReferenceHigh,
|
||
labNumber,
|
||
);
|
||
|
||
return (
|
||
<span className={className}>
|
||
{t("labs.sourceRange.fromReport", { range: inForce })}
|
||
{reading.printedReferenceText && reading.sourceUnit
|
||
? ` · ${t("labs.sourceRange.printed", {
|
||
range: reading.printedReferenceText,
|
||
unit: reading.sourceUnit,
|
||
})}`
|
||
: null}
|
||
{reading.referenceDivergesFromCatalog && catalog
|
||
? ` · ${t("labs.sourceRange.catalogDiffers", {
|
||
range: withLabUnit(catalog, reading.unit),
|
||
})}`
|
||
: null}
|
||
</span>
|
||
);
|
||
}
|