@openmrs/esm-patient-orders-app 11.3.0 → 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/.turbo/turbo-build.log +26 -23
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/1253.js +1 -0
- package/dist/1253.js.map +1 -0
- package/dist/1268.js +2 -0
- package/dist/1268.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/3685.js +1 -1
- package/dist/375.js +1 -0
- package/dist/375.js.map +1 -0
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4341.js +1 -0
- package/dist/4341.js.map +1 -0
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4687.js +1 -0
- package/dist/4687.js.map +1 -0
- package/dist/4937.js +1 -1
- package/dist/4937.js.map +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/5670.js +1 -0
- package/dist/5670.js.map +1 -0
- package/dist/6022.js +1 -1
- package/dist/6336.js +1 -0
- package/dist/6336.js.map +1 -0
- package/dist/6364.js +1 -0
- package/dist/6364.js.map +1 -0
- package/dist/6411.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6473.js +1 -0
- package/dist/6473.js.map +1 -0
- package/dist/6542.js +1 -1
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/7657.js +2 -0
- package/dist/7657.js.map +1 -0
- package/dist/795.js +1 -1
- package/dist/8154.js +1 -1
- package/dist/8154.js.map +1 -1
- package/dist/8163.js +1 -1
- package/dist/8349.js +1 -1
- package/dist/8416.js +1 -0
- package/dist/8416.js.map +1 -0
- package/dist/8618.js +1 -1
- package/dist/890.js +1 -1
- package/dist/9214.js +1 -1
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-orders-app.js +1 -1
- package/dist/openmrs-esm-patient-orders-app.js.buildmanifest.json +307 -330
- package/dist/openmrs-esm-patient-orders-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +5 -4
- package/src/api/api.ts +16 -30
- package/src/components/general-order-table.scss +4 -1
- package/src/components/order-details-table.component.tsx +88 -54
- package/src/components/test-order.scss +4 -2
- package/src/dashboard.meta.ts +3 -1
- package/src/index.ts +12 -9
- package/src/lab-results/{lab-results-form.component.tsx → exported-lab-results-form.workspace.tsx} +68 -67
- package/src/lab-results/lab-results-form-field.component.tsx +8 -5
- package/src/lab-results/lab-results-form.test.tsx +23 -21
- package/src/lab-results/lab-results-form.workspace.tsx +25 -0
- package/src/lab-results/print-results/print-modal/print-results-modal.tsx +5 -1
- package/src/order-basket/exported-order-basket.workspace.tsx +54 -0
- package/src/order-basket/general-order-type/{orderable-concept-search/orderable-concept-search.workspace.tsx → add-general-order/add-general-order.component.tsx} +89 -80
- package/src/order-basket/general-order-type/add-general-order/add-general-order.workspace.tsx +35 -0
- package/src/order-basket/general-order-type/add-general-order/exported-add-general-order.workspace.tsx +32 -0
- package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/search-results.component.tsx +21 -15
- package/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx +70 -23
- package/src/order-basket/general-order-type/{general-order-type.component.tsx → general-order-panel.component.tsx} +35 -52
- package/src/order-basket/general-order-type/resources.ts +4 -3
- package/src/order-basket/order-basket.component.tsx +213 -0
- package/src/order-basket/order-basket.workspace.tsx +35 -235
- package/src/order-basket-action-button/order-basket-action-button.component.tsx +35 -0
- package/src/order-basket-action-button/order-basket-action-button.test.tsx +28 -61
- package/src/order-cancellation-form/cancel-order-form.component.tsx +82 -85
- package/src/routes.json +19 -27
- package/src/utils/index.ts +15 -3
- package/translations/am.json +2 -0
- package/translations/ar.json +2 -0
- package/translations/ar_SY.json +2 -0
- package/translations/bn.json +2 -0
- package/translations/de.json +2 -0
- package/translations/en.json +5 -9
- package/translations/en_US.json +2 -0
- package/translations/es.json +2 -0
- package/translations/es_MX.json +2 -0
- package/translations/fr.json +5 -3
- package/translations/he.json +2 -0
- package/translations/hi.json +2 -0
- package/translations/hi_IN.json +2 -0
- package/translations/id.json +2 -0
- package/translations/it.json +8 -6
- package/translations/ka.json +2 -0
- package/translations/km.json +2 -0
- package/translations/ku.json +2 -0
- package/translations/ky.json +2 -0
- package/translations/lg.json +2 -0
- package/translations/ne.json +2 -0
- package/translations/pl.json +2 -0
- package/translations/pt.json +2 -0
- package/translations/pt_BR.json +2 -0
- package/translations/qu.json +2 -0
- package/translations/ro_RO.json +2 -0
- package/translations/ru_RU.json +2 -0
- package/translations/si.json +2 -0
- package/translations/sw.json +2 -0
- package/translations/sw_KE.json +2 -0
- package/translations/tr.json +2 -0
- package/translations/tr_TR.json +2 -0
- package/translations/uk.json +2 -0
- package/translations/uz.json +2 -0
- package/translations/uz@Latn.json +2 -0
- package/translations/uz_UZ.json +2 -0
- package/translations/vi.json +2 -0
- package/translations/zh.json +2 -0
- package/translations/zh_CN.json +2 -0
- package/dist/1571.js +0 -1
- package/dist/1571.js.map +0 -1
- package/dist/2537.js +0 -1
- package/dist/2537.js.map +0 -1
- package/dist/4051.js +0 -1
- package/dist/4051.js.map +0 -1
- package/dist/4918.js +0 -1
- package/dist/4918.js.map +0 -1
- package/dist/5048.js +0 -1
- package/dist/5048.js.map +0 -1
- package/dist/6432.js +0 -1
- package/dist/6432.js.map +0 -1
- package/dist/717.js +0 -1
- package/dist/717.js.map +0 -1
- package/dist/7202.js +0 -1
- package/dist/7202.js.map +0 -1
- package/dist/7337.js +0 -2
- package/dist/7337.js.map +0 -1
- package/dist/7817.js +0 -2
- package/dist/7817.js.map +0 -1
- package/dist/8625.js +0 -1
- package/dist/8625.js.map +0 -1
- package/dist/8960.js +0 -1
- package/dist/8960.js.map +0 -1
- package/src/order-basket-action-button/order-basket-action-button.extension.tsx +0 -23
- /package/dist/{7817.js.LICENSE.txt → 1268.js.LICENSE.txt} +0 -0
- /package/dist/{7337.js.LICENSE.txt → 7657.js.LICENSE.txt} +0 -0
- /package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/orderable-concept-search.scss +0 -0
- /package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/search-results.scss +0 -0
package/src/lab-results/{lab-results-form.component.tsx → exported-lab-results-form.workspace.tsx}
RENAMED
|
@@ -5,8 +5,15 @@ import { type Control, useForm } from 'react-hook-form';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { useSWRConfig } from 'swr';
|
|
7
7
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
restBaseUrl,
|
|
10
|
+
showSnackbar,
|
|
11
|
+
useAbortController,
|
|
12
|
+
useLayoutType,
|
|
13
|
+
Workspace2,
|
|
14
|
+
type Workspace2DefinitionProps,
|
|
15
|
+
} from '@openmrs/esm-framework';
|
|
16
|
+
import { type Order, type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
|
|
10
17
|
import { type ObservationValue } from '../types/encounter';
|
|
11
18
|
import {
|
|
12
19
|
createObservationPayload,
|
|
@@ -24,21 +31,17 @@ import { createLabResultsFormSchema } from './lab-results-schema.resource';
|
|
|
24
31
|
import ResultFormField from './lab-results-form-field.component';
|
|
25
32
|
import styles from './lab-results-form.scss';
|
|
26
33
|
|
|
27
|
-
export interface LabResultsFormProps
|
|
34
|
+
export interface LabResultsFormProps {
|
|
28
35
|
order: Order;
|
|
36
|
+
/** Callback to refresh lab orders in the Laboratory app after results are saved.
|
|
37
|
+
* This ensures the orders list stays in sync across the different tabs in the Laboratory app.
|
|
38
|
+
* @see https://github.com/openmrs/openmrs-esm-laboratory-app/pull/117 */
|
|
29
39
|
invalidateLabOrders?: () => void;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
const
|
|
42
|
+
const ExportedLabResultsForm: React.FC<Workspace2DefinitionProps<LabResultsFormProps, {}, {}>> = ({
|
|
43
|
+
workspaceProps: { order, invalidateLabOrders },
|
|
33
44
|
closeWorkspace,
|
|
34
|
-
closeWorkspaceWithSavedChanges,
|
|
35
|
-
order,
|
|
36
|
-
promptBeforeClosing,
|
|
37
|
-
/* Callback to refresh lab orders in the Laboratory app after results are saved.
|
|
38
|
-
* This ensures the orders list stays in sync across the different tabs in the Laboratory app.
|
|
39
|
-
* @see https://github.com/openmrs/openmrs-esm-laboratory-app/pull/117
|
|
40
|
-
*/
|
|
41
|
-
invalidateLabOrders,
|
|
42
45
|
}) => {
|
|
43
46
|
const { t } = useTranslation();
|
|
44
47
|
const abortController = useAbortController();
|
|
@@ -93,10 +96,6 @@ const LabResultsForm: React.FC<LabResultsFormProps> = ({
|
|
|
93
96
|
}
|
|
94
97
|
}, [concept, completeLabResult, order, setValue]);
|
|
95
98
|
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
promptBeforeClosing(() => isDirty);
|
|
98
|
-
}, [isDirty, promptBeforeClosing]);
|
|
99
|
-
|
|
100
99
|
if (isLoadingConcepts) {
|
|
101
100
|
return (
|
|
102
101
|
<div className={styles.loaderContainer}>
|
|
@@ -147,7 +146,7 @@ const LabResultsForm: React.FC<LabResultsFormProps> = ({
|
|
|
147
146
|
if (failedObsconceptUuids.length) {
|
|
148
147
|
showNotification('error', 'Could not save obs with concept uuids ' + failedObsconceptUuids.join(', '));
|
|
149
148
|
} else {
|
|
150
|
-
|
|
149
|
+
closeWorkspace({ discardUnsavedChanges: true });
|
|
151
150
|
showNotification(
|
|
152
151
|
'success',
|
|
153
152
|
t('successfullySavedLabResults', 'Lab results for {{orderNumber}} have been successfully updated', {
|
|
@@ -188,7 +187,7 @@ const LabResultsForm: React.FC<LabResultsFormProps> = ({
|
|
|
188
187
|
abortController,
|
|
189
188
|
);
|
|
190
189
|
|
|
191
|
-
|
|
190
|
+
closeWorkspace({ discardUnsavedChanges: true });
|
|
192
191
|
mutateOrderData();
|
|
193
192
|
mutateResults();
|
|
194
193
|
invalidateLabOrders?.();
|
|
@@ -207,57 +206,59 @@ const LabResultsForm: React.FC<LabResultsFormProps> = ({
|
|
|
207
206
|
};
|
|
208
207
|
|
|
209
208
|
return (
|
|
210
|
-
<
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
{
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
209
|
+
<Workspace2 title={t('enterTestResults', 'Enter test results')} hasUnsavedChanges={isDirty}>
|
|
210
|
+
<Form className={styles.form} onSubmit={handleSubmit(saveLabResults)}>
|
|
211
|
+
<Layer level={isTablet ? 1 : 0}>
|
|
212
|
+
<div className={styles.grid}>
|
|
213
|
+
{concept && (
|
|
214
|
+
<Stack gap={5}>
|
|
215
|
+
{!isLoading ? (
|
|
216
|
+
<ResultFormField
|
|
217
|
+
defaultValue={completeLabResult}
|
|
218
|
+
concept={concept}
|
|
219
|
+
control={control as unknown as Control<Record<string, unknown>>}
|
|
220
|
+
/>
|
|
221
|
+
) : (
|
|
222
|
+
<InlineLoading description={t('loadingInitialValues', 'Loading initial values') + '...'} />
|
|
223
|
+
)}
|
|
224
|
+
</Stack>
|
|
225
|
+
)}
|
|
226
|
+
{showEmptyFormErrorNotification && (
|
|
227
|
+
<InlineNotification
|
|
228
|
+
className={styles.emptyFormError}
|
|
229
|
+
lowContrast
|
|
230
|
+
title={t('error', 'Error')}
|
|
231
|
+
subtitle={t('pleaseFillField', 'Please fill at least one field') + '.'}
|
|
232
|
+
/>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
</Layer>
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
>
|
|
243
|
-
<Button className={styles.button} kind="secondary" disabled={isSubmitting} onClick={() => closeWorkspace()}>
|
|
244
|
-
{t('discard', 'Discard')}
|
|
245
|
-
</Button>
|
|
246
|
-
<Button
|
|
247
|
-
className={styles.button}
|
|
248
|
-
kind="primary"
|
|
249
|
-
disabled={isSubmitting || Object.keys(errors).length > 0}
|
|
250
|
-
type="submit"
|
|
237
|
+
<ButtonSet
|
|
238
|
+
className={classNames({
|
|
239
|
+
[styles.tablet]: isTablet,
|
|
240
|
+
[styles.desktop]: !isTablet,
|
|
241
|
+
})}
|
|
251
242
|
>
|
|
252
|
-
{isSubmitting
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
243
|
+
<Button className={styles.button} kind="secondary" disabled={isSubmitting} onClick={() => closeWorkspace()}>
|
|
244
|
+
{t('discard', 'Discard')}
|
|
245
|
+
</Button>
|
|
246
|
+
<Button
|
|
247
|
+
className={styles.button}
|
|
248
|
+
kind="primary"
|
|
249
|
+
disabled={isSubmitting || Object.keys(errors).length > 0}
|
|
250
|
+
type="submit"
|
|
251
|
+
>
|
|
252
|
+
{isSubmitting ? (
|
|
253
|
+
<InlineLoading description={t('saving', 'Saving') + '...'} />
|
|
254
|
+
) : (
|
|
255
|
+
t('saveAndClose', 'Save and close')
|
|
256
|
+
)}
|
|
257
|
+
</Button>
|
|
258
|
+
</ButtonSet>
|
|
259
|
+
</Form>
|
|
260
|
+
</Workspace2>
|
|
260
261
|
);
|
|
261
262
|
};
|
|
262
263
|
|
|
263
|
-
export default
|
|
264
|
+
export default ExportedLabResultsForm;
|
|
@@ -2,18 +2,18 @@ import React, { useCallback, useMemo } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Accordion,
|
|
4
4
|
AccordionItem,
|
|
5
|
+
InlineNotification,
|
|
5
6
|
NumberInput,
|
|
6
7
|
Select,
|
|
7
8
|
SelectItem,
|
|
8
9
|
TextInput,
|
|
9
|
-
InlineNotification,
|
|
10
10
|
} from '@carbon/react';
|
|
11
11
|
import { useTranslation } from 'react-i18next';
|
|
12
12
|
import { type Control, Controller } from 'react-hook-form';
|
|
13
|
+
import { useLayoutType } from '@openmrs/esm-framework';
|
|
13
14
|
import { isCoded, isNumeric, isPanel, isText, type LabOrderConcept } from './lab-results.resource';
|
|
14
15
|
import { type Observation } from '../types/encounter';
|
|
15
16
|
import styles from './lab-results-form.scss';
|
|
16
|
-
import { useLayoutType } from '@openmrs/esm-framework';
|
|
17
17
|
|
|
18
18
|
interface ResultFormFieldProps {
|
|
19
19
|
concept: LabOrderConcept;
|
|
@@ -65,7 +65,10 @@ const ResultFormField: React.FC<ResultFormFieldProps> = ({ concept, control, def
|
|
|
65
65
|
[defaultValue],
|
|
66
66
|
);
|
|
67
67
|
|
|
68
|
-
const labelText = useMemo(() =>
|
|
68
|
+
const labelText = useMemo(() => {
|
|
69
|
+
const displayText = concept.display?.trim() || concept.name?.name?.trim() || concept.name?.display?.trim() || '--';
|
|
70
|
+
return `${displayText} ${formatLabRange(concept)}`.trim();
|
|
71
|
+
}, [concept]);
|
|
69
72
|
|
|
70
73
|
const { isTextField, isNumericField, isCodedField, isPanelField } = useMemo(
|
|
71
74
|
() => ({
|
|
@@ -136,7 +139,7 @@ const ResultFormField: React.FC<ResultFormFieldProps> = ({ concept, control, def
|
|
|
136
139
|
/>
|
|
137
140
|
{isPanelField ? (
|
|
138
141
|
<Accordion>
|
|
139
|
-
<AccordionItem title={concept.display} open>
|
|
142
|
+
<AccordionItem title={concept.display || concept.name?.name || concept.name?.display || '--'} open>
|
|
140
143
|
{concept.setMembers.map((member) => (
|
|
141
144
|
<ResultFormField
|
|
142
145
|
key={member.uuid}
|
|
@@ -154,7 +157,7 @@ const ResultFormField: React.FC<ResultFormFieldProps> = ({ concept, control, def
|
|
|
154
157
|
|
|
155
158
|
return (
|
|
156
159
|
<div className={styles.formField}>
|
|
157
|
-
<
|
|
160
|
+
<span className={styles.label}>{labelText}</span>
|
|
158
161
|
<InlineNotification
|
|
159
162
|
kind="error"
|
|
160
163
|
hideCloseButton
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
type Datatype,
|
|
11
11
|
useCompletedLabResults,
|
|
12
12
|
} from './lab-results.resource';
|
|
13
|
-
import LabResultsForm from './lab-results-form.
|
|
14
|
-
import { type Order } from '@openmrs/esm-patient-common-lib';
|
|
13
|
+
import LabResultsForm, { type LabResultsFormProps } from './lab-results-form.workspace';
|
|
14
|
+
import { type PatientWorkspace2DefinitionProps, type Order } from '@openmrs/esm-patient-common-lib';
|
|
15
15
|
import { type Encounter } from '../types/encounter';
|
|
16
16
|
import { mockPatient } from 'tools';
|
|
17
17
|
|
|
@@ -39,14 +39,24 @@ const mockOrder = {
|
|
|
39
39
|
orderer: { uuid: 'orderer-uuid' },
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
const mockCloseWorkspace = jest.fn();
|
|
43
|
+
|
|
44
|
+
const testProps: PatientWorkspace2DefinitionProps<LabResultsFormProps, {}> = {
|
|
45
|
+
closeWorkspace: mockCloseWorkspace,
|
|
46
|
+
workspaceProps: {
|
|
47
|
+
order: mockOrder as Order,
|
|
48
|
+
},
|
|
49
|
+
groupProps: {
|
|
50
|
+
patientUuid: mockPatient.id,
|
|
51
|
+
patient: mockPatient,
|
|
52
|
+
visitContext: null,
|
|
53
|
+
mutateVisitContext: null,
|
|
54
|
+
},
|
|
55
|
+
launchChildWorkspace: jest.fn(),
|
|
56
|
+
windowProps: {},
|
|
57
|
+
workspaceName: '',
|
|
58
|
+
windowName: '',
|
|
59
|
+
isRootWorkspace: false,
|
|
50
60
|
};
|
|
51
61
|
|
|
52
62
|
describe('LabResultsForm', () => {
|
|
@@ -263,16 +273,8 @@ describe('LabResultsForm', () => {
|
|
|
263
273
|
|
|
264
274
|
test('submits form with valid data', async () => {
|
|
265
275
|
const user = userEvent.setup();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
render(
|
|
270
|
-
<LabResultsForm
|
|
271
|
-
{...testProps}
|
|
272
|
-
closeWorkspace={mockCloseWorkspace}
|
|
273
|
-
closeWorkspaceWithSavedChanges={mockCloseWorkspaceWithSavedChanges}
|
|
274
|
-
/>,
|
|
275
|
-
);
|
|
276
|
+
|
|
277
|
+
render(<LabResultsForm {...testProps} />);
|
|
276
278
|
|
|
277
279
|
const input = await screen.findByLabelText(`Test Concept (0 - 100 mg/dL)`);
|
|
278
280
|
await user.type(input, '50');
|
|
@@ -281,7 +283,7 @@ describe('LabResultsForm', () => {
|
|
|
281
283
|
await user.click(saveButton);
|
|
282
284
|
|
|
283
285
|
await waitFor(() => {
|
|
284
|
-
expect(
|
|
286
|
+
expect(mockCloseWorkspace).toHaveBeenCalled();
|
|
285
287
|
});
|
|
286
288
|
});
|
|
287
289
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Order, type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import ExportedLabResultsForm from './exported-lab-results-form.workspace';
|
|
4
|
+
|
|
5
|
+
export interface LabResultsFormProps {
|
|
6
|
+
order: Order;
|
|
7
|
+
/** Callback to refresh lab orders in the Laboratory app after results are saved.
|
|
8
|
+
* This ensures the orders list stays in sync across the different tabs in the Laboratory app.
|
|
9
|
+
* @see https://github.com/openmrs/openmrs-esm-laboratory-app/pull/117 */
|
|
10
|
+
invalidateLabOrders?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This workspace displays the form to input lab results for orders.
|
|
15
|
+
* This workspace should only be used within the patient chart. Use ExportedLabResultsForm
|
|
16
|
+
* for use cases outside the patient chart.
|
|
17
|
+
*/
|
|
18
|
+
const LabResultsForm: React.FC<PatientWorkspace2DefinitionProps<LabResultsFormProps, {}>> = ({
|
|
19
|
+
workspaceProps: { order, invalidateLabOrders },
|
|
20
|
+
...rest
|
|
21
|
+
}) => {
|
|
22
|
+
return <ExportedLabResultsForm workspaceProps={{ order, invalidateLabOrders }} {...rest} />;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default LabResultsForm;
|
|
@@ -65,7 +65,11 @@ const PrintResultsModal: React.FC<PrintResultsModalProps> = ({ orders, closeModa
|
|
|
65
65
|
<Checkbox
|
|
66
66
|
key={order.uuid}
|
|
67
67
|
id={order.uuid}
|
|
68
|
-
labelText={
|
|
68
|
+
labelText={
|
|
69
|
+
<span className={styles.checkboxLabel}>
|
|
70
|
+
{capitalize(order.concept.display || order.concept.name?.display || '--')}
|
|
71
|
+
</span>
|
|
72
|
+
}
|
|
69
73
|
checked={selectedOrders.has(order.uuid)}
|
|
70
74
|
onChange={(_, { checked }) => handleOrderSelection(order.uuid, checked)}
|
|
71
75
|
className={styles.checkboxItem}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
3
|
+
import {
|
|
4
|
+
type OrderBasketExtensionProps,
|
|
5
|
+
type OrderBasketItem,
|
|
6
|
+
type ExportedOrderBasketWindowProps,
|
|
7
|
+
} from '@openmrs/esm-patient-common-lib';
|
|
8
|
+
import OrderBasket from './order-basket.component';
|
|
9
|
+
|
|
10
|
+
const ExportedOrderBasketWorkspace: React.FC<Workspace2DefinitionProps<{}, ExportedOrderBasketWindowProps, {}>> = ({
|
|
11
|
+
windowProps: {
|
|
12
|
+
patientUuid,
|
|
13
|
+
patient,
|
|
14
|
+
visitContext,
|
|
15
|
+
mutateVisitContext,
|
|
16
|
+
drugOrderWorkspaceName,
|
|
17
|
+
labOrderWorkspaceName,
|
|
18
|
+
generalOrderWorkspaceName,
|
|
19
|
+
},
|
|
20
|
+
closeWorkspace,
|
|
21
|
+
launchChildWorkspace,
|
|
22
|
+
}) => {
|
|
23
|
+
const orderBasketExtensionProps = useMemo(() => {
|
|
24
|
+
const launchDrugOrderForm = (order: OrderBasketItem) => {
|
|
25
|
+
launchChildWorkspace(drugOrderWorkspaceName, { order });
|
|
26
|
+
};
|
|
27
|
+
const launchLabOrderForm = (orderTypeUuid: string, order: OrderBasketItem) => {
|
|
28
|
+
launchChildWorkspace(labOrderWorkspaceName, { orderTypeUuid, order });
|
|
29
|
+
};
|
|
30
|
+
const launchGeneralOrderForm = (orderTypeUuid: string, order: OrderBasketItem) => {
|
|
31
|
+
launchChildWorkspace(generalOrderWorkspaceName, { orderTypeUuid, order });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
patient,
|
|
36
|
+
launchDrugOrderForm,
|
|
37
|
+
launchLabOrderForm,
|
|
38
|
+
launchGeneralOrderForm,
|
|
39
|
+
} satisfies OrderBasketExtensionProps;
|
|
40
|
+
}, [launchChildWorkspace, drugOrderWorkspaceName, labOrderWorkspaceName, generalOrderWorkspaceName, patient]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<OrderBasket
|
|
44
|
+
patientUuid={patientUuid}
|
|
45
|
+
patient={patient}
|
|
46
|
+
visitContext={visitContext}
|
|
47
|
+
mutateVisitContext={mutateVisitContext}
|
|
48
|
+
closeWorkspace={closeWorkspace}
|
|
49
|
+
orderBasketExtensionProps={orderBasketExtensionProps}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default ExportedOrderBasketWorkspace;
|
|
@@ -1,67 +1,64 @@
|
|
|
1
|
-
import React, { type ComponentProps, useCallback,
|
|
1
|
+
import React, { type ComponentProps, useCallback, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { Button, Search } from '@carbon/react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import {
|
|
5
5
|
ArrowLeftIcon,
|
|
6
|
-
launchWorkspace,
|
|
7
6
|
ResponsiveWrapper,
|
|
8
7
|
useConfig,
|
|
9
8
|
useDebounce,
|
|
10
9
|
useLayoutType,
|
|
11
|
-
type
|
|
10
|
+
type Visit,
|
|
11
|
+
Workspace2,
|
|
12
|
+
type Workspace2DefinitionProps,
|
|
12
13
|
} from '@openmrs/esm-framework';
|
|
13
|
-
import {
|
|
14
|
-
type OrderBasketItem,
|
|
15
|
-
useOrderBasket,
|
|
16
|
-
useOrderType,
|
|
17
|
-
usePatientChartStore,
|
|
18
|
-
} from '@openmrs/esm-patient-common-lib';
|
|
14
|
+
import { type OrderBasketItem, useOrderBasket, useOrderType } from '@openmrs/esm-patient-common-lib';
|
|
19
15
|
import { OrderForm } from '../general-order-form/general-order-form.component';
|
|
20
16
|
import { prepOrderPostData } from '../resources';
|
|
21
17
|
import { type ConfigObject } from '../../../config-schema';
|
|
22
18
|
import OrderableConceptSearchResults from './search-results.component';
|
|
23
19
|
import styles from './orderable-concept-search.scss';
|
|
24
20
|
|
|
25
|
-
interface
|
|
26
|
-
|
|
21
|
+
interface AddGeneralOrderProps {
|
|
22
|
+
initialOrder: OrderBasketItem;
|
|
27
23
|
orderTypeUuid: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0';
|
|
33
|
-
|
|
34
|
-
type DrugsOrOrders = Pick<OrderBasketItem, 'action'>;
|
|
35
|
-
|
|
36
|
-
export function ordersEqual(order1: DrugsOrOrders, order2: DrugsOrOrders) {
|
|
37
|
-
return order1.action === order2.action;
|
|
24
|
+
patient: fhir.Patient;
|
|
25
|
+
visitContext: Visit;
|
|
26
|
+
closeWorkspace: Workspace2DefinitionProps['closeWorkspace'];
|
|
38
27
|
}
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
/**
|
|
30
|
+
* This workspace displays the drug order form for adding or editing a general order.
|
|
31
|
+
*/
|
|
32
|
+
const AddGeneralOrder: React.FC<AddGeneralOrderProps> = ({
|
|
33
|
+
initialOrder,
|
|
42
34
|
orderTypeUuid,
|
|
35
|
+
patient,
|
|
36
|
+
visitContext,
|
|
43
37
|
closeWorkspace,
|
|
44
|
-
closeWorkspaceWithSavedChanges,
|
|
45
|
-
promptBeforeClosing,
|
|
46
|
-
setTitle,
|
|
47
38
|
}) => {
|
|
48
39
|
const { t } = useTranslation();
|
|
49
40
|
const isTablet = useLayoutType() === 'tablet';
|
|
50
|
-
const { orders } = useOrderBasket<OrderBasketItem>(orderTypeUuid, prepOrderPostData);
|
|
51
|
-
const { patientUuid, patient } = usePatientChartStore();
|
|
41
|
+
const { orders } = useOrderBasket<OrderBasketItem>(patient, orderTypeUuid, prepOrderPostData);
|
|
52
42
|
const { orderTypes } = useConfig<ConfigObject>();
|
|
53
43
|
const [currentOrder, setCurrentOrder] = useState(initialOrder);
|
|
54
44
|
const { orderType } = useOrderType(orderTypeUuid);
|
|
45
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
55
46
|
|
|
56
|
-
|
|
47
|
+
const title = useMemo(() => {
|
|
57
48
|
if (orderType) {
|
|
58
|
-
|
|
59
|
-
t(`
|
|
49
|
+
if (initialOrder?.action == 'REVISE') {
|
|
50
|
+
return t(`editOrderableForOrderType`, 'Edit {{orderTypeDisplay}}', {
|
|
60
51
|
orderTypeDisplay: orderType.display.toLocaleLowerCase(),
|
|
61
|
-
})
|
|
62
|
-
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
return t(`addOrderableForOrderType`, 'Add {{orderTypeDisplay}}', {
|
|
55
|
+
orderTypeDisplay: orderType.display.toLocaleLowerCase(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
return '';
|
|
63
60
|
}
|
|
64
|
-
}, [orderType, t,
|
|
61
|
+
}, [orderType, t, initialOrder?.action]);
|
|
65
62
|
|
|
66
63
|
const orderableConceptSets = useMemo(
|
|
67
64
|
() => orderTypes.find((orderType) => orderType.orderTypeUuid === orderTypeUuid).orderableConceptSets,
|
|
@@ -69,10 +66,7 @@ const OrderableConceptSearchWorkspace: React.FC<OrderableConceptSearchWorkspaceP
|
|
|
69
66
|
);
|
|
70
67
|
|
|
71
68
|
const cancelDrugOrder = useCallback(() => {
|
|
72
|
-
closeWorkspace(
|
|
73
|
-
onWorkspaceClose: () => launchWorkspace('order-basket'),
|
|
74
|
-
closeWorkspaceGroup: false,
|
|
75
|
-
});
|
|
69
|
+
closeWorkspace();
|
|
76
70
|
}, [closeWorkspace]);
|
|
77
71
|
|
|
78
72
|
const openOrderForm = useCallback(
|
|
@@ -88,52 +82,61 @@ const OrderableConceptSearchWorkspace: React.FC<OrderableConceptSearchWorkspaceP
|
|
|
88
82
|
);
|
|
89
83
|
|
|
90
84
|
return (
|
|
91
|
-
<
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
</
|
|
85
|
+
<Workspace2 title={title} hasUnsavedChanges={hasUnsavedChanges}>
|
|
86
|
+
<div className={styles.workspaceWrapper}>
|
|
87
|
+
{!isTablet && (
|
|
88
|
+
<div className={styles.backButton}>
|
|
89
|
+
<Button
|
|
90
|
+
iconDescription="Return to order basket"
|
|
91
|
+
kind="ghost"
|
|
92
|
+
onClick={cancelDrugOrder}
|
|
93
|
+
renderIcon={(props: ComponentProps<typeof ArrowLeftIcon>) => <ArrowLeftIcon size={24} {...props} />}
|
|
94
|
+
size="sm"
|
|
95
|
+
>
|
|
96
|
+
<span>{t('backToOrderBasket', 'Back to order basket')}</span>
|
|
97
|
+
</Button>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
{currentOrder ? (
|
|
101
|
+
<OrderForm
|
|
102
|
+
initialOrder={currentOrder}
|
|
103
|
+
closeWorkspace={closeWorkspace}
|
|
104
|
+
setHasUnsavedChanges={setHasUnsavedChanges}
|
|
105
|
+
orderTypeUuid={orderTypeUuid}
|
|
106
|
+
patient={patient}
|
|
107
|
+
/>
|
|
108
|
+
) : (
|
|
109
|
+
<ConceptSearch
|
|
110
|
+
openOrderForm={openOrderForm}
|
|
111
|
+
closeWorkspace={closeWorkspace}
|
|
112
|
+
orderableConceptSets={orderableConceptSets}
|
|
113
|
+
orderTypeUuid={orderTypeUuid}
|
|
114
|
+
patient={patient}
|
|
115
|
+
visit={visitContext}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
</Workspace2>
|
|
126
120
|
);
|
|
127
121
|
};
|
|
128
122
|
|
|
129
123
|
interface ConceptSearchProps {
|
|
130
|
-
closeWorkspace:
|
|
124
|
+
closeWorkspace: Workspace2DefinitionProps['closeWorkspace'];
|
|
131
125
|
openOrderForm: (search: OrderBasketItem) => void;
|
|
132
126
|
orderTypeUuid: string;
|
|
133
127
|
orderableConceptSets: Array<string>;
|
|
128
|
+
patient: fhir.Patient;
|
|
129
|
+
visit: Visit;
|
|
134
130
|
}
|
|
135
131
|
|
|
136
|
-
function ConceptSearch({
|
|
132
|
+
function ConceptSearch({
|
|
133
|
+
closeWorkspace,
|
|
134
|
+
orderTypeUuid,
|
|
135
|
+
openOrderForm,
|
|
136
|
+
orderableConceptSets,
|
|
137
|
+
patient,
|
|
138
|
+
visit,
|
|
139
|
+
}: ConceptSearchProps) {
|
|
137
140
|
const { t } = useTranslation();
|
|
138
141
|
const { orderType } = useOrderType(orderTypeUuid);
|
|
139
142
|
const isTablet = useLayoutType() === 'tablet';
|
|
@@ -142,9 +145,7 @@ function ConceptSearch({ closeWorkspace, orderTypeUuid, openOrderForm, orderable
|
|
|
142
145
|
const searchInputRef = useRef(null);
|
|
143
146
|
|
|
144
147
|
const cancelDrugOrder = useCallback(() => {
|
|
145
|
-
closeWorkspace(
|
|
146
|
-
onWorkspaceClose: () => launchWorkspace('order-basket'),
|
|
147
|
-
});
|
|
148
|
+
closeWorkspace();
|
|
148
149
|
}, [closeWorkspace]);
|
|
149
150
|
|
|
150
151
|
const focusAndClearSearchInput = () => {
|
|
@@ -178,10 +179,12 @@ function ConceptSearch({ closeWorkspace, orderTypeUuid, openOrderForm, orderable
|
|
|
178
179
|
searchTerm={debouncedSearchTerm}
|
|
179
180
|
openOrderForm={openOrderForm}
|
|
180
181
|
focusAndClearSearchInput={focusAndClearSearchInput}
|
|
181
|
-
closeWorkspace={closeWorkspace}
|
|
182
182
|
orderTypeUuid={orderTypeUuid}
|
|
183
183
|
cancelOrder={() => {}}
|
|
184
184
|
orderableConceptSets={orderableConceptSets}
|
|
185
|
+
closeWorkspace={closeWorkspace}
|
|
186
|
+
patient={patient}
|
|
187
|
+
visit={visit}
|
|
185
188
|
/>
|
|
186
189
|
{isTablet && (
|
|
187
190
|
<div className={styles.separatorContainer}>
|
|
@@ -195,4 +198,10 @@ function ConceptSearch({ closeWorkspace, orderTypeUuid, openOrderForm, orderable
|
|
|
195
198
|
);
|
|
196
199
|
}
|
|
197
200
|
|
|
198
|
-
|
|
201
|
+
type DrugsOrOrders = Pick<OrderBasketItem, 'action' | 'concept'>;
|
|
202
|
+
|
|
203
|
+
function ordersEqual(order1: DrugsOrOrders, order2: DrugsOrOrders) {
|
|
204
|
+
return order1.action === order2.action && order1.concept.uuid === order2.concept.uuid;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default AddGeneralOrder;
|