feat: simplify tube stock editing UI (#357)
* feat: add package amount persistence and backend route support * test: align backend test schemas with medication metadata fields * fix(backend): restore intake usage normalizer for planner endpoint * fix(backend): keep export typing compatible before liquid-unit stack step * feat: simplify tube stock editing in desktop and mobile forms
This commit is contained in:
@@ -8,6 +8,9 @@ const defaultForm: FormState = {
|
||||
name: "",
|
||||
genericName: "",
|
||||
takenBy: [],
|
||||
medicationForm: "tablet",
|
||||
pillForm: "tablet",
|
||||
lifecycleCategory: "refill_when_empty",
|
||||
packageType: "blister",
|
||||
packCount: "1",
|
||||
blistersPerPack: "1",
|
||||
@@ -17,6 +20,8 @@ const defaultForm: FormState = {
|
||||
pillWeightMg: "",
|
||||
doseUnit: "mg",
|
||||
medicationStartDate: "",
|
||||
medicationEndDate: "",
|
||||
autoMarkObsoleteAfterEndDate: true,
|
||||
expiryDate: "",
|
||||
notes: "",
|
||||
intakeRemindersEnabled: false,
|
||||
@@ -235,6 +240,54 @@ describe("MobileEditModal", () => {
|
||||
const header = document.querySelector(".edit-modal-header");
|
||||
expect(header).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses plain numeric input for tube amount without stepper controls", () => {
|
||||
render(
|
||||
<MobileEditModal
|
||||
{...defaultProps}
|
||||
form={{
|
||||
...defaultForm,
|
||||
packageType: "tube",
|
||||
medicationForm: "topical",
|
||||
packageAmountValue: "150",
|
||||
packageAmountUnit: "g",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const amountInput = screen.getByLabelText("form.packageAmountPerTube") as HTMLInputElement;
|
||||
expect(amountInput).toBeInTheDocument();
|
||||
expect(amountInput.tagName).toBe("INPUT");
|
||||
expect(amountInput).toHaveAttribute("inputmode", "decimal");
|
||||
|
||||
const unitSelect = screen.getByLabelText("form.packageAmountUnitG") as HTMLSelectElement;
|
||||
expect(unitSelect).toBeDisabled();
|
||||
expect(unitSelect.value).toBe("g");
|
||||
});
|
||||
|
||||
it("uses plain numeric input for liquid container package amount", () => {
|
||||
render(
|
||||
<MobileEditModal
|
||||
{...defaultProps}
|
||||
form={{
|
||||
...defaultForm,
|
||||
packageType: "liquid_container",
|
||||
medicationForm: "liquid",
|
||||
packageAmountValue: "250",
|
||||
packageAmountUnit: "ml",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const amountInput = screen.getByLabelText("form.packageAmount") as HTMLInputElement;
|
||||
expect(amountInput).toBeInTheDocument();
|
||||
expect(amountInput.tagName).toBe("INPUT");
|
||||
expect(amountInput).toHaveAttribute("inputmode", "decimal");
|
||||
|
||||
const unitSelect = screen.getByLabelText("form.packageAmountUnitMl") as HTMLSelectElement;
|
||||
expect(unitSelect).toBeDisabled();
|
||||
expect(unitSelect.value).toBe("ml");
|
||||
});
|
||||
});
|
||||
|
||||
describe("MobileEditModal with existing people", () => {
|
||||
|
||||
@@ -155,6 +155,78 @@ describe("useMedicationForm", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("enforces liquid defaults when packageType is liquid_container", () => {
|
||||
const { result } = renderHook(() => useMedicationForm());
|
||||
|
||||
act(() => {
|
||||
result.current.handleValueChange("packageType", "liquid_container");
|
||||
});
|
||||
|
||||
expect(result.current.form.packageType).toBe("liquid_container");
|
||||
expect(result.current.form.medicationForm).toBe("liquid");
|
||||
expect(result.current.form.lifecycleCategory).toBe("refill_when_empty");
|
||||
expect(result.current.form.doseUnit).toBe("ml");
|
||||
expect(result.current.form.packageAmountUnit).toBe("ml");
|
||||
});
|
||||
|
||||
it("keeps liquid settings locked when editing medicationForm under liquid_container", () => {
|
||||
const { result } = renderHook(() => useMedicationForm());
|
||||
|
||||
act(() => {
|
||||
result.current.handleValueChange("packageType", "liquid_container");
|
||||
result.current.handleValueChange("medicationForm", "tablet");
|
||||
});
|
||||
|
||||
expect(result.current.form.packageType).toBe("liquid_container");
|
||||
expect(result.current.form.medicationForm).toBe("liquid");
|
||||
expect(result.current.form.doseUnit).toBe("ml");
|
||||
expect(result.current.form.packageAmountUnit).toBe("ml");
|
||||
});
|
||||
|
||||
it("enforces tube defaults and locks amount unit to grams", () => {
|
||||
const { result } = renderHook(() => useMedicationForm());
|
||||
|
||||
act(() => {
|
||||
result.current.handleValueChange("packageType", "tube");
|
||||
result.current.handleValueChange("medicationForm", "liquid");
|
||||
result.current.handleValueChange("packageAmountUnit", "ml");
|
||||
});
|
||||
|
||||
expect(result.current.form.packageType).toBe("tube");
|
||||
expect(result.current.form.medicationForm).toBe("topical");
|
||||
expect(result.current.form.lifecycleCategory).toBe("treatment_period");
|
||||
expect(result.current.form.doseUnit).toBe("units");
|
||||
expect(result.current.form.packageAmountUnit).toBe("g");
|
||||
});
|
||||
|
||||
it("normalizes legacy tube records to grams in startEdit", () => {
|
||||
const { result } = renderHook(() => useMedicationForm());
|
||||
const openEditModal = vi.fn();
|
||||
Object.defineProperty(window, "innerWidth", { value: 1024, writable: true });
|
||||
|
||||
const med: Medication = {
|
||||
id: 12,
|
||||
name: "Topical Gel",
|
||||
takenBy: [],
|
||||
packageType: "tube",
|
||||
packageAmountUnit: "ml",
|
||||
packageAmountValue: 150,
|
||||
packCount: 1,
|
||||
blistersPerPack: 1,
|
||||
pillsPerBlister: 1,
|
||||
looseTablets: 0,
|
||||
blisters: [{ usage: 1, every: 1, start: "2026-01-01T08:00:00.000Z" }],
|
||||
updatedAt: null,
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.startEdit(med, openEditModal);
|
||||
});
|
||||
|
||||
expect(result.current.form.packageType).toBe("tube");
|
||||
expect(result.current.form.packageAmountUnit).toBe("g");
|
||||
});
|
||||
|
||||
it("adds, edits and removes blister rows", () => {
|
||||
const { result } = renderHook(() => useMedicationForm());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user