@openmrs/esm-patient-common-lib 11.3.1-patch.9064 → 11.3.1-patch.9310
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 +1 -1
- package/src/empty-state/empty-state.test.tsx +4 -4
- package/src/form-entry/form-entry.ts +22 -2
- package/src/index.ts +0 -1
- package/src/launchStartVisitPrompt.tsx +2 -1
- package/src/offline/visit.ts +2 -16
- package/src/orders/index.ts +4 -2
- package/src/orders/postOrders.ts +1 -1
- package/src/orders/showOrderSuccessToast.ts +28 -0
- package/src/orders/types/order.ts +109 -9
- package/src/orders/useMutatePatientOrders.ts +24 -0
- package/src/orders/useOrderBasket.test.tsx +6 -3
- package/src/orders/useOrderBasket.ts +23 -13
- package/src/orders/useOrders.ts +5 -5
- package/src/store/patient-chart-store.ts +43 -29
- package/src/visit/revalidation-utils.ts +9 -0
- package/src/visit/visit-mutations.ts +8 -14
- package/src/workspaces.ts +53 -28
- package/src/form-entry-interop.ts +0 -152
package/package.json
CHANGED
|
@@ -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,26 @@ 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
|
+
setHasUnsavedChanges?(hasUnsavedChanges: boolean);
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ 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';
|
|
8
7
|
export * from './launchStartVisitPrompt';
|
|
9
8
|
export * from './offline/visit';
|
|
@@ -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/offline/visit.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
queueSynchronizationItem,
|
|
6
6
|
useConnectivity,
|
|
7
7
|
useSession,
|
|
8
|
-
useVisit,
|
|
8
|
+
type useVisit,
|
|
9
9
|
type Visit,
|
|
10
10
|
} from '@openmrs/esm-framework';
|
|
11
11
|
import { useEffect, useState } from 'react';
|
|
@@ -23,20 +23,6 @@ export interface OfflineVisit extends NewVisitPayload {
|
|
|
23
23
|
uuid: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Similar to {@link useVisit}, returns the given patient's active visit, but also considers
|
|
28
|
-
* offline visits created by the patient chart while offline.
|
|
29
|
-
* @param patientUuid The UUID of the patient.
|
|
30
|
-
*/
|
|
31
|
-
export function useVisitOrOfflineVisit(patientUuid: string) {
|
|
32
|
-
const isOnline = useConnectivity();
|
|
33
|
-
|
|
34
|
-
const onlineVisit = useVisit(patientUuid);
|
|
35
|
-
const offlineVisit = useOfflineVisit(patientUuid);
|
|
36
|
-
|
|
37
|
-
return isOnline ? onlineVisit : offlineVisit;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
26
|
/**
|
|
41
27
|
* Returns the patient's current offline visit.
|
|
42
28
|
* @param patientUuid The UUID of the patient.
|
|
@@ -132,7 +118,7 @@ export async function createOfflineVisitForPatient(
|
|
|
132
118
|
};
|
|
133
119
|
|
|
134
120
|
await queueSynchronizationItem(visitSyncType, offlineVisit, descriptor);
|
|
135
|
-
return offlineVisit;
|
|
121
|
+
return offlineVisitToVisit(offlineVisit);
|
|
136
122
|
}
|
|
137
123
|
|
|
138
124
|
function offlineVisitToVisit(offlineVisit: OfflineVisit): Visit {
|
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
|
@@ -120,7 +120,7 @@ export async function postOrders(patientUuid: string, encounterUuid: string, abo
|
|
|
120
120
|
return erroredItems;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
function postOrder(body: OrderPost, abortController?: AbortController) {
|
|
123
|
+
export function postOrder(body: OrderPost, abortController?: AbortController) {
|
|
124
124
|
return openmrsFetch(`${restBaseUrl}/order`, {
|
|
125
125
|
method: 'POST',
|
|
126
126
|
signal: abortController?.signal,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
2
|
+
import { type TFunction } from 'i18next';
|
|
3
|
+
import { type OrderBasketItem } from './types';
|
|
4
|
+
|
|
5
|
+
export function showOrderSuccessToast(t: TFunction, patientOrderItems: OrderBasketItem[]) {
|
|
6
|
+
const orderedString = patientOrderItems
|
|
7
|
+
.filter((item) => ['NEW', 'RENEW'].includes(item.action))
|
|
8
|
+
.map((item) => item.display)
|
|
9
|
+
.join(', ');
|
|
10
|
+
const updatedString = patientOrderItems
|
|
11
|
+
.filter((item) => item.action === 'REVISE')
|
|
12
|
+
.map((item) => item.display)
|
|
13
|
+
.join(', ');
|
|
14
|
+
const discontinuedString = patientOrderItems
|
|
15
|
+
.filter((item) => item.action === 'DISCONTINUE')
|
|
16
|
+
.map((item) => item.display)
|
|
17
|
+
.join(', ');
|
|
18
|
+
|
|
19
|
+
showSnackbar({
|
|
20
|
+
isLowContrast: true,
|
|
21
|
+
kind: 'success',
|
|
22
|
+
title: t('orderCompleted', 'Placed orders'),
|
|
23
|
+
subtitle:
|
|
24
|
+
(orderedString && `${t('ordered', 'Placed order for')} ${orderedString}. `) +
|
|
25
|
+
(updatedString && `${t('updated', 'Updated')} ${updatedString}. `) +
|
|
26
|
+
(discontinuedString && `${t('discontinued', 'Discontinued')} ${discontinuedString}.`),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -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?: {
|
|
@@ -71,6 +71,8 @@ export interface OrderBasketItem {
|
|
|
71
71
|
orderType?: string;
|
|
72
72
|
orderNumber?: string;
|
|
73
73
|
scheduledDate?: Date;
|
|
74
|
+
encounterUuid?: string;
|
|
75
|
+
visit: Visit;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export type OrderUrgency = 'ROUTINE' | 'STAT' | 'ON_SCHEDULED_DATE';
|
|
@@ -142,7 +144,7 @@ export interface Order {
|
|
|
142
144
|
drug: Drug;
|
|
143
145
|
duration: number;
|
|
144
146
|
durationUnits: OpenmrsResource;
|
|
145
|
-
encounter:
|
|
147
|
+
encounter: Encounter;
|
|
146
148
|
frequency: OpenmrsResource;
|
|
147
149
|
instructions?: string | null;
|
|
148
150
|
numRefills: number;
|
|
@@ -207,16 +209,114 @@ export interface OrderType {
|
|
|
207
209
|
description: string;
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
export type FulfillerStatus =
|
|
211
|
-
| 'RECEIVED'
|
|
212
|
-
| 'IN_PROGRESS'
|
|
213
|
-
| 'EXCEPTION'
|
|
214
|
-
| 'ON_HOLD'
|
|
215
|
-
| 'DECLINED'
|
|
216
|
-
| 'COMPLETED';
|
|
212
|
+
export type FulfillerStatus = 'RECEIVED' | 'IN_PROGRESS' | 'EXCEPTION' | 'ON_HOLD' | 'DECLINED' | 'COMPLETED';
|
|
217
213
|
|
|
218
214
|
export type PostDataPrepFunction = (
|
|
219
215
|
order: OrderBasketItem,
|
|
220
216
|
patientUuid: string,
|
|
221
217
|
encounterUuid: string | null,
|
|
222
218
|
) => OrderPost;
|
|
219
|
+
|
|
220
|
+
export interface OrderBasketExtensionProps {
|
|
221
|
+
patient: fhir.Patient;
|
|
222
|
+
launchDrugOrderForm(order?: DrugOrderBasketItem): void;
|
|
223
|
+
launchLabOrderForm(orderTypeUuid: string, order?: TestOrderBasketItem): void;
|
|
224
|
+
launchGeneralOrderForm(orderTypeUuid: string, order?: OrderBasketItem): void;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface DrugOrderBasketItem extends OrderBasketItem {
|
|
228
|
+
drug: Drug;
|
|
229
|
+
unit: DosingUnit;
|
|
230
|
+
commonMedicationName: string;
|
|
231
|
+
dosage: number;
|
|
232
|
+
frequency: MedicationFrequency;
|
|
233
|
+
route: MedicationRoute;
|
|
234
|
+
quantityUnits: QuantityUnit;
|
|
235
|
+
patientInstructions: string;
|
|
236
|
+
asNeeded: boolean;
|
|
237
|
+
asNeededCondition: string;
|
|
238
|
+
startDate: Date | string;
|
|
239
|
+
durationUnit: DurationUnit;
|
|
240
|
+
duration: number | null;
|
|
241
|
+
pillsDispensed: number | null;
|
|
242
|
+
numRefills: number | null;
|
|
243
|
+
indication: string;
|
|
244
|
+
isFreeTextDosage: boolean;
|
|
245
|
+
freeTextDosage: string;
|
|
246
|
+
previousOrder?: string;
|
|
247
|
+
template?: OrderTemplate;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface DrugOrderTemplate {
|
|
251
|
+
uuid: string;
|
|
252
|
+
name: string;
|
|
253
|
+
drug: Drug;
|
|
254
|
+
template: OrderTemplate;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface OrderTemplate {
|
|
258
|
+
type: string;
|
|
259
|
+
dosingType: string;
|
|
260
|
+
dosingInstructions: DosingInstructions;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface DosingInstructions {
|
|
264
|
+
dose: Array<MedicationDosage>;
|
|
265
|
+
units: Array<DosingUnit>;
|
|
266
|
+
route: Array<MedicationRoute>;
|
|
267
|
+
frequency: Array<MedicationFrequency>;
|
|
268
|
+
instructions?: Array<MedicationInstructions>;
|
|
269
|
+
durationUnits?: Array<DurationUnit>;
|
|
270
|
+
quantityUnits?: Array<QuantityUnit>;
|
|
271
|
+
asNeeded?: boolean;
|
|
272
|
+
asNeededCondition?: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface MedicationDosage extends Omit<CommonMedicationProps, 'value'> {
|
|
276
|
+
value: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export type MedicationFrequency = CommonMedicationValueCoded;
|
|
280
|
+
|
|
281
|
+
export type MedicationRoute = CommonMedicationValueCoded;
|
|
282
|
+
|
|
283
|
+
export type MedicationInstructions = CommonMedicationProps;
|
|
284
|
+
|
|
285
|
+
export type DosingUnit = CommonMedicationValueCoded;
|
|
286
|
+
|
|
287
|
+
export type QuantityUnit = CommonMedicationValueCoded;
|
|
288
|
+
|
|
289
|
+
export type DurationUnit = CommonMedicationValueCoded;
|
|
290
|
+
|
|
291
|
+
interface CommonMedicationProps {
|
|
292
|
+
value: string;
|
|
293
|
+
default?: boolean;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface CommonMedicationValueCoded extends CommonMedicationProps {
|
|
297
|
+
valueCoded: string;
|
|
298
|
+
names?: string[];
|
|
299
|
+
}
|
|
300
|
+
export interface TestOrderBasketItem extends OrderBasketItem {
|
|
301
|
+
testType: {
|
|
302
|
+
label: string;
|
|
303
|
+
conceptUuid: string;
|
|
304
|
+
};
|
|
305
|
+
orderReason?: string;
|
|
306
|
+
specimenSource?: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface OrderBasketWindowProps {
|
|
310
|
+
encounterUuid: string;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface ExportedOrderBasketWindowProps {
|
|
314
|
+
encounterUuid: string;
|
|
315
|
+
drugOrderWorkspaceName: string;
|
|
316
|
+
labOrderWorkspaceName: string;
|
|
317
|
+
generalOrderWorkspaceName: string;
|
|
318
|
+
patient: fhir.Patient;
|
|
319
|
+
patientUuid: string;
|
|
320
|
+
visitContext: Visit;
|
|
321
|
+
mutateVisitContext: () => void;
|
|
322
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { useSWRConfig } from 'swr';
|
|
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((key) => {
|
|
16
|
+
return typeof key === 'string' && key.startsWith(`${restBaseUrl}/order?patient=${patientUuid}`);
|
|
17
|
+
}),
|
|
18
|
+
[patientUuid, mutate],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
mutate: mutateOrders,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react';
|
|
|
2
2
|
import { useOrderBasket } from './useOrderBasket';
|
|
3
3
|
import { type OrderBasketItem, type PostDataPrepFunction } from './types';
|
|
4
4
|
import { _resetOrderBasketStore } from './store';
|
|
5
|
+
import { mockFhirPatient } from '__mocks__';
|
|
5
6
|
|
|
6
7
|
const mockDrugOrderBasketItem = {
|
|
7
8
|
action: 'NEW',
|
|
@@ -19,7 +20,9 @@ describe('useOrderBasket', () => {
|
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
it('returns the correct list of orders given a grouping', () => {
|
|
22
|
-
const { result } = renderHook(() =>
|
|
23
|
+
const { result } = renderHook(() =>
|
|
24
|
+
useOrderBasket(mockFhirPatient, 'medications', ((x) => x) as unknown as PostDataPrepFunction),
|
|
25
|
+
);
|
|
23
26
|
expect(result.current.orders).toEqual([]);
|
|
24
27
|
act(() => {
|
|
25
28
|
result.current.setOrders([mockDrugOrderBasketItem]);
|
|
@@ -29,10 +32,10 @@ describe('useOrderBasket', () => {
|
|
|
29
32
|
|
|
30
33
|
it('can modify items in one grouping without affecting the other', () => {
|
|
31
34
|
const { result: drugResult } = renderHook(() =>
|
|
32
|
-
useOrderBasket('medications', ((x) => x) as unknown as PostDataPrepFunction),
|
|
35
|
+
useOrderBasket(mockFhirPatient, 'medications', ((x) => x) as unknown as PostDataPrepFunction),
|
|
33
36
|
);
|
|
34
37
|
const { result: labResult } = renderHook(() =>
|
|
35
|
-
useOrderBasket('labs', ((x) => x) as unknown as PostDataPrepFunction),
|
|
38
|
+
useOrderBasket(mockFhirPatient, 'labs', ((x) => x) as unknown as PostDataPrepFunction),
|
|
36
39
|
);
|
|
37
40
|
expect(drugResult.current.orders).toEqual([]);
|
|
38
41
|
expect(labResult.current.orders).toEqual([]);
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { type Actions, useStoreWithActions } from '@openmrs/esm-framework';
|
|
2
2
|
import type { OrderBasketItem, PostDataPrepFunction } from './types';
|
|
3
|
-
import { getPatientUuidFromStore } from '../store/patient-chart-store';
|
|
4
3
|
import { useEffect } from 'react';
|
|
5
4
|
import { type OrderBasketStore, orderBasketStore } from './store';
|
|
6
5
|
|
|
7
6
|
const orderBasketStoreActions = {
|
|
8
7
|
setOrderBasketItems(
|
|
9
8
|
state: OrderBasketStore,
|
|
9
|
+
patientUuid: string,
|
|
10
10
|
grouping: string,
|
|
11
11
|
value: Array<OrderBasketItem> | (() => Array<OrderBasketItem>),
|
|
12
12
|
) {
|
|
13
|
-
const patientUuid = getPatientUuidFromStore();
|
|
14
13
|
if (!Object.keys(state.postDataPrepFunctions).includes(grouping)) {
|
|
15
14
|
console.warn(`Programming error: You must register a postDataPrepFunction for grouping ${grouping} `);
|
|
16
15
|
}
|
|
@@ -34,8 +33,11 @@ const orderBasketStoreActions = {
|
|
|
34
33
|
},
|
|
35
34
|
} satisfies Actions<OrderBasketStore>;
|
|
36
35
|
|
|
37
|
-
function getOrderItems(
|
|
38
|
-
|
|
36
|
+
function getOrderItems(
|
|
37
|
+
patientUuid: string,
|
|
38
|
+
items: OrderBasketStore['items'],
|
|
39
|
+
grouping?: string | null,
|
|
40
|
+
): Array<OrderBasketItem> {
|
|
39
41
|
const patientItems = items?.[patientUuid] ?? {};
|
|
40
42
|
return grouping ? patientItems[grouping] ?? [] : Object.values(patientItems).flat();
|
|
41
43
|
}
|
|
@@ -44,9 +46,8 @@ export interface ClearOrdersOptions {
|
|
|
44
46
|
exceptThoseMatching: (order: OrderBasketItem) => boolean;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
function clearOrders(options?: ClearOrdersOptions) {
|
|
49
|
+
function clearOrders(patientUuid: string, options?: ClearOrdersOptions) {
|
|
48
50
|
const exceptThoseMatchingFcn = options?.exceptThoseMatching ?? (() => false);
|
|
49
|
-
const patientUuid = getPatientUuidFromStore();
|
|
50
51
|
const items = orderBasketStore.getState().items;
|
|
51
52
|
const patientItems = items[patientUuid] ?? {};
|
|
52
53
|
const newPatientItems = Object.fromEntries(
|
|
@@ -77,13 +78,18 @@ type UseOrderBasketReturn<T, U> = {
|
|
|
77
78
|
* A PostDataPrepFunction must be provided for each grouping, but does not necessarily have to be provided
|
|
78
79
|
* in every usage of useOrderBasket with a grouping key.
|
|
79
80
|
*/
|
|
80
|
-
export function useOrderBasket<T extends OrderBasketItem>(): UseOrderBasketReturn<T, void>;
|
|
81
|
-
export function useOrderBasket<T extends OrderBasketItem>(grouping: string): UseOrderBasketReturn<T, string>;
|
|
81
|
+
export function useOrderBasket<T extends OrderBasketItem>(patient: fhir.Patient): UseOrderBasketReturn<T, void>;
|
|
82
82
|
export function useOrderBasket<T extends OrderBasketItem>(
|
|
83
|
+
patient: fhir.Patient,
|
|
84
|
+
grouping: string,
|
|
85
|
+
): UseOrderBasketReturn<T, string>;
|
|
86
|
+
export function useOrderBasket<T extends OrderBasketItem>(
|
|
87
|
+
patient: fhir.Patient,
|
|
83
88
|
grouping: string,
|
|
84
89
|
postDataPrepFunction: PostDataPrepFunction,
|
|
85
90
|
): UseOrderBasketReturn<T, string>;
|
|
86
91
|
export function useOrderBasket<T extends OrderBasketItem>(
|
|
92
|
+
patient: fhir.Patient,
|
|
87
93
|
grouping?: string | null,
|
|
88
94
|
postDataPrepFunction?: PostDataPrepFunction,
|
|
89
95
|
): UseOrderBasketReturn<T, string | void> {
|
|
@@ -91,7 +97,7 @@ export function useOrderBasket<T extends OrderBasketItem>(
|
|
|
91
97
|
orderBasketStore,
|
|
92
98
|
orderBasketStoreActions,
|
|
93
99
|
);
|
|
94
|
-
const orders = getOrderItems(items, grouping);
|
|
100
|
+
const orders = getOrderItems(patient.id, items, grouping);
|
|
95
101
|
|
|
96
102
|
useEffect(() => {
|
|
97
103
|
if (postDataPrepFunction && !postDataPrepFunctions[grouping]) {
|
|
@@ -99,15 +105,19 @@ export function useOrderBasket<T extends OrderBasketItem>(
|
|
|
99
105
|
}
|
|
100
106
|
}, [postDataPrepFunction, grouping, postDataPrepFunctions, setPostDataPrepFunctionForGrouping]);
|
|
101
107
|
|
|
108
|
+
const clearOrdersForPatient = (options?: ClearOrdersOptions) => {
|
|
109
|
+
clearOrders(patient.id, options);
|
|
110
|
+
};
|
|
111
|
+
|
|
102
112
|
if (typeof grouping === 'string') {
|
|
103
113
|
const setOrders = (value: Array<T> | (() => Array<T>)) => {
|
|
104
|
-
return setOrderBasketItems(grouping, value);
|
|
114
|
+
return setOrderBasketItems(patient.id, grouping, value);
|
|
105
115
|
};
|
|
106
|
-
return { orders, clearOrders, setOrders } as UseOrderBasketReturn<T, string>;
|
|
116
|
+
return { orders, clearOrders: clearOrdersForPatient, setOrders } as UseOrderBasketReturn<T, string>;
|
|
107
117
|
} else {
|
|
108
118
|
const setOrders = (groupingKey: string, value: Array<T> | (() => Array<T>)) => {
|
|
109
|
-
setOrderBasketItems(groupingKey, value);
|
|
119
|
+
setOrderBasketItems(patient.id, groupingKey, value);
|
|
110
120
|
};
|
|
111
|
-
return { orders, clearOrders, setOrders } as UseOrderBasketReturn<T, void>;
|
|
121
|
+
return { orders, clearOrders: clearOrdersForPatient, setOrders } as UseOrderBasketReturn<T, void>;
|
|
112
122
|
}
|
|
113
123
|
}
|
package/src/orders/useOrders.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
2
|
import useSWR, { useSWRConfig } from 'swr';
|
|
3
3
|
import { type FetchResponse, openmrsFetch, restBaseUrl, translateFrom } from '@openmrs/esm-framework';
|
|
4
|
-
import type { PatientOrderFetchResponse, PriorityOption } from './types';
|
|
4
|
+
import type { Order, PatientOrderFetchResponse, PriorityOption } from './types';
|
|
5
5
|
|
|
6
6
|
export type Status = 'ACTIVE' | 'any';
|
|
7
7
|
export const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0';
|
|
@@ -10,7 +10,7 @@ const patientChartAppModuleName = '@openmrs/esm-patient-chart-app';
|
|
|
10
10
|
export const drugCustomRepresentation =
|
|
11
11
|
'custom:(uuid,dosingType,orderNumber,accessionNumber,' +
|
|
12
12
|
'patient:ref,action,careSetting:ref,previousOrder:ref,dateActivated,scheduledDate,dateStopped,autoExpireDate,' +
|
|
13
|
-
'orderType:ref,encounter:
|
|
13
|
+
'orderType:ref,encounter:(uuid,display,visit),orderer:(uuid,display,person:(display)),orderReason,orderReasonNonCoded,orderType,urgency,instructions,' +
|
|
14
14
|
'commentToFulfiller,drug:(uuid,display,strength,dosageForm:(display,uuid),concept),dose,doseUnits:ref,' +
|
|
15
15
|
'frequency:ref,asNeeded,asNeededCondition,quantity,quantityUnits:ref,numRefills,dosingInstructions,' +
|
|
16
16
|
'duration,durationUnits:ref,route:ref,brandName,dispenseAsWritten)';
|
|
@@ -25,8 +25,8 @@ export function usePatientOrders(
|
|
|
25
25
|
const { mutate } = useSWRConfig();
|
|
26
26
|
const baseOrdersUrl =
|
|
27
27
|
startDate && endDate
|
|
28
|
-
? `${restBaseUrl}/order?patient=${patientUuid}&careSetting=${careSettingUuid}&v=
|
|
29
|
-
: `${restBaseUrl}/order?patient=${patientUuid}&careSetting=${careSettingUuid}&v=
|
|
28
|
+
? `${restBaseUrl}/order?patient=${patientUuid}&careSetting=${careSettingUuid}&v=default&activatedOnOrAfterDate=${startDate}&activatedOnOrBeforeDate=${endDate}`
|
|
29
|
+
: `${restBaseUrl}/order?patient=${patientUuid}&careSetting=${careSettingUuid}&v=default&status=${status}`;
|
|
30
30
|
const ordersUrl = orderType ? `${baseOrdersUrl}&orderTypes=${orderType}` : baseOrdersUrl;
|
|
31
31
|
|
|
32
32
|
const { data, error, isLoading, isValidating } = useSWR<FetchResponse<PatientOrderFetchResponse>, Error>(
|
|
@@ -60,7 +60,7 @@ export function usePatientOrders(
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function getDrugOrderByUuid(orderUuid: string) {
|
|
63
|
-
return openmrsFetch(`${restBaseUrl}/order/${orderUuid}?v=${drugCustomRepresentation}`);
|
|
63
|
+
return openmrsFetch<Order>(`${restBaseUrl}/order/${orderUuid}?v=${drugCustomRepresentation}`);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// See the Urgency enum in https://github.com/openmrs/openmrs-core/blob/492dcd35b85d48730bd19da48f6db146cc882c22/api/src/main/java/org/openmrs/Order.java
|
|
@@ -1,40 +1,54 @@
|
|
|
1
|
-
import { createGlobalStore,
|
|
1
|
+
import { type Actions, createGlobalStore, useStoreWithActions, type Visit } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
3
|
export interface PatientChartStore {
|
|
4
|
-
patientUuid
|
|
5
|
-
patient
|
|
4
|
+
patientUuid: string;
|
|
5
|
+
patient: fhir.Patient;
|
|
6
|
+
visitContext: Visit;
|
|
7
|
+
mutateVisitContext: () => void;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
const patientChartStoreName = 'patient-chart-global-store';
|
|
9
11
|
|
|
10
|
-
const patientChartStore = createGlobalStore<PatientChartStore>(patientChartStoreName, {
|
|
12
|
+
const patientChartStore = createGlobalStore<PatientChartStore>(patientChartStoreName, {
|
|
13
|
+
patientUuid: null,
|
|
14
|
+
patient: null,
|
|
15
|
+
visitContext: null,
|
|
16
|
+
mutateVisitContext: null,
|
|
17
|
+
});
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function usePatientChartStore() {
|
|
23
|
-
return useStore(patientChartStore);
|
|
24
|
-
}
|
|
19
|
+
const patientCharStoreActions = {
|
|
20
|
+
setPatient(_, patient: fhir.Patient) {
|
|
21
|
+
return { patient, patientUuid: patient?.id ?? null };
|
|
22
|
+
},
|
|
23
|
+
setVisitContext(_, visitContext: Visit, mutateVisitContext: () => void) {
|
|
24
|
+
return { visitContext, mutateVisitContext };
|
|
25
|
+
},
|
|
26
|
+
} satisfies Actions<PatientChartStore>;
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
* @returns {string} patientUuid
|
|
29
|
+
* Hook to access the values and sets of the patient chart store.
|
|
30
|
+
* Note: This hooks SHOULD only be used by components inside the patient chart app.
|
|
31
|
+
*
|
|
32
|
+
* Workspaces / extensions that can be mounted by other apps (ex: the start visit form in the queue's app,
|
|
33
|
+
* the clinical forms workspace in the ward app)
|
|
34
|
+
* should have the patient / visitContext explicitly passed in as props.
|
|
35
|
+
*
|
|
36
|
+
* As a safety feature, this hook requires the the patientUuid as the input, and only
|
|
37
|
+
* returns the actual store values if input patientUuid matches that in the store.
|
|
37
38
|
*/
|
|
38
|
-
export function
|
|
39
|
-
|
|
39
|
+
export function usePatientChartStore(patientUuid: string) {
|
|
40
|
+
const store = useStoreWithActions(patientChartStore, patientCharStoreActions);
|
|
41
|
+
if (store.patientUuid === patientUuid) {
|
|
42
|
+
return store;
|
|
43
|
+
} else {
|
|
44
|
+
const fakeStore: typeof store = {
|
|
45
|
+
...store,
|
|
46
|
+
mutateVisitContext: null,
|
|
47
|
+
setVisitContext: () => {},
|
|
48
|
+
patient: null,
|
|
49
|
+
patientUuid: null,
|
|
50
|
+
visitContext: null,
|
|
51
|
+
};
|
|
52
|
+
return fakeStore;
|
|
53
|
+
}
|
|
40
54
|
}
|
|
@@ -100,3 +100,12 @@ export function invalidateVisitAndEncounterData(mutate: KeyedMutator<unknown>, p
|
|
|
100
100
|
invalidateVisitHistory(mutate, patientUuid);
|
|
101
101
|
invalidatePatientEncounters(mutate, patientUuid);
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Invalidates a visit fetched by URL /visit/<uuid>
|
|
106
|
+
* @param mutate - SWR mutate function from useSWRConfig()
|
|
107
|
+
* @param visitUuid
|
|
108
|
+
*/
|
|
109
|
+
export function invalidateVisitByUuid(mutate: KeyedMutator<unknown>, visitUuid: string) {
|
|
110
|
+
mutate(new RegExp(`${restBaseUrl}/visit/${visitUuid}`));
|
|
111
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { useSWRConfig } from 'swr';
|
|
3
3
|
import { useVisit, type Visit, restBaseUrl } from '@openmrs/esm-framework';
|
|
4
|
+
import { usePatientChartStore } from '../store/patient-chart-store';
|
|
4
5
|
|
|
5
6
|
export interface VisitMutationOptions {
|
|
6
7
|
encounters?: boolean;
|
|
@@ -19,7 +20,7 @@ export interface VisitMutationOptions {
|
|
|
19
20
|
*/
|
|
20
21
|
export function useOptimisticVisitMutations(patientUuid: string) {
|
|
21
22
|
const { mutate } = useSWRConfig();
|
|
22
|
-
const {
|
|
23
|
+
const { visitContext, mutateVisitContext } = usePatientChartStore(patientUuid);
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Optimistically updates visit data in SWR caches without triggering network requests.
|
|
@@ -28,15 +29,8 @@ export function useOptimisticVisitMutations(patientUuid: string) {
|
|
|
28
29
|
const updateVisitOptimistically = useCallback(
|
|
29
30
|
(visitUuid: string, updates: Partial<Visit>) => {
|
|
30
31
|
// Update current visit SWR cache if it matches
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
`${restBaseUrl}/visit?patient=${patientUuid}&v=custom`,
|
|
34
|
-
(current: any) => ({
|
|
35
|
-
...current,
|
|
36
|
-
data: { ...current?.data, ...updates },
|
|
37
|
-
}),
|
|
38
|
-
false, // Don't revalidate
|
|
39
|
-
);
|
|
32
|
+
if (visitContext?.uuid === visitUuid) {
|
|
33
|
+
mutateVisitContext();
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
// Update visit lists across all hooks using regex pattern matching
|
|
@@ -59,7 +53,7 @@ export function useOptimisticVisitMutations(patientUuid: string) {
|
|
|
59
53
|
false, // Don't revalidate
|
|
60
54
|
);
|
|
61
55
|
},
|
|
62
|
-
[
|
|
56
|
+
[visitContext, mutateVisitContext, mutate, patientUuid],
|
|
63
57
|
);
|
|
64
58
|
|
|
65
59
|
/**
|
|
@@ -87,11 +81,11 @@ export function useOptimisticVisitMutations(patientUuid: string) {
|
|
|
87
81
|
);
|
|
88
82
|
|
|
89
83
|
// If deleted visit was current, revalidate current visit to get new state
|
|
90
|
-
if (
|
|
91
|
-
|
|
84
|
+
if (visitContext?.uuid === visitUuid) {
|
|
85
|
+
mutateVisitContext();
|
|
92
86
|
}
|
|
93
87
|
},
|
|
94
|
-
[
|
|
88
|
+
[visitContext, mutateVisitContext, mutate, patientUuid],
|
|
95
89
|
);
|
|
96
90
|
|
|
97
91
|
/**
|
package/src/workspaces.ts
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
navigateAndLaunchWorkspace,
|
|
3
|
+
launchWorkspace2,
|
|
4
|
+
navigate,
|
|
6
5
|
showModal,
|
|
7
6
|
useFeatureFlag,
|
|
7
|
+
type Visit,
|
|
8
|
+
type Workspace2DefinitionProps,
|
|
8
9
|
} from '@openmrs/esm-framework';
|
|
9
|
-
import { launchStartVisitPrompt } from './launchStartVisitPrompt';
|
|
10
10
|
import { usePatientChartStore } from './store/patient-chart-store';
|
|
11
11
|
import { useSystemVisitSetting } from './useSystemVisitSetting';
|
|
12
|
-
import { useVisitOrOfflineVisit } from './offline/visit';
|
|
13
12
|
|
|
14
|
-
export interface
|
|
13
|
+
export interface PatientWorkspaceGroupProps {
|
|
15
14
|
patient: fhir.Patient;
|
|
16
15
|
patientUuid: string;
|
|
16
|
+
visitContext: Visit;
|
|
17
|
+
mutateVisitContext: () => void;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
export interface PatientChartWorkspaceActionButtonProps {
|
|
21
|
+
groupProps: PatientWorkspaceGroupProps;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PatientWorkspace2DefinitionProps<WorkspaceProps extends Object, WindowProps extends Object>
|
|
25
|
+
extends Workspace2DefinitionProps<WorkspaceProps, WindowProps, PatientWorkspaceGroupProps> {}
|
|
26
|
+
|
|
19
27
|
export function launchPatientChartWithWorkspaceOpen({
|
|
20
28
|
patientUuid,
|
|
21
29
|
workspaceName,
|
|
@@ -27,38 +35,55 @@ export function launchPatientChartWithWorkspaceOpen({
|
|
|
27
35
|
dashboardName?: string;
|
|
28
36
|
additionalProps?: object;
|
|
29
37
|
}) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
workspaceName: workspaceName,
|
|
33
|
-
contextKey: `patient/${patientUuid}`,
|
|
34
|
-
additionalProps,
|
|
35
|
-
});
|
|
38
|
+
launchWorkspace2(workspaceName, additionalProps);
|
|
39
|
+
navigate({ to: '${openmrsSpaBase}/patient/' + `${patientUuid}/chart` + (dashboardName ? `/${dashboardName}` : '') });
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
export function useLaunchWorkspaceRequiringVisit<T extends object>(workspaceName: string) {
|
|
39
|
-
const
|
|
42
|
+
export function useLaunchWorkspaceRequiringVisit<T extends object>(patientUuid: string, workspaceName: string) {
|
|
43
|
+
const startVisitIfNeeded = useStartVisitIfNeeded(patientUuid);
|
|
44
|
+
const launchPatientWorkspaceCb = useCallback(
|
|
45
|
+
(workspaceProps?: T, windowProps?: any, groupProps?: any) => {
|
|
46
|
+
startVisitIfNeeded().then((didStartVisit) => {
|
|
47
|
+
if (didStartVisit) {
|
|
48
|
+
launchWorkspace2(workspaceName, workspaceProps, windowProps);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
[startVisitIfNeeded, workspaceName],
|
|
53
|
+
);
|
|
54
|
+
return launchPatientWorkspaceCb;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useStartVisitIfNeeded(patientUuid: string) {
|
|
58
|
+
const { visitContext } = usePatientChartStore(patientUuid);
|
|
40
59
|
const { systemVisitEnabled } = useSystemVisitSetting();
|
|
41
|
-
const { currentVisit } = useVisitOrOfflineVisit(patientUuid);
|
|
42
60
|
const isRdeEnabled = useFeatureFlag('rde');
|
|
43
61
|
|
|
44
|
-
const
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
const startVisitIfNeeded = useCallback(async (): Promise<boolean> => {
|
|
63
|
+
if (!systemVisitEnabled || visitContext) {
|
|
64
|
+
return true;
|
|
65
|
+
} else {
|
|
66
|
+
return new Promise<boolean>((resolve) => {
|
|
49
67
|
if (isRdeEnabled) {
|
|
50
68
|
const dispose = showModal('visit-context-switcher', {
|
|
51
69
|
patientUuid,
|
|
52
|
-
closeModal: () =>
|
|
53
|
-
|
|
70
|
+
closeModal: () => {
|
|
71
|
+
dispose();
|
|
72
|
+
resolve(false);
|
|
73
|
+
},
|
|
74
|
+
onAfterVisitSelected: () => {
|
|
75
|
+
resolve(true);
|
|
76
|
+
},
|
|
54
77
|
size: 'sm',
|
|
55
78
|
});
|
|
56
79
|
} else {
|
|
57
|
-
|
|
80
|
+
const dispose = showModal('start-visit-dialog', {
|
|
81
|
+
closeModal: () => dispose(),
|
|
82
|
+
onVisitStarted: () => resolve(true),
|
|
83
|
+
});
|
|
58
84
|
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return launchPatientWorkspaceCb;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}, [visitContext, systemVisitEnabled, isRdeEnabled, patientUuid]);
|
|
88
|
+
return startVisitIfNeeded;
|
|
64
89
|
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { launchWorkspace } from '@openmrs/esm-framework';
|
|
2
|
-
import { type Form, type HtmlFormEntryForm } from './types';
|
|
3
|
-
import { launchStartVisitPrompt } from './launchStartVisitPrompt';
|
|
4
|
-
|
|
5
|
-
export const clinicalFormsWorkspace = 'clinical-forms-workspace';
|
|
6
|
-
export const formEntryWorkspace = 'patient-form-entry-workspace';
|
|
7
|
-
export const htmlFormEntryWorkspace = 'patient-html-form-entry-workspace';
|
|
8
|
-
|
|
9
|
-
const formEngineResourceName = 'formEngine';
|
|
10
|
-
const htmlformentryFormEngine = 'htmlformentry';
|
|
11
|
-
const uiStyleResourceName = 'uiStyle';
|
|
12
|
-
const uiStyleSimple = 'simple';
|
|
13
|
-
|
|
14
|
-
export function launchFormEntryOrHtmlForms(
|
|
15
|
-
htmlFormEntryForms: Array<HtmlFormEntryForm>,
|
|
16
|
-
patientUuid: string,
|
|
17
|
-
form: Form,
|
|
18
|
-
visitUuid?: string,
|
|
19
|
-
encounterUuid?: string,
|
|
20
|
-
visitTypeUuid?: string,
|
|
21
|
-
visitStartDatetime?: string,
|
|
22
|
-
visitStopDatetime?: string,
|
|
23
|
-
mutateForms?: () => void,
|
|
24
|
-
clinicalFormsWorkspaceName = clinicalFormsWorkspace,
|
|
25
|
-
formEntryWorkspaceName = formEntryWorkspace,
|
|
26
|
-
htmlFormEntryWorkspaceName = htmlFormEntryWorkspace,
|
|
27
|
-
) {
|
|
28
|
-
if (visitUuid) {
|
|
29
|
-
const { uuid: formUuid, display, name } = form ?? {};
|
|
30
|
-
const formName = display ?? name ?? '--';
|
|
31
|
-
|
|
32
|
-
const htmlForm = toHtmlForm(form, htmlFormEntryForms);
|
|
33
|
-
if (htmlForm) {
|
|
34
|
-
launchHtmlFormEntry(patientUuid, formName, encounterUuid, visitUuid, htmlForm, htmlFormEntryWorkspaceName);
|
|
35
|
-
} else {
|
|
36
|
-
launchFormEntry(
|
|
37
|
-
formUuid,
|
|
38
|
-
patientUuid,
|
|
39
|
-
encounterUuid,
|
|
40
|
-
formName,
|
|
41
|
-
visitUuid,
|
|
42
|
-
visitTypeUuid,
|
|
43
|
-
visitStartDatetime,
|
|
44
|
-
visitStopDatetime,
|
|
45
|
-
htmlForm,
|
|
46
|
-
mutateForms,
|
|
47
|
-
clinicalFormsWorkspaceName,
|
|
48
|
-
formEntryWorkspaceName,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
} else {
|
|
52
|
-
launchStartVisitPrompt();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function launchFormEntry(
|
|
57
|
-
formUuid: string,
|
|
58
|
-
patientUuid: string,
|
|
59
|
-
encounterUuid?: string,
|
|
60
|
-
formName?: string,
|
|
61
|
-
visitUuid?: string,
|
|
62
|
-
visitTypeUuid?: string,
|
|
63
|
-
visitStartDatetime?: string,
|
|
64
|
-
visitStopDatetime?: string,
|
|
65
|
-
htmlForm?: HtmlFormEntryForm,
|
|
66
|
-
mutateForm?: () => void,
|
|
67
|
-
clinicalFormsWorkspaceName = clinicalFormsWorkspace,
|
|
68
|
-
formEntryWorkspaceName = formEntryWorkspace,
|
|
69
|
-
) {
|
|
70
|
-
launchWorkspace(formEntryWorkspaceName, {
|
|
71
|
-
workspaceTitle: formName,
|
|
72
|
-
clinicalFormsWorkspaceName,
|
|
73
|
-
formEntryWorkspaceName,
|
|
74
|
-
patientUuid,
|
|
75
|
-
mutateForm,
|
|
76
|
-
formInfo: {
|
|
77
|
-
encounterUuid,
|
|
78
|
-
formUuid,
|
|
79
|
-
patientUuid,
|
|
80
|
-
visitTypeUuid: visitTypeUuid,
|
|
81
|
-
visitUuid: visitUuid,
|
|
82
|
-
visitStartDatetime,
|
|
83
|
-
visitStopDatetime,
|
|
84
|
-
htmlForm,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function launchHtmlFormEntry(
|
|
90
|
-
patientUuid: string,
|
|
91
|
-
formName: string,
|
|
92
|
-
encounterUuid: string,
|
|
93
|
-
visitUuid: string,
|
|
94
|
-
htmlForm: HtmlFormEntryForm,
|
|
95
|
-
workspaceName = htmlFormEntryWorkspace,
|
|
96
|
-
) {
|
|
97
|
-
launchWorkspace(workspaceName, {
|
|
98
|
-
workspaceTitle: formName,
|
|
99
|
-
patientUuid,
|
|
100
|
-
formInfo: {
|
|
101
|
-
encounterUuid,
|
|
102
|
-
visitUuid,
|
|
103
|
-
htmlForm,
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* For a given form , check if it is an HTML form. If it is, return the HtmlFormEntryForm object,
|
|
110
|
-
* otherwise return null
|
|
111
|
-
* @param form
|
|
112
|
-
* @param htmlFormEntryForms A list of HTML forms configured in @esm-patient-forms-app's config
|
|
113
|
-
*
|
|
114
|
-
* @returns
|
|
115
|
-
*/
|
|
116
|
-
function toHtmlForm(form: Form, htmlFormEntryForms: Array<HtmlFormEntryForm>): HtmlFormEntryForm {
|
|
117
|
-
const isHtmlForm =
|
|
118
|
-
htmlFormEntryForms?.some((hfeForm) => hfeForm.formUuid === form.uuid) ||
|
|
119
|
-
form.resources?.some((resource) => {
|
|
120
|
-
return resource.name === formEngineResourceName && resource.valueReference === htmlformentryFormEngine;
|
|
121
|
-
});
|
|
122
|
-
if (isHtmlForm) {
|
|
123
|
-
const hfeForm = htmlFormEntryForms?.find((f) => f.formUuid === form.uuid);
|
|
124
|
-
const simple = form.resources?.some((r) => r.name === uiStyleResourceName && r.valueReference === uiStyleSimple);
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
formUuid: form.uuid,
|
|
128
|
-
formName: hfeForm?.formName ?? form.display ?? form.name,
|
|
129
|
-
formUiResource: hfeForm?.formUiResource,
|
|
130
|
-
formUiPage: hfeForm?.formUiPage ?? (simple ? 'enterHtmlFormWithSimpleUi' : 'enterHtmlFormWithStandardUi'),
|
|
131
|
-
formEditUiPage: hfeForm?.formEditUiPage ?? (simple ? 'editHtmlFormWithSimpleUi' : 'editHtmlFormWithStandardUi'),
|
|
132
|
-
};
|
|
133
|
-
} else {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Given a list of forms and a list of HtmlFormEntryForm objects from configuration, return a List of HtmlFormEntryForm
|
|
140
|
-
* returned forms either
|
|
141
|
-
* a) have a form resource with a name of `formEngine` and a value of `htmlformentry, or
|
|
142
|
-
* b) have an entry in the HtmlFormEntryForm array for a given form uuid
|
|
143
|
-
* The HtmlFormEntryForm configuration provides a means to override the name and rendering mode of a given form
|
|
144
|
-
* @param allForms
|
|
145
|
-
* @param htmlFormEntryForms
|
|
146
|
-
*/
|
|
147
|
-
export function mapFormsToHtmlFormEntryForms(
|
|
148
|
-
allForms: Array<Form>,
|
|
149
|
-
htmlFormEntryForms: Array<HtmlFormEntryForm>,
|
|
150
|
-
): Array<HtmlFormEntryForm> {
|
|
151
|
-
return allForms?.map((form) => toHtmlForm(form, htmlFormEntryForms))?.filter((form) => form !== null);
|
|
152
|
-
}
|