From 844de36977b3fb8b9069f18c87ce139ca279db52 Mon Sep 17 00:00:00 2001 From: McCarthy Date: Sat, 3 Feb 2024 23:58:24 +0300 Subject: [PATCH 01/16] (fix) dispensing units in drug-order-form default to dosage units with user still able to change the dispensing units --- .../drug-order-form.component.tsx | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx index 343610da63..6d985ac580 100644 --- a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx +++ b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx @@ -166,6 +166,10 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug }, }); + const { + field: { onChange: dispensingUnitsOnChange }, + } = useController({ name: 'quantityUnits', control }); + const routeValue = watch('route')?.value; const unitValue = watch('unit')?.value; const dosage = watch('dosage'); @@ -368,6 +372,7 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug control={control} name="unit" type="comboBox" + dispensingUnitsOnChange={dispensingUnitsOnChange} size={isTablet ? 'lg' : 'md'} id="dosingUnits" items={drugDosingUnits} @@ -732,16 +737,32 @@ const ControlledFieldInput = ({ name, control, type, ...restProps }) => { ); if (type === 'comboBox') - return ( - onChange(selectedItem)} - onBlur={onBlur} - ref={ref} - className={fieldState?.error?.message && styles.fieldError} - {...restProps} - /> - ); + if (name === 'unit') { + return ( + { + onChange(selectedItem); + restProps.dispensingUnitsOnChange?.(selectedItem); + }} + onBlur={onBlur} + ref={ref} + className={fieldState?.error?.message && styles.fieldError} + {...restProps} + /> + ); + } + + return ( + onChange(selectedItem)} + onBlur={onBlur} + ref={ref} + className={fieldState?.error?.message && styles.fieldError} + {...restProps} + /> + ); return null; }, [fieldState?.error?.message, onBlur, onChange, ref, restProps, type, value]); From 772d7579c3ff1915ec54312b2f0c3e9fe28c92ca Mon Sep 17 00:00:00 2001 From: McCarthy Date: Sun, 4 Feb 2024 00:58:59 +0300 Subject: [PATCH 02/16] changes in dosage unit only trigger changes in the dispensing unit when the quantity to dispense is greater than 0 --- .../src/add-drug-order/drug-order-form.component.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx index 6d985ac580..d9118252c6 100644 --- a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx +++ b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx @@ -372,6 +372,7 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug control={control} name="unit" type="comboBox" + watch={watch} dispensingUnitsOnChange={dispensingUnitsOnChange} size={isTablet ? 'lg' : 'md'} id="dosingUnits" @@ -743,7 +744,10 @@ const ControlledFieldInput = ({ name, control, type, ...restProps }) => { selectedItem={value} onChange={({ selectedItem }) => { onChange(selectedItem); - restProps.dispensingUnitsOnChange?.(selectedItem); + const dispensingQuantity = restProps.watch?.('pillsDispensed'); + if (dispensingQuantity && Number(dispensingQuantity) > 0) { + restProps.dispensingUnitsOnChange?.(selectedItem); + } }} onBlur={onBlur} ref={ref} From 7d91e9e4975e2dfc56779bb503f9b31ba8d6a25f Mon Sep 17 00:00:00 2001 From: McCarthy Date: Sun, 4 Feb 2024 01:10:35 +0300 Subject: [PATCH 03/16] the dispensing units default to the dosing units when the dispensing quantity is greater than 0 --- .../src/add-drug-order/drug-order-form.component.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx index d9118252c6..f5d2907d66 100644 --- a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx +++ b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import capitalize from 'lodash-es/capitalize'; @@ -169,6 +169,16 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug const { field: { onChange: dispensingUnitsOnChange }, } = useController({ name: 'quantityUnits', control }); + const { + field: { value: dispensingQuantity }, + } = useController({ name: 'pillsDispensed', control }); + + useEffect(() => { + if (dispensingQuantity && Number(dispensingQuantity) > 0) { + const currentDosageUnit = watch('unit'); + dispensingUnitsOnChange(currentDosageUnit); + } + }, [dispensingQuantity]); const routeValue = watch('route')?.value; const unitValue = watch('unit')?.value; From 08ea98bc7e11d66bb79f6a39d20b891da2e6bb4b Mon Sep 17 00:00:00 2001 From: Vineet Sharma Date: Tue, 6 Feb 2024 14:09:06 +0530 Subject: [PATCH 04/16] Quantity units should be same as dosing units by default --- .../drug-order-form.component.tsx | 757 +++++++++--------- 1 file changed, 376 insertions(+), 381 deletions(-) diff --git a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx index f5d2907d66..b2a12ca403 100644 --- a/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx +++ b/packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import capitalize from 'lodash-es/capitalize'; @@ -24,7 +24,7 @@ import { import { Add, ArrowLeft, Subtract } from '@carbon/react/icons'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Controller, useController, useForm } from 'react-hook-form'; +import { Controller, FormProvider, useController, useForm, useFormContext } from 'react-hook-form'; import { age, formatDate, parseDate, useConfig, useLayoutType, usePatient } from '@openmrs/esm-framework'; import { useOrderConfig } from '../api/order-config'; import { type ConfigObject } from '../config-schema'; @@ -143,7 +143,7 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug return initialOrderBasketItem?.startDate as Date; }, [initialOrderBasketItem?.startDate]); - const { handleSubmit, control, watch, setValue } = useForm({ + const methods = useForm({ mode: 'all', resolver: zodResolver(medicationOrderFormSchema), defaultValues: { @@ -165,20 +165,16 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug startDate: defaultStartDate, }, }); - - const { - field: { onChange: dispensingUnitsOnChange }, - } = useController({ name: 'quantityUnits', control }); - const { - field: { value: dispensingQuantity }, - } = useController({ name: 'pillsDispensed', control }); - - useEffect(() => { - if (dispensingQuantity && Number(dispensingQuantity) > 0) { - const currentDosageUnit = watch('unit'); - dispensingUnitsOnChange(currentDosageUnit); + const { handleSubmit, control, watch, setValue } = methods; + + const handleUnitAfterChange = ( + newValue: MedicationOrderFormData['unit'], + prevValue: MedicationOrderFormData['unit'], + ) => { + if (prevValue?.valueCoded === watch('quantityUnits')?.valueCoded) { + setValue('quantityUnits', newValue); } - }, [dispensingQuantity]); + }; const routeValue = watch('route')?.value; const unitValue = watch('unit')?.value; @@ -271,54 +267,10 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug ); return ( -
- {showStickyMedicationHeader && ( -
- -
- )} - {isTablet && !isLoadingPatientDetails && ( -
- {patientName} - - {capitalize(patient?.gender)} · {age(patient?.birthDate)} ·{' '} - {formatDate(parseDate(patient?.birthDate), { mode: 'wide', time: false })} - -
- )} - -
-
- {errorFetchingOrderConfig && ( - - )} - {!isTablet && ( -
- -
- )} - -

{t('orderForm', 'Order Form')}

-
+ +
+ {showStickyMedicationHeader && ( +
-
- - -

{t('dosageInstructions', '1. Dosage instructions')}

-
- - + {patientName} + + {capitalize(patient?.gender)} · {age(patient?.birthDate)} ·{' '} + {formatDate(parseDate(patient?.birthDate), { mode: 'wide', time: false })} + +
+ )} + + +
+ {errorFetchingOrderConfig && ( + + )} + {!isTablet && ( +
+ +
+ )} + +

{t('orderForm', 'Order Form')}

+
+ +
+
- + +

{t('dosageInstructions', '1. Dosage instructions')}

+
+
- ) : ( - <> + {watch('isFreeTextDosage') ? ( - - -
+ + + + + ) : ( + <> + + + +
+ +
+
+
+ + item?.value} + handleAfterChange={handleUnitAfterChange} /> -
-
-
- - - item?.value} - /> - - -
- - + + + + + + + item?.value} + /> + + + + + item?.value} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )} +
+
+

