-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(fix) O3-2807: Quantity Units should be required when a quantity to dispense is specified #1636
Conversation
…h user still able to change the dispensing units
…en the quantity to dispense is greater than 0
…quantity is greater than 0
|
The Dose unit and Quantity units don't need to always be the same. E.g. the Dose unit can be Tablets, but the quantity units might be 3 boxes of Tablet, so it's wrong to assume that these will always be the same. So, the right condition I feel should be: If |
Also, the ticket is not understood properly: |
Could you add a test? |
Hi, @denniskigen! |
In general, the way I would expect this to work is:
|
…defaults to the dose unit
… defaults the quantity unit to the selected dose unit
@ibacher could you kindly take a look at the changes I have a pushed and suggest any modifications |
packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx
Outdated
Show resolved
Hide resolved
@@ -158,13 +171,29 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug | |||
duration: initialOrderBasketItem?.duration, | |||
durationUnit: initialOrderBasketItem?.durationUnit, | |||
pillsDispensed: initialOrderBasketItem?.pillsDispensed, | |||
quantityUnits: initialOrderBasketItem?.quantityUnits, | |||
quantityUnits: initialOrderBasketItem?.quantityUnits ?? initialOrderBasketItem?.unit, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already handled here:
Line 136 in 7222e81
unit: drug?.dosageForm |
@@ -177,8 +177,8 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug | |||
newValue: MedicationOrderFormData['unit'], | |||
prevValue: MedicationOrderFormData['unit'], | |||
) => { | |||
if (prevValue?.valueCoded === watch('quantityUnits')?.valueCoded) { | |||
setValue('quantityUnits', newValue); | |||
if (!watch('quantityUnits')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this condition changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If the dose unit is set (including by order template), but the quantity unit is unset, the quantity unit is set to the dose unit
- In no other circumstance do we automatically change either the dose unit or quantity unit
@vasharma05 with the previous condition, if the dose unit and quantity unit were the same, then changing the dose units was also changing the quantity unit to the new value of the dose unit. Yet according to @ibacher the dose unit should only change the quantity unit if the quantity unit is unset. Also bullet 2 above emphasizes it again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vasharma05 @ibacher could you kindly offer some clarity on this; should a change in the dose unit trigger a change in the quantity unit if they the values of both are the same? Which happens to be what @vasharma05 wants. What I understood from @ibacher is that the dose unit should only change the quantity unit if the quantity unit is not set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dosing units are only rarely dispensing units. For example, a relatively common in-patient order might be for 1000 ml of saline, which is commonly dispensed as either 1 or 2 bags, depending on availability. Here the dosing is 1000 ml, but the dispensing units are in "bags". Similarly, for paracetamol, you might be given a dose of 650 mg, but that's dispensed in the form of 2 325 mg tablets per dose. Again, the dosing is in mg, but the dispensing is in tablets. There are a few narrow cases, though, where the dose ordered is the same units as the thing dispensed. This feature is targeted at those narrow cases.
So, my opinion is that once a user has selected a dispensing unit, we should not change that, because in most cases, the dispensing unit is not going to be the same as the dosing unit. Prior to the user making that choice, however, its fine to automatically update, as for a few cases this will make people's lives easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Thanks
…rder-form.component.tsx
…ient-chart into drug-order-form
Tangential to this, but the forms inputs ought to have an error outline when validation fails, which is not the case here: Additionally, the error messages need a bit of breathing room. They're too close to the inputs. Could you help ticket that out under the bug fix epic, @vasharma05? |
return true; | ||
}, | ||
{ | ||
message: 'Please select Quantity unit', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
message: 'Please select Quantity unit', | |
message: 'Please select a quantity unit', |
@vasharma05, could you guide @mccarthyaaron on how to make these error messages translatable? We should probably add an example to our Coding Conventions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be eager to learn how to do that. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @mccarthyaaron!
You can refer #1652.
Thanks!
prevValue: MedicationOrderFormData[keyof MedicationOrderFormData], | ||
) => void; | ||
control: Control<MedicationOrderFormData>; | ||
[x: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get a better type here than any
? any
completely bails out of the type system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Properly typing this requires a discriminated union, since the values of restProps
depends on the type. Something like:
type ControlledFieldInputProps = BaseControlledFieldInputProps & (
ToggleFieldProps | CheckboxFieldProps | NumberFieldProps | TextAreaProps | TextInputProps | ComboBoxProps
)
interface BaseControlledFieldInputProps {
name: keyof MedicationOrderFormData;
handleAfterChange?: (
newValue: MedicationOrderFormData[keyof MedicationOrderFormData],
prevValue: MedicationOrderFormData[keyof MedicationOrderFormData],
) => void;
control: Control<MedicationOrderFormData>;
}
interface ToggleFieldProps extends ToggleProps /* part of Carbon */ {
type: 'toggle',
}
// repeat for other types
And that should give us something fully typed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this, @mccarthyaaron!
@ibacher I've tested things out locally against the functional requirements you laid out, and they seem to work OK for me. Do you have any qualms about the approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mostly fine with the logic. A few small nits and one edge-case requirement. Thanks for the great work @mccarthyaaron!
@@ -143,7 +156,7 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug | |||
return initialOrderBasketItem?.startDate as Date; | |||
}, [initialOrderBasketItem?.startDate]); | |||
|
|||
const { handleSubmit, control, watch, setValue } = useForm<MedicationOrderFormData>({ | |||
const methods = useForm<MedicationOrderFormData>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for the methods
variable?
newValue: MedicationOrderFormData['unit'], | ||
prevValue: MedicationOrderFormData['unit'], | ||
) => { | ||
if (prevValue?.valueCoded === watch('quantityUnits')?.valueCoded) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably be substituted (more correctly) with getValues()
(also returned from useForm()
. watch()
is intended to subscribe to input changes, but here it's used to get a point-in-time value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is passed as a prop to a component, it would be best to wrap this in useCallback()
. Otherwise, each render will re-render the ControlledFieldInput
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I don't think the logic here avoids the edge-case where the user has selected a quantityUnits
value that happens to be the same as the dosingUnits
, but then changes the dosingUnits
. I'm ok with automatically changing the value as long as its been automatically determined, but user input (especially for medication instructions) needs to always override automatic determinations.
prevValue: MedicationOrderFormData[keyof MedicationOrderFormData], | ||
) => void; | ||
control: Control<MedicationOrderFormData>; | ||
[x: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Properly typing this requires a discriminated union, since the values of restProps
depends on the type. Something like:
type ControlledFieldInputProps = BaseControlledFieldInputProps & (
ToggleFieldProps | CheckboxFieldProps | NumberFieldProps | TextAreaProps | TextInputProps | ComboBoxProps
)
interface BaseControlledFieldInputProps {
name: keyof MedicationOrderFormData;
handleAfterChange?: (
newValue: MedicationOrderFormData[keyof MedicationOrderFormData],
prevValue: MedicationOrderFormData[keyof MedicationOrderFormData],
) => void;
control: Control<MedicationOrderFormData>;
}
interface ToggleFieldProps extends ToggleProps /* part of Carbon */ {
type: 'toggle',
}
// repeat for other types
And that should give us something fully typed.
packages/esm-patient-medications-app/src/add-drug-order/drug-order-form.component.tsx
Outdated
Show resolved
Hide resolved
and wrapped handlers in a useCallback()
@ibacher pushed the requested changes. anymore recommended changes? |
@mccarthyaaron Could you take a look into the Typescript thing? |
@ibacher could you kindly direct me to a resource with the carbon types if possible? |
@mccarthyaaron The Carbon source code is quite readable and has the types. Just make sure you're looking at the v11.37.0 tag. |
For educative purposes, is there a reason it has to be that specific tag? |
Is the version of Carbon we're currently using. The current version of Carbon may have any number of differences. |
@ibacher I am running into this error: Cannot use namespace 'ToggleProps' as a type. I think I imported the correct type because the import statement bringing it in isn't throwing any errors. I don't know whats causing that. Does it need downloading the @types/carbon-components-react package |
@ibacher I have something for the types thing. Feels makeshift-ish especially with the importing of the types. For some reason I can't use the carbon types imported from '@carbon/react'. I get the error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
@mccarthyaaron Oh right... we kind of tell TS to ignore the types from Carbon... Although the preferred fallback solution would've been to mark those properties as |
Requirements
Summary
In the drug-order-form component, the dispensing quantity units will default to the dosage units if the quantity to be dispensed is greater than 0 while still giving the user the option to select their preferred quantity units. I attempt to achieve the result in two ways. Firstly changing the dosage units will trigger a corresponsing change in the quantity units but only if the quantity to be dispensed is not 0. Secondly, a change in the quantity to be dispensed to a value greater than 0 will trigger the quantity units to take on the value of the dosage units at the time. In both cases the user can still change the quantity units to a value other than the one defined in the dosing units.
Related Issue
https://openmrs.atlassian.net/browse/O3-2807
Screenshots
drug-order-form.mp4