mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
Three surfaces built the label key by interpolation — the doctor-report PDF's visit table, the daily digest's upcoming-visit line and the clinician share view. `EncounterKind` is `ROUTINE` and the bundle leaf is `routine`, so all three rendered `encounters.kind.ROUTINE` verbatim: into a document a practice files, onto a lock screen, and onto the page. Route them through `encounterKindLabelKey`, a literal switch beside the existing `encounterKindLabel`, so the mapping is written down once. The enum-derived i18n guard could not have caught this: it only knows key spaces of the form `prefix + member`, and this one is not. It gains a second arm for spaces whose mapping is a function, with `encounters.kind` as its first entry. Break-proof: point one arm of the resolver back at the uppercase key and six locale cases go red.
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
/**
|
|
* The human name of a visit kind, in the record owner's language.
|
|
*
|
|
* Spelled out with literal `t()` calls rather than an interpolated key, for the
|
|
* same reason the restore-skip catalogue labels are: an interpolated key is
|
|
* invisible to `i18n-call-site-coverage`, so a renamed or missing kind would
|
|
* ship as raw dot notation instead of failing CI.
|
|
*
|
|
* This is not decoration. The value becomes a `MeasurementReminder.label`, and
|
|
* that label IS the push body — so the fallback for a visit with no practice
|
|
* attached is read off a lock screen. A raw enum constant there is the schema
|
|
* leaking into the product.
|
|
*/
|
|
import type { EncounterKind } from "@/generated/prisma/client";
|
|
import { getServerTranslator } from "@/lib/i18n/server-translator";
|
|
import { defaultLocale, locales, type Locale } from "@/lib/i18n/config";
|
|
|
|
function resolveLocale(locale: string | null | undefined): Locale {
|
|
return locales.includes(locale as Locale)
|
|
? (locale as Locale)
|
|
: defaultLocale;
|
|
}
|
|
|
|
export function encounterKindLabel(
|
|
kind: EncounterKind,
|
|
locale: string | null | undefined,
|
|
): string {
|
|
const { t } = getServerTranslator(resolveLocale(locale));
|
|
switch (kind) {
|
|
case "ROUTINE":
|
|
return t("encounters.kind.routine");
|
|
case "ACUTE":
|
|
return t("encounters.kind.acute");
|
|
case "SPECIALIST":
|
|
return t("encounters.kind.specialist");
|
|
case "PREVENTIVE":
|
|
return t("encounters.kind.preventive");
|
|
case "EMERGENCY":
|
|
return t("encounters.kind.emergency");
|
|
case "HOSPITAL":
|
|
return t("encounters.kind.hospital");
|
|
case "THERAPY":
|
|
return t("encounters.kind.therapy");
|
|
default:
|
|
return t("encounters.kind.other");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The same mapping as a KEY, for callers that already hold a translator and
|
|
* cannot reach for a second one — the doctor-report PDF, the daily digest and
|
|
* the clinician share view.
|
|
*
|
|
* Those three built the key by interpolation (`encounters.kind.${kind}`), and
|
|
* the enum is `ROUTINE` while the bundle leaf is `routine`, so all three
|
|
* printed raw dot notation into a clinical document. The interpolated form was
|
|
* invisible to `i18n-call-site-coverage`, which is the reason it survived: the
|
|
* key it asked for never existed and nothing said so.
|
|
*/
|
|
export function encounterKindLabelKey(kind: EncounterKind): string {
|
|
switch (kind) {
|
|
case "ROUTINE":
|
|
return "encounters.kind.routine";
|
|
case "ACUTE":
|
|
return "encounters.kind.acute";
|
|
case "SPECIALIST":
|
|
return "encounters.kind.specialist";
|
|
case "PREVENTIVE":
|
|
return "encounters.kind.preventive";
|
|
case "EMERGENCY":
|
|
return "encounters.kind.emergency";
|
|
case "HOSPITAL":
|
|
return "encounters.kind.hospital";
|
|
case "THERAPY":
|
|
return "encounters.kind.therapy";
|
|
default:
|
|
return "encounters.kind.other";
|
|
}
|
|
}
|