{t('prescriptionDuration', '2. Prescription duration')}

+ + {/* TODO: This input does nothing */} + +
- item?.value} + render={({ field: { onBlur, value, onChange, ref } }) => ( + onChange(newStartDate)} + onBlur={onBlur} + ref={ref} + > + + + )} /> - - - +
+
+ + + {!isTablet ? ( item?.value} + name="duration" + type="number" + size="lg" + id="durationInput" + label={t('duration', 'Duration')} + min={0} + step={1} + max={maxDispenseDurationInDays} + allowEmpty={true} /> - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - )} -
-
-

{t('prescriptionDuration', '2. Prescription duration')}

- - {/* TODO: This input does nothing */} - -
+ )} + + + - ( - onChange(newStartDate)} - onBlur={onBlur} - ref={ref} - > - - - )} + name="durationUnit" + type="comboBox" + size="lg" + id="durationUnitPlaceholder" + titleText={t('durationUnit', 'Duration unit')} + items={durationUnits} + itemToString={(item) => item?.value} + placeholder={t('durationUnitPlaceholder', 'Duration Unit')} /> -
-
- - - {!isTablet ? ( + +
+
+
+

{t('dispensingInformation', '3. Dispensing instructions')}

+ + + - ) : ( - - )} - - - - - item?.value} - placeholder={t('durationUnitPlaceholder', 'Duration Unit')} - /> - - - -
-
-

