mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
feat(medications): Wirkung tab + Insights efficacy summary
Add the per-medication "Wirkung" tab (hidden for one-shot and no-target meds) rendering the target series with a start marker, dose-change markers, shaded pause bands and a faint reference band, an adherence lane, a neutral before/after-start card with honest empty states, an optional level-shift note, and a retarget chooser. Weave a compact, read-only efficacy summary into the Insights medication surface that reads the same DTO and links through to the tab. All copy is descriptive; add a banned-verdict-verb guard test over the medications.efficacy namespace across all six locales.
This commit is contained in:
1 parent
725f69097f
commit
8617fa7129
14 files changed
+1127
-14
No files matched your search
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* v1.28 — banned-verb guard for the `medications.efficacy.*` namespace.
|
||||
*
|
||||
* The medication-efficacy view ("Wirkung") is a medication-adjacent claim, so
|
||||
* its safety boundary is structural, not left to reviewer vigilance: the DTO
|
||||
* carries no verdict / score field, and the copy must stay strictly
|
||||
* descriptive — an association, never "the drug works / is effective", never a
|
||||
* dose change. This test walks every efficacy string in all six locales and
|
||||
* fails if any value contains a verdict or dose-advice verb, so a forbidden
|
||||
* word can never creep into the bundle. Mirrors the `i18n-english-leak-guard`
|
||||
* flatten + filter + `toEqual([])` pattern.
|
||||
*
|
||||
* If this fails: rewrite the offending string to descriptive phrasing (numbers
|
||||
* + neutral connectives). Do NOT weaken the denylist to pass.
|
||||
*/
|
||||
import { readFileSync, readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
const MESSAGES = join(__dirname, "..", "..", "messages");
|
||||
const LOCALES = readdirSync(MESSAGES).filter((f) => f.endsWith(".json"));
|
||||
|
||||
/** Verdict + efficacy-adjective + dose-advice verbs across the six locales. */
|
||||
const BANNED: RegExp[] = [
|
||||
// English
|
||||
/\bworks?\b/i,
|
||||
/\bworking\b/i,
|
||||
/\beffective(ly)?\b/i,
|
||||
/\bineffective\b/i,
|
||||
/\bcures?\b/i,
|
||||
/\bcured\b/i,
|
||||
/\bheals?\b/i,
|
||||
/\bhealed\b/i,
|
||||
/\bimprove(d|s|ment)?\b/i,
|
||||
/\bworsen(ed|s)?\b/i,
|
||||
/\bbetter\b/i,
|
||||
/\bworse\b/i,
|
||||
/\bincrease\b/i,
|
||||
/\bdecrease\b/i,
|
||||
/\breduce\b/i,
|
||||
/\braise\b/i,
|
||||
/\bsuccess(ful)?\b/i,
|
||||
// German
|
||||
/\bwirksam\b/i,
|
||||
/\bunwirksam\b/i,
|
||||
/\banschlägt\b/i,
|
||||
/\bheilt\b/i,
|
||||
/\bverbessert\b/i,
|
||||
/\bverschlechtert\b/i,
|
||||
/\berhöhen\b/i,
|
||||
/\bsenken\b/i,
|
||||
/\babsetzen\b/i,
|
||||
// Spanish
|
||||
/\bfunciona\b/i,
|
||||
/\beficaz\b/i,
|
||||
/\bineficaz\b/i,
|
||||
/\bmejora\b/i,
|
||||
/\bempeora\b/i,
|
||||
// French
|
||||
/\befficace\b/i,
|
||||
/\binefficace\b/i,
|
||||
/\baméliore\b/i,
|
||||
/\baggrave\b/i,
|
||||
// Italian
|
||||
/\bmigliora\b/i,
|
||||
/\bpeggiora\b/i,
|
||||
// Polish
|
||||
/\bskuteczn\w*\b/i,
|
||||
/\bpoprawia\b/i,
|
||||
/\bpogarsza\b/i,
|
||||
];
|
||||
|
||||
function flatten(obj: unknown, prefix: string, out: [string, string][]): void {
|
||||
if (obj === null || typeof obj !== "object") return;
|
||||
for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
|
||||
const key = prefix ? `${prefix}.${k}` : k;
|
||||
if (typeof v === "string") out.push([key, v]);
|
||||
else flatten(v, key, out);
|
||||
}
|
||||
}
|
||||
|
||||
describe("medications.efficacy — banned verdict/advice verbs", () => {
|
||||
it.each(LOCALES)("%s carries no forbidden verb", (file) => {
|
||||
const bundle = JSON.parse(readFileSync(join(MESSAGES, file), "utf8")) as {
|
||||
medications?: { efficacy?: unknown };
|
||||
};
|
||||
const efficacy = bundle.medications?.efficacy;
|
||||
expect(efficacy, `${file} is missing medications.efficacy`).toBeDefined();
|
||||
|
||||
const flat: [string, string][] = [];
|
||||
flatten(efficacy, "medications.efficacy", flat);
|
||||
expect(flat.length).toBeGreaterThan(0);
|
||||
|
||||
const violations = flat
|
||||
.filter(([, value]) => BANNED.some((re) => re.test(value)))
|
||||
.map(([key, value]) => `${key} = ${JSON.stringify(value)}`);
|
||||
|
||||
expect(
|
||||
violations,
|
||||
`Forbidden verdict/advice verb in ${file} — the efficacy view is ` +
|
||||
`association-only. Rewrite to descriptive phrasing:\n` +
|
||||
violations.map((v) => ` ${v}`).join("\n"),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user