@openmrs/esm-patient-common-lib 11.3.1-patch.9508 → 11.3.1-pre.10001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/empty-state/empty-state.test.tsx +4 -4
- package/src/form-entry/form-entry.ts +23 -2
- package/src/index.ts +1 -1
- package/src/launchStartVisitPrompt.tsx +2 -1
- package/src/orders/index.ts +4 -2
- package/src/orders/postOrders.ts +64 -51
- package/src/orders/showOrderSuccessToast.ts +86 -0
- package/src/orders/types/order.ts +128 -23
- package/src/orders/useMutatePatientOrders.ts +28 -0
- package/src/orders/useOrderTypes.ts +2 -2
- package/src/orders/useOrderableConceptSets.ts +1 -1
- package/src/orders/useOrders.ts +28 -7
- package/src/results/helpers.test.ts +123 -0
- package/src/results/helpers.ts +63 -0
- package/src/results/index.ts +3 -0
- package/src/results/reference-range-display.component.tsx +25 -0
- package/src/results/results.scss +11 -0
- package/src/results/useReferenceRanges.ts +72 -0
- package/src/types/test-results.ts +11 -0
- package/src/visit/revalidation-utils.test.ts +27 -0
- package/src/visit/revalidation-utils.ts +18 -0
- package/src/visit/visit-mutations.ts +2 -2
- package/src/workspaces.ts +50 -24
- package/src/form-entry-interop.ts +0 -152
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-patient-common-lib",
|
|
3
|
-
"version": "11.3.1-
|
|
3
|
+
"version": "11.3.1-pre.10001",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Library for common patient chart components",
|
|
6
6
|
"browser": "dist/openmrs-esm-patient-common-lib.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@carbon/react": "^1.83.0",
|
|
33
|
-
"lodash-es": "^4.17.
|
|
33
|
+
"lodash-es": "^4.17.23",
|
|
34
34
|
"uuid": "^8.3.2"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import {
|
|
4
|
+
import { launchWorkspace2 } from '@openmrs/esm-framework';
|
|
5
5
|
import { EmptyState } from '.';
|
|
6
6
|
|
|
7
|
-
const mockLaunchWorkspace = jest.mocked(
|
|
7
|
+
const mockLaunchWorkspace = jest.mocked(launchWorkspace2);
|
|
8
8
|
|
|
9
9
|
describe('EmptyState', () => {
|
|
10
10
|
it('renders an empty state widget card', () => {
|
|
@@ -12,7 +12,7 @@ describe('EmptyState', () => {
|
|
|
12
12
|
<EmptyState
|
|
13
13
|
headerTitle="appointments"
|
|
14
14
|
displayText="appointments"
|
|
15
|
-
launchForm={() =>
|
|
15
|
+
launchForm={() => launchWorkspace2('sample-form-workspace')}
|
|
16
16
|
/>,
|
|
17
17
|
);
|
|
18
18
|
|
|
@@ -28,7 +28,7 @@ describe('EmptyState', () => {
|
|
|
28
28
|
<EmptyState
|
|
29
29
|
headerTitle="appointments"
|
|
30
30
|
displayText="appointments"
|
|
31
|
-
launchForm={() =>
|
|
31
|
+
launchForm={() => launchWorkspace2('sample-form-workspace')}
|
|
32
32
|
/>,
|
|
33
33
|
);
|
|
34
34
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Encounter, type Visit, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
3
|
export interface FormEntryProps {
|
|
4
4
|
encounterUuid?: string;
|
|
@@ -7,6 +7,27 @@ export interface FormEntryProps {
|
|
|
7
7
|
visitTypeUuid?: string;
|
|
8
8
|
visitStartDatetime?: string;
|
|
9
9
|
visitStopDatetime?: string;
|
|
10
|
-
htmlForm?: HtmlFormEntryForm;
|
|
11
10
|
additionalProps?: Record<string, any>;
|
|
12
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Workspace control props are made optional to support usage in non-workspace contexts,
|
|
15
|
+
* such as the Fast Data Entry app or other standalone form zones.
|
|
16
|
+
*/
|
|
17
|
+
export interface FormRendererProps {
|
|
18
|
+
additionalProps?: Record<string, any>;
|
|
19
|
+
encounterUuid?: string;
|
|
20
|
+
formUuid: string;
|
|
21
|
+
patientUuid: string;
|
|
22
|
+
patient: fhir.Patient;
|
|
23
|
+
visit?: Visit;
|
|
24
|
+
visitUuid?: string;
|
|
25
|
+
hideControls?: boolean;
|
|
26
|
+
hidePatientBanner?: boolean;
|
|
27
|
+
handlePostResponse?: (encounter: Encounter) => void;
|
|
28
|
+
preFilledQuestions?: Record<string, string>;
|
|
29
|
+
launchChildWorkspace?: Workspace2DefinitionProps['launchChildWorkspace'];
|
|
30
|
+
closeWorkspace?: Workspace2DefinitionProps['closeWorkspace'];
|
|
31
|
+
closeWorkspaceWithSavedChanges?: () => void;
|
|
32
|
+
setHasUnsavedChanges?(hasUnsavedChanges: boolean);
|
|
33
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,8 @@ export * from './compare';
|
|
|
3
3
|
export * from './dashboards/createDashboardLink';
|
|
4
4
|
export * from './empty-state';
|
|
5
5
|
export * from './error-state';
|
|
6
|
-
export * from './form-entry-interop';
|
|
7
6
|
export * from './form-entry/form-entry';
|
|
7
|
+
export * from './results';
|
|
8
8
|
export * from './launchStartVisitPrompt';
|
|
9
9
|
export * from './offline/visit';
|
|
10
10
|
export * from './orders';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { showModal } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
|
-
export function launchStartVisitPrompt() {
|
|
3
|
+
export async function launchStartVisitPrompt(onVisitStarted?: () => void) {
|
|
4
4
|
const dispose = showModal('start-visit-dialog', {
|
|
5
5
|
closeModal: () => dispose(),
|
|
6
|
+
onVisitStarted,
|
|
6
7
|
});
|
|
7
8
|
}
|
package/src/orders/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export * from './useOrderBasket';
|
|
2
1
|
export * from './postOrders';
|
|
3
|
-
export * from './
|
|
2
|
+
export * from './showOrderSuccessToast';
|
|
4
3
|
export * from './types';
|
|
4
|
+
export * from './useMutatePatientOrders';
|
|
5
|
+
export * from './useOrderBasket';
|
|
5
6
|
export * from './useOrderableConceptSets';
|
|
7
|
+
export * from './useOrders';
|
|
6
8
|
export * from './useOrderType';
|
|
7
9
|
export * from './useOrderTypes';
|
package/src/orders/postOrders.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Concept,
|
|
3
|
+
type Encounter,
|
|
3
4
|
openmrsFetch,
|
|
4
5
|
type OpenmrsResource,
|
|
5
6
|
parseDate,
|
|
@@ -7,20 +8,28 @@ import {
|
|
|
7
8
|
type Visit,
|
|
8
9
|
} from '@openmrs/esm-framework';
|
|
9
10
|
import { type OrderBasketStore, orderBasketStore } from './store';
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
OrderPost
|
|
17
|
-
|
|
11
|
+
import type { ExtractedOrderErrorObject, Order, OrderBasketItem, OrderErrorObject, OrderPost } from './types';
|
|
12
|
+
|
|
13
|
+
function getOrdersPayloadFromOrderBasket(patientUuid: string, ordererUuid: string) {
|
|
14
|
+
const { items, postDataPrepFunctions }: OrderBasketStore = orderBasketStore.getState();
|
|
15
|
+
const patientItems = items[patientUuid];
|
|
16
|
+
|
|
17
|
+
const orders: Array<OrderPost> = [];
|
|
18
|
+
Object.entries(patientItems).forEach(([grouping, groupOrders]) => {
|
|
19
|
+
groupOrders.forEach((order) => {
|
|
20
|
+
orders.push(postDataPrepFunctions[grouping](order, patientUuid, null, ordererUuid));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return orders;
|
|
25
|
+
}
|
|
18
26
|
|
|
19
27
|
export async function postOrdersOnNewEncounter(
|
|
20
28
|
patientUuid: string,
|
|
21
29
|
orderEncounterType: string,
|
|
22
30
|
currentVisit: Visit | null,
|
|
23
|
-
|
|
31
|
+
orderLocationUuid: string,
|
|
32
|
+
ordererUuid: string,
|
|
24
33
|
abortController?: AbortController,
|
|
25
34
|
|
|
26
35
|
/**
|
|
@@ -29,36 +38,23 @@ export async function postOrdersOnNewEncounter(
|
|
|
29
38
|
*/
|
|
30
39
|
encounterDate?: Date,
|
|
31
40
|
) {
|
|
32
|
-
const now = new Date();
|
|
33
|
-
const visitStartDate = parseDate(currentVisit?.startDatetime);
|
|
34
|
-
const visitEndDate = parseDate(currentVisit?.stopDatetime);
|
|
35
41
|
if (!encounterDate) {
|
|
36
|
-
if (
|
|
37
|
-
encounterDate =
|
|
42
|
+
if (currentVisit?.stopDatetime) {
|
|
43
|
+
encounterDate = parseDate(currentVisit.startDatetime);
|
|
38
44
|
} else {
|
|
39
|
-
|
|
40
|
-
'postOrdersOnNewEncounter received an active visit that is not currently active. This is a programming error. Attempting to place the order using the visit start date.',
|
|
41
|
-
);
|
|
42
|
-
encounterDate = visitStartDate;
|
|
45
|
+
encounterDate = null;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
const
|
|
47
|
-
const patientItems = items[patientUuid];
|
|
48
|
-
|
|
49
|
-
const orders: Array<DrugOrderPost | TestOrderPost> = [];
|
|
50
|
-
|
|
51
|
-
Object.entries(patientItems).forEach(([grouping, groupOrders]) => {
|
|
52
|
-
groupOrders.forEach((order) => {
|
|
53
|
-
orders.push(postDataPrepFunctions[grouping](order, patientUuid, null));
|
|
54
|
-
});
|
|
55
|
-
});
|
|
49
|
+
const orders = getOrdersPayloadFromOrderBasket(patientUuid, ordererUuid);
|
|
56
50
|
|
|
57
51
|
const encounterPostData: EncounterPost = {
|
|
58
52
|
patient: patientUuid,
|
|
59
|
-
location:
|
|
53
|
+
location: orderLocationUuid,
|
|
60
54
|
encounterType: orderEncounterType,
|
|
61
|
-
encounterDatetime
|
|
55
|
+
// only specify the encounterDatetime if it's given, otherwise
|
|
56
|
+
// don't specify that let the server default it to `now`
|
|
57
|
+
...(encounterDate ? { encounterDatetime: encounterDate } : {}),
|
|
62
58
|
visit: currentVisit?.uuid,
|
|
63
59
|
obs: [],
|
|
64
60
|
orders,
|
|
@@ -75,53 +71,70 @@ export interface EncounterPost {
|
|
|
75
71
|
patient: string;
|
|
76
72
|
location: string;
|
|
77
73
|
encounterType: string;
|
|
78
|
-
encounterDatetime
|
|
74
|
+
encounterDatetime?: Date;
|
|
79
75
|
visit?: string;
|
|
80
76
|
obs: ObsPayload[];
|
|
81
77
|
orders: OrderPost[];
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
export async function postEncounter(encounterPostData: EncounterPost, abortController?: AbortController) {
|
|
85
|
-
return openmrsFetch<
|
|
81
|
+
return openmrsFetch<Encounter>(`${restBaseUrl}/encounter`, {
|
|
86
82
|
headers: {
|
|
87
83
|
'Content-Type': 'application/json',
|
|
88
84
|
},
|
|
89
85
|
method: 'POST',
|
|
90
86
|
body: encounterPostData,
|
|
91
87
|
signal: abortController?.signal,
|
|
92
|
-
}).then((res) => res?.data
|
|
88
|
+
}).then((res) => res?.data);
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
export async function postOrders(
|
|
91
|
+
export async function postOrders(
|
|
92
|
+
patientUuid: string,
|
|
93
|
+
encounterUuid: string,
|
|
94
|
+
abortController: AbortController,
|
|
95
|
+
ordererUuid: string,
|
|
96
|
+
) {
|
|
96
97
|
const { items, postDataPrepFunctions }: OrderBasketStore = orderBasketStore.getState();
|
|
97
98
|
const patientItems = items[patientUuid];
|
|
98
99
|
|
|
99
100
|
const erroredItems: Array<OrderBasketItem> = [];
|
|
100
|
-
|
|
101
|
+
const postedOrders: Array<Order> = [];
|
|
102
|
+
const promises: Array<Promise<void>> = [];
|
|
103
|
+
|
|
104
|
+
for (const grouping in patientItems) {
|
|
101
105
|
const orders = patientItems[grouping];
|
|
106
|
+
const dataPrepFn = postDataPrepFunctions[grouping];
|
|
107
|
+
|
|
108
|
+
if (typeof dataPrepFn !== 'function') {
|
|
109
|
+
console.warn(`The postDataPrep function registered for ${grouping} orders is not a function`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
for (let i = 0; i < orders.length; i++) {
|
|
103
114
|
const order = orders[i];
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
extractedOrderError: extractErrorDetails(error),
|
|
115
|
+
|
|
116
|
+
const promise = postOrder(dataPrepFn(order, patientUuid, encounterUuid, ordererUuid), abortController)
|
|
117
|
+
.then((response) => {
|
|
118
|
+
postedOrders.push(response.data);
|
|
119
|
+
})
|
|
120
|
+
.catch((error) => {
|
|
121
|
+
erroredItems.push({
|
|
122
|
+
...order,
|
|
123
|
+
orderError: error,
|
|
124
|
+
extractedOrderError: extractErrorDetails(error),
|
|
125
|
+
});
|
|
116
126
|
});
|
|
117
|
-
|
|
127
|
+
|
|
128
|
+
promises.push(promise);
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
|
-
|
|
131
|
+
await Promise.allSettled(promises);
|
|
132
|
+
|
|
133
|
+
return { postedOrders, erroredItems };
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
function postOrder(body: OrderPost, abortController?: AbortController) {
|
|
124
|
-
return openmrsFetch(`${restBaseUrl}/order`, {
|
|
136
|
+
export function postOrder(body: OrderPost, abortController?: AbortController) {
|
|
137
|
+
return openmrsFetch<Order>(`${restBaseUrl}/order`, {
|
|
125
138
|
method: 'POST',
|
|
126
139
|
signal: abortController?.signal,
|
|
127
140
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { showSnackbar, translateFrom } from '@openmrs/esm-framework';
|
|
2
|
+
import { type OrderBasketItem } from './types';
|
|
3
|
+
|
|
4
|
+
type OrderAction = 'placed' | 'updated' | 'discontinued';
|
|
5
|
+
|
|
6
|
+
function getNotificationTitle(
|
|
7
|
+
moduleName: string,
|
|
8
|
+
placedOrders: OrderBasketItem[],
|
|
9
|
+
updatedOrders: OrderBasketItem[],
|
|
10
|
+
discontinuedOrders: OrderBasketItem[],
|
|
11
|
+
activeActions: OrderAction[],
|
|
12
|
+
): string {
|
|
13
|
+
if (activeActions.length > 1) {
|
|
14
|
+
return translateFrom(moduleName, 'ordersCompleted', 'Orders completed');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const action = activeActions[0];
|
|
18
|
+
|
|
19
|
+
if (action === 'placed') {
|
|
20
|
+
return placedOrders.length === 1
|
|
21
|
+
? translateFrom(moduleName, 'orderPlaced', 'Order placed')
|
|
22
|
+
: translateFrom(moduleName, 'ordersPlaced', 'Orders placed');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (action === 'updated') {
|
|
26
|
+
return updatedOrders.length === 1
|
|
27
|
+
? translateFrom(moduleName, 'orderUpdated', 'Order updated')
|
|
28
|
+
: translateFrom(moduleName, 'ordersUpdated', 'Orders updated');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// action === 'discontinued'
|
|
32
|
+
return discontinuedOrders.length === 1
|
|
33
|
+
? translateFrom(moduleName, 'orderDiscontinued', 'Order discontinued')
|
|
34
|
+
: translateFrom(moduleName, 'ordersDiscontinued', 'Orders discontinued');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Shows a success toast notification for order operations.
|
|
39
|
+
* The notification title dynamically reflects the action(s) taken (placed, updated, discontinued)
|
|
40
|
+
* and whether it's singular or plural.
|
|
41
|
+
*
|
|
42
|
+
* @param moduleName - The module name (e.g., '@openmrs/esm-patient-orders-app') to use for translations
|
|
43
|
+
* @param patientOrderItems - Array of order basket items that were processed
|
|
44
|
+
*/
|
|
45
|
+
export function showOrderSuccessToast(moduleName: string, patientOrderItems: OrderBasketItem[]) {
|
|
46
|
+
if (patientOrderItems.length === 0) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const placedOrders = patientOrderItems.filter((item) => ['NEW', 'RENEW'].includes(item.action));
|
|
51
|
+
const updatedOrders = patientOrderItems.filter((item) => item.action === 'REVISE');
|
|
52
|
+
const discontinuedOrders = patientOrderItems.filter((item) => item.action === 'DISCONTINUE');
|
|
53
|
+
|
|
54
|
+
const orderedString = placedOrders.map((item) => item.display).join(', ');
|
|
55
|
+
const updatedString = updatedOrders.map((item) => item.display).join(', ');
|
|
56
|
+
const discontinuedString = discontinuedOrders.map((item) => item.display).join(', ');
|
|
57
|
+
|
|
58
|
+
const activeActions: OrderAction[] = [
|
|
59
|
+
orderedString && 'placed',
|
|
60
|
+
updatedString && 'updated',
|
|
61
|
+
discontinuedString && 'discontinued',
|
|
62
|
+
].filter(Boolean) as OrderAction[];
|
|
63
|
+
|
|
64
|
+
const title = getNotificationTitle(moduleName, placedOrders, updatedOrders, discontinuedOrders, activeActions);
|
|
65
|
+
|
|
66
|
+
const subtitleParts: string[] = [];
|
|
67
|
+
|
|
68
|
+
if (orderedString) {
|
|
69
|
+
subtitleParts.push(`${translateFrom(moduleName, 'orderedFor', 'Placed order for')} ${orderedString}.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (updatedString) {
|
|
73
|
+
subtitleParts.push(`${translateFrom(moduleName, 'updated', 'Updated')} ${updatedString}.`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (discontinuedString) {
|
|
77
|
+
subtitleParts.push(`${translateFrom(moduleName, 'discontinued', 'Discontinued')} ${discontinuedString}.`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
showSnackbar({
|
|
81
|
+
isLowContrast: true,
|
|
82
|
+
kind: 'success',
|
|
83
|
+
title,
|
|
84
|
+
subtitle: subtitleParts.join(' '),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenmrsResource } from '@openmrs/esm-framework';
|
|
1
|
+
import type { Encounter, OpenmrsResource, Visit, Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
3
|
export interface Concept extends OpenmrsResource {
|
|
4
4
|
name?: {
|
|
@@ -47,8 +47,6 @@ export interface OrderBasketItem {
|
|
|
47
47
|
action: OrderAction;
|
|
48
48
|
display: string;
|
|
49
49
|
uuid?: string;
|
|
50
|
-
orderer?: string;
|
|
51
|
-
careSetting?: string;
|
|
52
50
|
orderError?: Error & {
|
|
53
51
|
responseBody?: {
|
|
54
52
|
error?: {
|
|
@@ -71,6 +69,8 @@ export interface OrderBasketItem {
|
|
|
71
69
|
orderType?: string;
|
|
72
70
|
orderNumber?: string;
|
|
73
71
|
scheduledDate?: Date;
|
|
72
|
+
encounterUuid?: string;
|
|
73
|
+
visit: Visit;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export type OrderUrgency = 'ROUTINE' | 'STAT' | 'ON_SCHEDULED_DATE';
|
|
@@ -116,7 +116,7 @@ export interface DrugOrderPost extends OrderPost {
|
|
|
116
116
|
dosingInstructions?: string;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
export
|
|
119
|
+
export type TestOrderPost = OrderPost;
|
|
120
120
|
|
|
121
121
|
export interface PatientOrderFetchResponse {
|
|
122
122
|
results: Array<Order>;
|
|
@@ -127,7 +127,7 @@ export interface Order {
|
|
|
127
127
|
action: OrderAction;
|
|
128
128
|
asNeeded: boolean;
|
|
129
129
|
asNeededCondition?: string;
|
|
130
|
-
autoExpireDate
|
|
130
|
+
autoExpireDate?: string | null;
|
|
131
131
|
brandName?: string;
|
|
132
132
|
careSetting: OpenmrsResource;
|
|
133
133
|
commentToFulfiller: string;
|
|
@@ -135,17 +135,17 @@ export interface Order {
|
|
|
135
135
|
dateActivated: string;
|
|
136
136
|
dateStopped?: string | null;
|
|
137
137
|
dispenseAsWritten: boolean;
|
|
138
|
-
dose: number;
|
|
139
|
-
doseUnits: OpenmrsResource;
|
|
138
|
+
dose: number | null;
|
|
139
|
+
doseUnits: OpenmrsResource | null;
|
|
140
140
|
dosingInstructions: string | null;
|
|
141
141
|
dosingType?: 'org.openmrs.FreeTextDosingInstructions' | 'org.openmrs.SimpleDosingInstructions';
|
|
142
|
-
drug: Drug;
|
|
143
|
-
duration: number;
|
|
144
|
-
durationUnits: OpenmrsResource;
|
|
145
|
-
encounter:
|
|
146
|
-
frequency: OpenmrsResource;
|
|
142
|
+
drug: Drug | null;
|
|
143
|
+
duration: number | null;
|
|
144
|
+
durationUnits: OpenmrsResource | null;
|
|
145
|
+
encounter: Encounter;
|
|
146
|
+
frequency: OpenmrsResource | null;
|
|
147
147
|
instructions?: string | null;
|
|
148
|
-
numRefills: number;
|
|
148
|
+
numRefills: number | null;
|
|
149
149
|
orderNumber: string;
|
|
150
150
|
orderReason: string | null;
|
|
151
151
|
orderReasonNonCoded: string | null;
|
|
@@ -167,9 +167,9 @@ export interface Order {
|
|
|
167
167
|
};
|
|
168
168
|
patient: OpenmrsResource;
|
|
169
169
|
previousOrder: { uuid: string; type: string; display: string } | null;
|
|
170
|
-
quantity: number;
|
|
171
|
-
quantityUnits: OpenmrsResource;
|
|
172
|
-
route: OpenmrsResource;
|
|
170
|
+
quantity: number | null;
|
|
171
|
+
quantityUnits: OpenmrsResource | null;
|
|
172
|
+
route: OpenmrsResource | null;
|
|
173
173
|
scheduleDate: null;
|
|
174
174
|
urgency: OrderUrgency;
|
|
175
175
|
|
|
@@ -207,16 +207,121 @@ export interface OrderType {
|
|
|
207
207
|
description: string;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
export type FulfillerStatus =
|
|
211
|
-
| 'RECEIVED'
|
|
212
|
-
| 'IN_PROGRESS'
|
|
213
|
-
| 'EXCEPTION'
|
|
214
|
-
| 'ON_HOLD'
|
|
215
|
-
| 'DECLINED'
|
|
216
|
-
| 'COMPLETED';
|
|
210
|
+
export type FulfillerStatus = 'RECEIVED' | 'IN_PROGRESS' | 'EXCEPTION' | 'ON_HOLD' | 'DECLINED' | 'COMPLETED';
|
|
217
211
|
|
|
212
|
+
/**
|
|
213
|
+
* A function type that converts a OrderBasketItem into
|
|
214
|
+
* a POST order payload
|
|
215
|
+
*/
|
|
218
216
|
export type PostDataPrepFunction = (
|
|
219
217
|
order: OrderBasketItem,
|
|
220
218
|
patientUuid: string,
|
|
221
219
|
encounterUuid: string | null,
|
|
220
|
+
orderingProviderUuid: string,
|
|
222
221
|
) => OrderPost;
|
|
222
|
+
|
|
223
|
+
export interface OrderBasketExtensionProps {
|
|
224
|
+
patient: fhir.Patient;
|
|
225
|
+
launchDrugOrderForm(order?: DrugOrderBasketItem): void;
|
|
226
|
+
launchLabOrderForm(orderTypeUuid: string, order?: TestOrderBasketItem): void;
|
|
227
|
+
launchGeneralOrderForm(orderTypeUuid: string, order?: OrderBasketItem): void;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface DrugOrderBasketItem extends OrderBasketItem {
|
|
231
|
+
drug: Drug;
|
|
232
|
+
unit: DosingUnit | null;
|
|
233
|
+
commonMedicationName: string;
|
|
234
|
+
dosage: number | null;
|
|
235
|
+
frequency: MedicationFrequency | null;
|
|
236
|
+
route: MedicationRoute | null;
|
|
237
|
+
quantityUnits: QuantityUnit | null;
|
|
238
|
+
patientInstructions: string | null;
|
|
239
|
+
asNeeded: boolean;
|
|
240
|
+
asNeededCondition: string | null;
|
|
241
|
+
startDate: Date | string;
|
|
242
|
+
durationUnit: DurationUnit | null;
|
|
243
|
+
duration: number | null;
|
|
244
|
+
pillsDispensed: number | null;
|
|
245
|
+
numRefills: number | null;
|
|
246
|
+
indication: string | null;
|
|
247
|
+
isFreeTextDosage: boolean;
|
|
248
|
+
freeTextDosage: string;
|
|
249
|
+
previousOrder?: string;
|
|
250
|
+
template?: OrderTemplate;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface DrugOrderTemplate {
|
|
254
|
+
uuid: string;
|
|
255
|
+
name: string;
|
|
256
|
+
drug: Drug;
|
|
257
|
+
template: OrderTemplate;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface OrderTemplate {
|
|
261
|
+
type: string;
|
|
262
|
+
dosingType: string;
|
|
263
|
+
dosingInstructions: DosingInstructions;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface DosingInstructions {
|
|
267
|
+
dose: Array<MedicationDosage>;
|
|
268
|
+
units: Array<DosingUnit>;
|
|
269
|
+
route: Array<MedicationRoute>;
|
|
270
|
+
frequency: Array<MedicationFrequency>;
|
|
271
|
+
instructions?: Array<MedicationInstructions>;
|
|
272
|
+
durationUnits?: Array<DurationUnit>;
|
|
273
|
+
quantityUnits?: Array<QuantityUnit>;
|
|
274
|
+
asNeeded?: boolean;
|
|
275
|
+
asNeededCondition?: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface MedicationDosage extends Omit<CommonMedicationProps, 'value'> {
|
|
279
|
+
value: number;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export type MedicationFrequency = CommonMedicationValueCoded;
|
|
283
|
+
|
|
284
|
+
export type MedicationRoute = CommonMedicationValueCoded;
|
|
285
|
+
|
|
286
|
+
export type MedicationInstructions = CommonMedicationProps;
|
|
287
|
+
|
|
288
|
+
export type DosingUnit = CommonMedicationValueCoded;
|
|
289
|
+
|
|
290
|
+
export type QuantityUnit = CommonMedicationValueCoded;
|
|
291
|
+
|
|
292
|
+
export type DurationUnit = CommonMedicationValueCoded;
|
|
293
|
+
|
|
294
|
+
interface CommonMedicationProps {
|
|
295
|
+
value: string;
|
|
296
|
+
default?: boolean;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface CommonMedicationValueCoded extends CommonMedicationProps {
|
|
300
|
+
valueCoded: string;
|
|
301
|
+
names?: string[];
|
|
302
|
+
}
|
|
303
|
+
export interface TestOrderBasketItem extends OrderBasketItem {
|
|
304
|
+
testType: {
|
|
305
|
+
label: string;
|
|
306
|
+
conceptUuid: string;
|
|
307
|
+
};
|
|
308
|
+
orderReason?: string;
|
|
309
|
+
specimenSource?: string;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface OrderBasketWindowProps {
|
|
313
|
+
encounterUuid: string;
|
|
314
|
+
onOrderBasketSubmitted?: (encounterUuid: string, postedOrders: Array<Order>) => void;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export interface ExportedOrderBasketWindowProps {
|
|
318
|
+
encounterUuid: string;
|
|
319
|
+
drugOrderWorkspaceName: string;
|
|
320
|
+
labOrderWorkspaceName: string;
|
|
321
|
+
generalOrderWorkspaceName: string;
|
|
322
|
+
patient: fhir.Patient;
|
|
323
|
+
patientUuid: string;
|
|
324
|
+
visitContext: Visit;
|
|
325
|
+
mutateVisitContext: () => void;
|
|
326
|
+
onOrderBasketSubmitted?: (encounterUuid: string, postedOrders: Array<Order>) => void;
|
|
327
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useSWRConfig } from 'swr';
|
|
3
|
+
import { restBaseUrl } from '@openmrs/esm-framework';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a function which refreshes the patient orders cache. Uses SWR's mutate function.
|
|
7
|
+
* Refreshes patient orders for all kinds of orders.
|
|
8
|
+
*
|
|
9
|
+
* @param patientUuid The UUID of the patient to get an order mutate function for.
|
|
10
|
+
*/
|
|
11
|
+
export function useMutatePatientOrders(patientUuid: string) {
|
|
12
|
+
const { mutate } = useSWRConfig();
|
|
13
|
+
const mutateOrders = useCallback(
|
|
14
|
+
() =>
|
|
15
|
+
mutate(
|
|
16
|
+
(key) => {
|
|
17
|
+
return typeof key === 'string' && key.startsWith(`${restBaseUrl}/order?patient=${patientUuid}`);
|
|
18
|
+
},
|
|
19
|
+
undefined,
|
|
20
|
+
{ revalidate: true },
|
|
21
|
+
),
|
|
22
|
+
[patientUuid, mutate],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
mutate: mutateOrders,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
2
|
import useSWRImmutable from 'swr/immutable';
|
|
3
|
+
import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
3
4
|
import type { OrderTypeFetchResponse } from './types';
|
|
4
|
-
import { useMemo } from 'react';
|
|
5
5
|
|
|
6
6
|
export function useOrderTypes() {
|
|
7
7
|
const orderTypesUrl = `${restBaseUrl}/ordertype`;
|
|
@@ -60,7 +60,7 @@ function useOrderableConceptSWR(searchTerm: string, orderableConceptSets?: Array
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export
|
|
63
|
+
export type OrderableConcept = OpenmrsResource;
|
|
64
64
|
|
|
65
65
|
export function useOrderableConceptSets(searchTerm: string, orderableConcepts: Array<string>) {
|
|
66
66
|
const { data, isLoading, error } = useOrderableConceptSWR(
|