diff --git a/packages/esm-patient-common-lib/src/orders/types.ts b/packages/esm-patient-common-lib/src/orders/types.ts index 6e1d603baa..2532264834 100644 --- a/packages/esm-patient-common-lib/src/orders/types.ts +++ b/packages/esm-patient-common-lib/src/orders/types.ts @@ -43,6 +43,7 @@ export interface OrderPost { previousOrder?: string; asNeededCondition?: string; orderReasonNonCoded?: string; + orderReason?: string; instructions?: string; } diff --git a/packages/esm-patient-labs-app/src/config-schema.ts b/packages/esm-patient-labs-app/src/config-schema.ts index 2eb53b0c9b..6313eee878 100644 --- a/packages/esm-patient-labs-app/src/config-schema.ts +++ b/packages/esm-patient-labs-app/src/config-schema.ts @@ -41,16 +41,46 @@ export const configSchema = { _default: '52a447d3-a64a-11e3-9aeb-50e549534c5e', }, }, + labTestsWithOrderReasons: { + _type: Type.Array, + _elements: { + labTestUuid: { + _type: Type.UUID, + _description: 'UUID of the lab test that requires a reason for ordering', + _default: '', + }, + orderReasons: { + _type: Type.Array, + _elements: { + _type: Type.ConceptUuid, + _description: 'Array of coded concepts that represent reasons for ordering a lab test', + }, + _default: [], + _description: 'Coded Lab test order reason options', + }, + }, + _default: [], + _description: 'Whether to allow for provision of coded order reason', + }, }; export interface ObsTreeEntry { conceptUuid: string; defaultOpen: boolean; } +export interface LabTestReason { + uuid: string; + label?: string; +} +export interface OrderReason { + labTestUuid: string; + orderReasons: Array; +} export interface ConfigObject { concepts: Array; showPrintButton: boolean; orders: { labOrderTypeUuid: string; }; + labTestsWithOrderReasons: Array; } diff --git a/packages/esm-patient-labs-app/src/lab-orders/add-lab-order/lab-order-form.component.tsx b/packages/esm-patient-labs-app/src/lab-orders/add-lab-order/lab-order-form.component.tsx index 2b96b18820..9ba866cbaa 100644 --- a/packages/esm-patient-labs-app/src/lab-orders/add-lab-order/lab-order-form.component.tsx +++ b/packages/esm-patient-labs-app/src/lab-orders/add-lab-order/lab-order-form.component.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useEffect, useState } from 'react'; import classNames from 'classnames'; import { launchPatientWorkspace, promptBeforeClosing, useOrderBasket } from '@openmrs/esm-patient-common-lib'; -import { translateFrom, useLayoutType, useSession } from '@openmrs/esm-framework'; -import { careSettingUuid, type LabOrderBasketItem, prepLabOrderPostData } from '../api'; +import { translateFrom, useLayoutType, useSession, useConfig } from '@openmrs/esm-framework'; +import { careSettingUuid, type LabOrderBasketItem, prepLabOrderPostData, useOrderReasons } from '../api'; import { Button, ButtonSet, @@ -22,6 +22,7 @@ import { Controller, type FieldErrors, useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { moduleName } from '@openmrs/esm-patient-chart-app/src/constants'; +import { type ConfigObject } from '../../config-schema'; import styles from './lab-order-form.scss'; export interface LabOrderFormProps { @@ -44,6 +45,7 @@ const labOrderFormSchema = z.object({ invalid_type_error: translateFrom(moduleName, 'addLabOrderLabReferenceRequired', 'Test type is required'), }, ), + orderReason: z.string().optional(), }); // Designs: @@ -70,6 +72,11 @@ export function LabOrderForm({ initialOrder, closeWorkspace }: LabOrderFormProps ...initialOrder, }, }); + const config = useConfig(); + const orderReasonUuids = + (config.labTestsWithOrderReasons?.find((c) => c.labTestUuid === defaultValues?.testType?.conceptUuid) || {}) + .orderReasons || []; + const { orderReasons } = useOrderReasons(orderReasonUuids); const handleFormSubmission = useCallback( (data: LabOrderBasketItem) => { @@ -187,6 +194,30 @@ export function LabOrderForm({ initialOrder, closeWorkspace }: LabOrderFormProps + {orderReasons.length > 0 && ( + + + + ( + item?.display} + items={orderReasons} + onBlur={onBlur} + onChange={({ selectedItem }) => onChange(selectedItem?.uuid || '')} + /> + )} + /> + + + + )} diff --git a/packages/esm-patient-labs-app/src/lab-orders/api.ts b/packages/esm-patient-labs-app/src/lab-orders/api.ts index 984d44d171..c6df3a6401 100644 --- a/packages/esm-patient-labs-app/src/lab-orders/api.ts +++ b/packages/esm-patient-labs-app/src/lab-orders/api.ts @@ -1,8 +1,9 @@ import useSWR, { mutate } from 'swr'; -import { type FetchResponse, openmrsFetch, useConfig } from '@openmrs/esm-framework'; +import { type FetchResponse, openmrsFetch, useConfig, showSnackbar } from '@openmrs/esm-framework'; import { type ConfigObject } from '../config-schema'; import { useCallback, useMemo } from 'react'; import { type OrderBasketItem, type OrderPost, type PatientOrderFetchResponse } from '@openmrs/esm-patient-common-lib'; +import useSWRImmutable from 'swr/immutable'; export const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0'; /** @@ -42,6 +43,32 @@ export function usePatientLabOrders(patientUuid: string, status: 'ACTIVE' | 'any }; } +export function useOrderReasons(conceptUuids: Array) { + const shouldFetch = conceptUuids && conceptUuids.length > 0; + const url = shouldFetch ? getConceptReferenceUrls(conceptUuids) : null; + const { data, error, isLoading } = useSWRImmutable, Error>( + shouldFetch ? `/ws/rest/v1/${url[0]}` : null, + openmrsFetch, + ); + + const ob = data?.data; + const orderReasons = ob + ? Object.entries(ob).map(([key, value]) => ({ + uuid: value.uuid, + display: value.display, + })) + : []; + + if (error) { + showSnackbar({ + title: error.name, + subtitle: error.message, + kind: 'error', + }); + } + + return { orderReasons: orderReasons, isLoading }; +} export interface LabOrderBasketItem extends OrderBasketItem { testType?: { label: string; @@ -50,6 +77,7 @@ export interface LabOrderBasketItem extends OrderBasketItem { labReferenceNumber?: string; urgency?: string; instructions?: string; + orderReason?: string; } export function prepLabOrderPostData(order: LabOrderBasketItem, patientUuid: string, encounterUuid: string): OrderPost { @@ -62,11 +90,36 @@ export function prepLabOrderPostData(order: LabOrderBasketItem, patientUuid: str encounter: encounterUuid, concept: order.testType.conceptUuid, instructions: order.instructions, + orderReason: order.orderReason, }; } +const chunkSize = 10; +export function getConceptReferenceUrls(conceptUuids: Array) { + const accumulator = []; + for (let i = 0; i < conceptUuids.length; i += chunkSize) { + accumulator.push(conceptUuids.slice(i, i + chunkSize)); + } + + return accumulator.map((partition) => `conceptreferences?references=${partition.join(',')}&v=custom:(uuid,display)`); +} export type PostDataPrepLabOrderFunction = ( order: LabOrderBasketItem, patientUuid: string, encounterUuid: string, ) => OrderPost; + +export interface ConceptAnswers { + display: string; + uuid: string; +} +export interface ConceptResponse { + uuid: string; + display: string; + datatype: { + uuid: string; + display: string; + }; + answers: Array; + setMembers: Array; +} diff --git a/packages/esm-patient-labs-app/translations/en.json b/packages/esm-patient-labs-app/translations/en.json index 9c5441e7ea..21eab6b7dc 100644 --- a/packages/esm-patient-labs-app/translations/en.json +++ b/packages/esm-patient-labs-app/translations/en.json @@ -24,6 +24,7 @@ "onDate": "on", "orderActionNew": "New", "ordered": "Ordered", + "orderReason": "Order reason", "other": "Other", "panel": "Panel", "pleaseRequiredFields": "Please fill all required fields",