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 f67d0c7206..986f65f3a5 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 @@ -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 { type Control, Controller, useController, useForm } from 'react-hook-form'; import { age, formatDate, @@ -107,21 +107,35 @@ const schemaFields = { ), }; -const medicationOrderFormSchema = z.discriminatedUnion('isFreeTextDosage', [ - z.object({ - ...schemaFields, - isFreeTextDosage: z.literal(false), - freeTextDosage: z.string().optional(), - }), - z.object({ - ...schemaFields, - isFreeTextDosage: z.literal(true), - dosage: z.number().nullable(), - unit: z.object({ ...comboSchema }).nullable(), - route: z.object({ ...comboSchema }).nullable(), - frequency: z.object({ ...comboSchema }).nullable(), - }), -]); +const medicationOrderFormSchema = z + .discriminatedUnion('isFreeTextDosage', [ + z.object({ + ...schemaFields, + isFreeTextDosage: z.literal(false), + freeTextDosage: z.string().optional(), + }), + z.object({ + ...schemaFields, + isFreeTextDosage: z.literal(true), + dosage: z.number().nullable(), + unit: z.object({ ...comboSchema }).nullable(), + route: z.object({ ...comboSchema }).nullable(), + frequency: z.object({ ...comboSchema }).nullable(), + }), + ]) + .refine( + (formValues) => { + if (formValues.pillsDispensed > 0) { + return Boolean(formValues.quantityUnits); + } + return true; + }, + { + // t('selectQuantityUnitsErrorMessage', 'Please select quantity unit') + message: translateFrom(moduleName, 'selectQuantityUnitsErrorMessage', 'Please select quantity unit'), + path: ['quantityUnits'], + }, + ); type MedicationOrderFormData = z.infer; @@ -182,7 +196,14 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug return initialOrderBasketItem?.startDate as Date; }, [initialOrderBasketItem?.startDate]); - const { handleSubmit, control, watch, setValue } = useForm({ + const { + handleSubmit, + control, + watch, + getValues, + setValue, + formState: { errors }, + } = useForm({ mode: 'all', resolver: zodResolver(medicationOrderFormSchema), defaultValues: { @@ -205,6 +226,15 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug }, }); + const handleUnitAfterChange = useCallback( + (newValue: MedicationOrderFormData['unit'], prevValue: MedicationOrderFormData['unit']) => { + if (prevValue?.valueCoded === getValues('quantityUnits')?.valueCoded) { + setValue('quantityUnits', newValue, { shouldValidate: true }); + } + }, + [setValue], + ); + const routeValue = watch('route')?.value; const unitValue = watch('unit')?.value; const dosage = watch('dosage'); @@ -316,7 +346,6 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug )} -
{errorFetchingOrderConfig && ( @@ -407,12 +436,14 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel }: Drug control={control} name="unit" type="comboBox" + getValues={getValues} size={isTablet ? 'lg' : 'md'} id="dosingUnits" items={drugDosingUnits} placeholder={t('editDosageUnitsPlaceholder', 'Unit')} titleText={t('editDosageUnitsTitle', 'Dose unit')} itemToString={(item) => item?.value} + handleAfterChange={handleUnitAfterChange} /> @@ -714,31 +745,58 @@ 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; + control: Control; + [x: string]: any; +} + +const ControlledFieldInput = ({ + name, + type, + control, + getValues, + handleAfterChange, + ...restProps +}: ControlledFieldInputProps) => { const { field: { onBlur, onChange, value, ref }, fieldState, } = useController({ name: name, control }); + const handleChange = useCallback( + (newValue: MedicationOrderFormData[keyof MedicationOrderFormData]) => { + const prevValue = getValues?.(name); + onChange(newValue); + handleAfterChange?.(newValue, prevValue); + }, + [getValues, onChange, handleAfterChange], + ); + 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} @@ -750,7 +808,7 @@ const ControlledFieldInput = ({ name, control, type, ...restProps }) => { return (