{t('dispensingInformation', '3. Dispensing instructions')}

- - - - - - - - - item?.value} - /> - - - - - {!isTablet ? ( + + + + item?.value} /> - ) : ( - + + + + {!isTablet ? ( + + ) : ( + + )} + + + + + + + - )} - - - - - - - - - - -
-
- - - -
+ + - {t('saveOrder', 'Save order')} - - - -
+ + + + +
+ ); } @@ -691,31 +688,48 @@ const CustomNumberInput = ({ setValue, control, name, labelText, ...inputProps } ); }; -const ControlledFieldInput = ({ name, control, type, ...restProps }) => { +interface ControlledFieldInputProps { + name: keyof MedicationOrderFormData; + type: 'toggle' | 'checkbox' | 'number' | 'textArea' | 'textInput' | 'comboBox'; + handleAfterChange?: ( + newValue: MedicationOrderFormData[keyof MedicationOrderFormData], + prevValue: MedicationOrderFormData[keyof MedicationOrderFormData], + ) => void; + [x: string]: any; +} + +const ControlledFieldInput = ({ name, type, handleAfterChange, ...restProps }: ControlledFieldInputProps) => { + const { control, watch } = useFormContext(); const { field: { onBlur, onChange, value, ref }, fieldState, } = useController({ name: name, control }); + const handleChange = (newValue: MedicationOrderFormData[keyof MedicationOrderFormData]) => { + const prevValue = watch(name); + onChange(newValue); + handleAfterChange?.(newValue, prevValue); + }; + const component = useMemo(() => { if (type === 'toggle') return ( {} /* Required by the typings, but we don't need it. */} - onToggle={(value) => onChange(value)} + onToggle={(value) => handleChange(value)} {...restProps} /> ); if (type === 'checkbox') - return onChange(checked)} {...restProps} />; + return handleChange(checked)} {...restProps} />; if (type === 'number') return ( onChange(parseFloat(value))} + onChange={(e, { value }) => handleChange(parseFloat(value))} className={fieldState?.error?.message && styles.fieldError} onBlur={onBlur} ref={ref} @@ -727,7 +741,7 @@ const ControlledFieldInput = ({ name, control, type, ...restProps }) => { return (