feat: replace hardcoded package assumptions with profile abstraction (#379)

This commit is contained in:
Daniel Volz
2026-03-04 21:15:05 +01:00
committed by GitHub
parent 6672fb78c9
commit 4936929849
23 changed files with 440 additions and 289 deletions
+16 -3
View File
@@ -2,7 +2,20 @@
// Core Types for MedAssist
// =============================================================================
export type PackageType = "blister" | "bottle" | "tube" | "liquid_container";
export type { PackageProfile, PackageType } from "./package-profiles";
export {
allowsPillFormSelection,
getPackageProfile,
isAmountBasedPackageType,
isLiquidContainerPackageType,
isTubePackageType,
normalizePackageType,
PACKAGE_PROFILES,
PACKAGE_TYPES,
} from "./package-profiles";
import type { PackageType } from "./package-profiles";
import { isAmountBasedPackageType } from "./package-profiles";
// Common medication dose units
export type DoseUnit = "mg" | "g" | "mcg" | "ml" | "units";
@@ -280,7 +293,7 @@ type MedLike = Pick<Medication, "packCount" | "blistersPerPack" | "pillsPerBlist
export function getMedTotal(med: MedLike): number {
// Amount-based package types store their current base stock directly
// in totalPills (fallback looseTablets for legacy rows).
if (med.packageType === "bottle" || med.packageType === "tube" || med.packageType === "liquid_container") {
if (isAmountBasedPackageType(med.packageType)) {
const baseStock = med.totalPills ?? med.looseTablets;
return baseStock + (med.stockAdjustment ?? 0);
}
@@ -291,7 +304,7 @@ export function getMedTotal(med: MedLike): number {
/** Get the base package size (without stockAdjustment) */
export function getPackageSize(med: MedLike): number {
// Amount-based package types use totalPills as base capacity
if (med.packageType === "bottle" || med.packageType === "tube" || med.packageType === "liquid_container") {
if (isAmountBasedPackageType(med.packageType)) {
return med.totalPills ?? med.looseTablets;
}
// For blister type, calculate from packs + loose
+72
View File
@@ -0,0 +1,72 @@
export const PACKAGE_TYPES = ["blister", "bottle", "tube", "liquid_container"] as const;
export type PackageType = (typeof PACKAGE_TYPES)[number];
export type PackageProfile = {
value: PackageType;
labelKey: string;
amountBased: boolean;
plannerUnitKind: "pills" | "ml" | "units";
allowsPillFormSelection: boolean;
};
export const PACKAGE_PROFILES: PackageProfile[] = [
{
value: "blister",
labelKey: "form.packageTypeBlister",
amountBased: false,
plannerUnitKind: "pills",
allowsPillFormSelection: true,
},
{
value: "bottle",
labelKey: "form.packageTypeBottle",
amountBased: true,
plannerUnitKind: "pills",
allowsPillFormSelection: true,
},
{
value: "tube",
labelKey: "form.packageTypeTube",
amountBased: true,
plannerUnitKind: "units",
allowsPillFormSelection: false,
},
{
value: "liquid_container",
labelKey: "form.packageTypeLiquidContainer",
amountBased: true,
plannerUnitKind: "ml",
allowsPillFormSelection: false,
},
];
const PACKAGE_TYPE_SET = new Set<string>(PACKAGE_TYPES);
const PROFILE_BY_TYPE = new Map(PACKAGE_PROFILES.map((profile) => [profile.value, profile] as const));
export function normalizePackageType(packageType?: string | null): PackageType {
if (packageType && PACKAGE_TYPE_SET.has(packageType)) {
return packageType as PackageType;
}
return "blister";
}
export function getPackageProfile(packageType?: string | null): PackageProfile {
return PROFILE_BY_TYPE.get(normalizePackageType(packageType)) ?? PACKAGE_PROFILES[0];
}
export function isTubePackageType(packageType?: string | null): boolean {
return normalizePackageType(packageType) === "tube";
}
export function isLiquidContainerPackageType(packageType?: string | null): boolean {
return normalizePackageType(packageType) === "liquid_container";
}
export function isAmountBasedPackageType(packageType?: string | null): boolean {
return getPackageProfile(packageType).amountBased;
}
export function allowsPillFormSelection(packageType?: string | null): boolean {
return getPackageProfile(packageType).allowsPillFormSelection;
}