diff --git a/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx b/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx index 25c3608213..3e51d440b0 100644 --- a/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx +++ b/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx @@ -65,12 +65,10 @@ const MedicationSummary: React.FC = ({ medications }) => )}

- {medication?.order?.orderReasonNonCoded ? ( - - {t('indication', 'Indication').toUpperCase()}{' '} - {medication?.order?.orderReasonNonCoded} - - ) : null} + + {t('indication', 'Indication').toUpperCase()}{' '} + {medication?.order?.orderReasonNonCoded ?? t('noIndicationProvided', 'No indication provided')} + {medication?.order?.quantity ? ( — {t('quantity', 'Quantity').toUpperCase()}{' '} diff --git a/packages/esm-patient-chart-app/translations/en.json b/packages/esm-patient-chart-app/translations/en.json index 89b919859d..63cbac5e5d 100644 --- a/packages/esm-patient-chart-app/translations/en.json +++ b/packages/esm-patient-chart-app/translations/en.json @@ -104,6 +104,7 @@ "noDiagnosesFound": "No diagnoses found", "noEncountersFound": "No encounters found", "noEncountersToDisplay": "No encounters to display", + "noIndicationProvided": "No indication provided", "noMatchingCodedCausesOfDeath": "No matching coded causes of death", "nonCodedCauseOfDeath": "Non-coded cause of death", "nonCodedCauseOfDeathRequired": "Please enter the non-coded cause of death", 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 3226682fb6..cd8ba88d57 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 @@ -60,115 +60,125 @@ export interface DrugOrderFormProps { promptBeforeClosing: (testFcn: () => boolean) => void; } -const createMedicationOrderFormSchema = (requireOutpatientQuantity: boolean, t: TFunction) => { - const comboSchema = { - default: z.boolean().optional(), - value: z.string(), - valueCoded: z.string(), - }; +function useCreateMedicationOrderFormSchema() { + const { t } = useTranslation(); + const { requireOutpatientQuantity } = useRequireOutpatientQuantity(); + const { requireIndication } = useConfig(); - const baseSchemaFields = { - freeTextDosage: z.string().refine((value) => !!value, { - message: t('freeDosageErrorMessage', 'Add free dosage note'), - }), - dosage: z.number({ - invalid_type_error: t('dosageRequiredErrorMessage', 'Dosage is required'), - }), - unit: z.object( - { ...comboSchema }, - { - invalid_type_error: t('selectUnitErrorMessage', 'Dose unit is required'), - }, - ), - route: z.object( - { ...comboSchema }, - { - invalid_type_error: t('selectRouteErrorMessage', 'Route is required'), - }, - ), - patientInstructions: z.string().nullable(), - asNeeded: z.boolean(), - asNeededCondition: z.string().nullable(), - duration: z.number().nullable(), - durationUnit: z.object({ ...comboSchema }).nullable(), - indication: z.string().refine((value) => value !== '', { - message: t('indicationErrorMessage', 'Indication is required'), - }), - startDate: z.date(), - frequency: z.object( - { ...comboSchema }, - { - invalid_type_error: t('selectFrequencyErrorMessage', 'Frequency is required'), - }, - ), - }; + const schema = useMemo(() => { + const comboSchema = { + default: z.boolean().optional(), + value: z.string(), + valueCoded: z.string(), + }; - const outpatientDrugOrderFields = { - pillsDispensed: z - .number() - .nullable() - .refine( - (value) => { - if (requireOutpatientQuantity && (typeof value !== 'number' || value < 1)) { - return false; - } - return true; - }, + const baseSchemaFields = { + freeTextDosage: z.string().refine((value) => !!value, { + message: t('freeDosageErrorMessage', 'Add free dosage note'), + }), + dosage: z.number({ + invalid_type_error: t('dosageRequiredErrorMessage', 'Dosage is required'), + }), + unit: z.object( + { ...comboSchema }, { - message: t('pillDispensedErrorMessage', 'Quantity to dispense is required'), + invalid_type_error: t('selectUnitErrorMessage', 'Dose unit is required'), }, ), - quantityUnits: z - .object(comboSchema) - .nullable() - .refine( - (value) => { - if (requireOutpatientQuantity && !value) { - return false; - } - return true; - }, + route: z.object( + { ...comboSchema }, { - message: t('selectQuantityUnitsErrorMessage', 'Quantity unit is required'), + invalid_type_error: t('selectRouteErrorMessage', 'Route is required'), }, ), - numRefills: z - .number() - .nullable() - .refine( - (value) => { - if (requireOutpatientQuantity && (typeof value !== 'number' || value < 0)) { - return false; - } - return true; - }, + patientInstructions: z.string().nullable(), + asNeeded: z.boolean(), + asNeededCondition: z.string().nullable(), + duration: z.number().nullable(), + durationUnit: z.object({ ...comboSchema }).nullable(), + indication: requireIndication + ? z.string().refine((value) => value !== '', { + message: t('indicationErrorMessage', 'Indication is required'), + }) + : z.string().nullish(), + startDate: z.date(), + frequency: z.object( + { ...comboSchema }, { - message: t('numRefillsErrorMessage', 'Number of refills is required'), + invalid_type_error: t('selectFrequencyErrorMessage', 'Frequency is required'), }, ), - }; - - const nonFreeTextDosageSchema = z.object({ - ...baseSchemaFields, - ...outpatientDrugOrderFields, - isFreeTextDosage: z.literal(false), - freeTextDosage: z.string().optional(), - }); + }; - const freeTextDosageSchema = z.object({ - ...baseSchemaFields, - ...outpatientDrugOrderFields, - isFreeTextDosage: z.literal(true), - dosage: z.number().nullable(), - unit: z.object(comboSchema).nullable(), - route: z.object(comboSchema).nullable(), - frequency: z.object(comboSchema).nullable(), - }); + const outpatientDrugOrderFields = { + pillsDispensed: z + .number() + .nullable() + .refine( + (value) => { + if (requireOutpatientQuantity && (typeof value !== 'number' || value < 1)) { + return false; + } + return true; + }, + { + message: t('pillDispensedErrorMessage', 'Quantity to dispense is required'), + }, + ), + quantityUnits: z + .object(comboSchema) + .nullable() + .refine( + (value) => { + if (requireOutpatientQuantity && !value) { + return false; + } + return true; + }, + { + message: t('selectQuantityUnitsErrorMessage', 'Quantity unit is required'), + }, + ), + numRefills: z + .number() + .nullable() + .refine( + (value) => { + if (requireOutpatientQuantity && (typeof value !== 'number' || value < 0)) { + return false; + } + return true; + }, + { + message: t('numRefillsErrorMessage', 'Number of refills is required'), + }, + ), + }; - return z.discriminatedUnion('isFreeTextDosage', [nonFreeTextDosageSchema, freeTextDosageSchema]); -}; + const nonFreeTextDosageSchema = z.object({ + ...baseSchemaFields, + ...outpatientDrugOrderFields, + isFreeTextDosage: z.literal(false), + freeTextDosage: z.string().optional(), + }); + + const freeTextDosageSchema = z.object({ + ...baseSchemaFields, + ...outpatientDrugOrderFields, + isFreeTextDosage: z.literal(true), + dosage: z.number().nullable(), + unit: z.object(comboSchema).nullable(), + route: z.object(comboSchema).nullable(), + frequency: z.object(comboSchema).nullable(), + }); + + return z.discriminatedUnion('isFreeTextDosage', [nonFreeTextDosageSchema, freeTextDosageSchema]); + }, [requireIndication, requireOutpatientQuantity, t]); + + return schema; +} -type MedicationOrderFormData = z.infer>; +type MedicationOrderFormData = z.infer>; function MedicationInfoHeader({ orderBasketItem, @@ -221,7 +231,6 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel, prompt const config = useConfig(); const isTablet = useLayoutType() === 'tablet'; const { orderConfigObject, error: errorFetchingOrderConfig } = useOrderConfig(); - const { requireOutpatientQuantity } = useRequireOutpatientQuantity(); const defaultStartDate = useMemo(() => { if (typeof initialOrderBasketItem?.startDate === 'string') parseDate(initialOrderBasketItem?.startDate); @@ -229,10 +238,7 @@ export function DrugOrderForm({ initialOrderBasketItem, onSave, onCancel, prompt return initialOrderBasketItem?.startDate as Date; }, [initialOrderBasketItem?.startDate]); - const medicationOrderFormSchema = useMemo( - () => createMedicationOrderFormSchema(requireOutpatientQuantity, t), - [requireOutpatientQuantity, t], - ); + const medicationOrderFormSchema = useCreateMedicationOrderFormSchema(); const { control, diff --git a/packages/esm-patient-medications-app/src/components/medications-details-table.component.tsx b/packages/esm-patient-medications-app/src/components/medications-details-table.component.tsx index b32a720cda..538d6a24fb 100644 --- a/packages/esm-patient-medications-app/src/components/medications-details-table.component.tsx +++ b/packages/esm-patient-medications-app/src/components/medications-details-table.component.tsx @@ -138,12 +138,10 @@ const MedicationsDetailsTable: React.FC = ({

- {medication.orderReasonNonCoded && ( - - {t('indication', 'Indication').toUpperCase()}{' '} - {medication.orderReasonNonCoded} - - )} + + {t('indication', 'Indication').toUpperCase()}{' '} + {medication.orderReasonNonCoded ?? t('noIndicationProvided', 'No indication provided')} + {medication.quantity && ( — {t('quantity', 'Quantity').toUpperCase()}{' '} diff --git a/packages/esm-patient-medications-app/src/config-schema.ts b/packages/esm-patient-medications-app/src/config-schema.ts index 88aeaf153f..1e6ca69d83 100644 --- a/packages/esm-patient-medications-app/src/config-schema.ts +++ b/packages/esm-patient-medications-app/src/config-schema.ts @@ -36,6 +36,11 @@ export const configSchema = { 'Number of milliseconds to delay the search operation in the drug search input by after the user starts typing. The useDebounce hook delays the search by 300ms by default', _default: 300, }, + requireIndication: { + _type: Type.Boolean, + _description: 'Whether to require an indication when placing a medication order', + _default: true, + }, }; export interface ConfigObject { @@ -47,4 +52,5 @@ export interface ConfigObject { showPrintButton: boolean; maxDispenseDurationInDays: number; debounceDelayInMs: number; + requireIndication: boolean; } diff --git a/packages/esm-patient-medications-app/src/drug-order-basket-panel/order-basket-item-tile.component.tsx b/packages/esm-patient-medications-app/src/drug-order-basket-panel/order-basket-item-tile.component.tsx index ef392f3aa1..f7fa3888f0 100644 --- a/packages/esm-patient-medications-app/src/drug-order-basket-panel/order-basket-item-tile.component.tsx +++ b/packages/esm-patient-medications-app/src/drug-order-basket-panel/order-basket-item-tile.component.tsx @@ -71,7 +71,11 @@ export default function OrderBasketItemTile({ orderBasketItem, onItemClick, onRe {t('indication', 'Indication').toUpperCase()}{' '} - {!!orderBasketItem.indication ? orderBasketItem.indication : {t('none', 'None')}} + {!!orderBasketItem.indication ? ( + orderBasketItem.indication + ) : ( + {t('noIndicationProvided', 'No indication provided')} + )} {!!orderBasketItem.orderError && ( <> diff --git a/packages/esm-patient-medications-app/translations/en.json b/packages/esm-patient-medications-app/translations/en.json index 5eb48ed2c4..0ec494a17b 100644 --- a/packages/esm-patient-medications-app/translations/en.json +++ b/packages/esm-patient-medications-app/translations/en.json @@ -45,7 +45,7 @@ "medicationIndefiniteDuration": "Indefinite duration", "Medications": "Medications", "modify": "Modify", - "none": "None", + "noIndicationProvided": "No indication provided", "noResultsForDrugSearch": "No results to display for \"{{searchTerm}}\"", "numRefillsErrorMessage": "Number of refills is required", "onDate": "on", diff --git a/packages/esm-patient-orders-app/src/components/medication-record.component.tsx b/packages/esm-patient-orders-app/src/components/medication-record.component.tsx index 958a80aaad..4fa251af19 100644 --- a/packages/esm-patient-orders-app/src/components/medication-record.component.tsx +++ b/packages/esm-patient-orders-app/src/components/medication-record.component.tsx @@ -46,12 +46,10 @@ const MedicationRecord: React.FC = ({ medication }) => { {medication.dosingInstructions && — {medication.dosingInstructions.toLocaleLowerCase()}}

- {medication.orderReasonNonCoded ? ( - - {t('indication', 'Indication').toUpperCase()}{' '} - {medication.orderReasonNonCoded} - - ) : null} + + {t('indication', 'Indication').toUpperCase()}{' '} + {medication.orderReasonNonCoded ?? t('noIndicationProvided', 'No indication provided')} + {medication.quantity ? ( — {t('quantity', 'Quantity').toUpperCase()}{' '} diff --git a/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx b/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx index 54ed9d2f53..54385acd21 100644 --- a/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx +++ b/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx @@ -207,8 +207,10 @@ const OrderDetailsTable: React.FC = ({ patientUuid, showAddBu dosage: order.type === 'drugorder' ? (

{`${t('indication', 'Indication').toUpperCase()} - ${order.orderReasonNonCoded} ${'-'} ${t('quantity', 'Quantity').toUpperCase()} ${order.quantity} ${order - ?.quantityUnits?.display} `}
+ ${order.orderReasonNonCoded ?? t('noIndicationProvided', 'No indication provided')} ${'-'} ${t( + 'quantity', + 'Quantity', + ).toUpperCase()} ${order.quantity} ${order?.quantityUnits?.display} `} ) : ( '--' ), diff --git a/packages/esm-patient-orders-app/translations/en.json b/packages/esm-patient-orders-app/translations/en.json index 3c560217a6..313cc7ecf2 100644 --- a/packages/esm-patient-orders-app/translations/en.json +++ b/packages/esm-patient-orders-app/translations/en.json @@ -43,6 +43,7 @@ "medicationIndefiniteDuration": "Indefinite duration", "medications": "Medications", "modifyOrder": "Modify order", + "noIndicationProvided": "No indication provided", "noMatchingOrdersToDisplay": "No matching orders to display", "noResultsForTestTypeSearch": "No results to display for \"{{searchTerm}}\"", "normalRange": "Normal range",