@openmrs/esm-laboratory-app
Version:
An O3 frontend module for managing laboratory requests and queues
109 lines (98 loc) • 4.43 kB
text/typescript
import { useCallback } from 'react';
import dayjs from 'dayjs';
import useSWR, { mutate } from 'swr';
import { openmrsFetch, type Order, restBaseUrl, useAppContext, useConfig } from '@openmrs/esm-framework';
import type { DateFilterContext, FulfillerStatus } from './types';
import { type Config } from './config-schema';
const useLabOrdersDefaultParams: UseLabOrdersParams = {
status: null,
newOrdersOnly: false,
excludeCanceled: true,
includePatientId: false,
};
interface UseLabOrdersParams {
status: FulfillerStatus;
newOrdersOnly: boolean;
excludeCanceled: boolean;
includePatientId: boolean;
}
/**
* Custom hook for retrieving laboratory orders.
*
* @param status - The fulfiller status to filter by (e.g. 'IN_PROGRESS', 'COMPLETED')
* @param newOrdersOnly - Whether to return only new orders that haven't been picked (action=NEW, fulfillerStatus=null). Filtered client-side as the REST API doesn't support this query natively.
* @param excludeCanceled - Whether to exclude canceled, discontinued and expired orders
* @param includePatientId - Whether to include patient identifiers in the response
*/
export function useLabOrders(params: Partial<UseLabOrdersParams> = useLabOrdersDefaultParams) {
const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined));
const { status, newOrdersOnly, excludeCanceled, includePatientId } = {
...useLabOrdersDefaultParams,
...definedParams,
};
const { dateRange } = useAppContext<DateFilterContext>('laboratory-date-filter') ?? {
dateRange: [dayjs().startOf('day').toDate(), new Date()],
};
const { laboratoryOrderTypeUuid } = useConfig();
const customRepresentation = `custom:(uuid,orderNumber,patient:(uuid,display,person:(uuid,display,age,birthdate,gender)${
includePatientId ? ',identifiers' : ''
}),concept:(uuid,display),action,careSetting:(uuid,display,description,careSettingType,display),previousOrder,dateActivated,scheduledDate,dateStopped,autoExpireDate,encounter:(uuid,display),orderer:(uuid,display),orderReason,orderReasonNonCoded,orderType:(uuid,display,name,description,conceptClasses,parent),urgency,instructions,commentToFulfiller,display,fulfillerStatus,fulfillerComment,accessionNumber,specimenSource,laterality,clinicalHistory,frequency,numberOfRepeats)`;
let url = `${restBaseUrl}/order?orderTypes=${laboratoryOrderTypeUuid}&v=${customRepresentation}`;
url = status ? url + `&fulfillerStatus=${status}` : url;
url = excludeCanceled ? `${url}&excludeCanceledAndExpired=true&excludeDiscontinueOrders=true` : url;
url = dateRange
? `${url}&activatedOnOrAfterDate=${dateRange.at(0).toISOString()}&activatedOnOrBeforeDate=${dateRange
.at(1)
.toISOString()}`
: url;
const { data, error, mutate, isLoading, isValidating } = useSWR<{
data: { results: Array<Order> };
}>(`${url}`, openmrsFetch);
const filteredOrders = data?.data?.results?.filter(
(order) => !newOrdersOnly || (order?.action === 'NEW' && order?.fulfillerStatus === null),
);
return {
labOrders: filteredOrders ?? [],
isLoading,
isError: error,
mutate,
isValidating,
};
}
export function setFulfillerStatus(orderId: string, status: FulfillerStatus, abortController: AbortController) {
return openmrsFetch(`${restBaseUrl}/order/${orderId}/fulfillerdetails/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
signal: abortController.signal,
body: { fulfillerStatus: status },
});
}
export function rejectLabOrder(orderId: string, comment: string, abortController: AbortController) {
return openmrsFetch(`${restBaseUrl}/order/${orderId}/fulfillerdetails/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
signal: abortController.signal,
body: {
fulfillerStatus: 'DECLINED',
fulfillerComment: comment,
},
});
}
/**
* Custom hook that returns a function to invalidate and refetch all lab orders.
* Use this after mutations to ensure the UI reflects the latest data.
*/
export function useInvalidateLabOrders() {
const { laboratoryOrderTypeUuid } = useConfig<Config>();
return useCallback(() => {
mutate(
(key) => typeof key === 'string' && key.startsWith(`${restBaseUrl}/order?orderTypes=${laboratoryOrderTypeUuid}`),
undefined,
{ revalidate: true },
);
}, [laboratoryOrderTypeUuid]);
}