@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.
Files changed (170) hide show
  1. package/.turbo/turbo-build.log +26 -23
  2. package/dist/1119.js +1 -1
  3. package/dist/1197.js +1 -1
  4. package/dist/1253.js +1 -0
  5. package/dist/1253.js.map +1 -0
  6. package/dist/1268.js +2 -0
  7. package/dist/1268.js.map +1 -0
  8. package/dist/2146.js +1 -1
  9. package/dist/2690.js +1 -1
  10. package/dist/3099.js +1 -1
  11. package/dist/3584.js +1 -1
  12. package/dist/3685.js +1 -1
  13. package/dist/375.js +1 -0
  14. package/dist/375.js.map +1 -0
  15. package/dist/4055.js +1 -1
  16. package/dist/4132.js +1 -1
  17. package/dist/4300.js +1 -1
  18. package/dist/4335.js +1 -1
  19. package/dist/4341.js +1 -0
  20. package/dist/4341.js.map +1 -0
  21. package/dist/4618.js +1 -1
  22. package/dist/4652.js +1 -1
  23. package/dist/4687.js +1 -0
  24. package/dist/4687.js.map +1 -0
  25. package/dist/4937.js +1 -1
  26. package/dist/4937.js.map +1 -1
  27. package/dist/4944.js +1 -1
  28. package/dist/5173.js +1 -1
  29. package/dist/5241.js +1 -1
  30. package/dist/5442.js +1 -1
  31. package/dist/5661.js +1 -1
  32. package/dist/5670.js +1 -0
  33. package/dist/5670.js.map +1 -0
  34. package/dist/6022.js +1 -1
  35. package/dist/6336.js +1 -0
  36. package/dist/6336.js.map +1 -0
  37. package/dist/6364.js +1 -0
  38. package/dist/6364.js.map +1 -0
  39. package/dist/6411.js +1 -1
  40. package/dist/6468.js +1 -1
  41. package/dist/6473.js +1 -0
  42. package/dist/6473.js.map +1 -0
  43. package/dist/6542.js +1 -1
  44. package/dist/6679.js +1 -1
  45. package/dist/6840.js +1 -1
  46. package/dist/6859.js +1 -1
  47. package/dist/7097.js +1 -1
  48. package/dist/7159.js +1 -1
  49. package/dist/723.js +1 -1
  50. package/dist/7617.js +1 -1
  51. package/dist/7657.js +2 -0
  52. package/dist/7657.js.map +1 -0
  53. package/dist/795.js +1 -1
  54. package/dist/8154.js +1 -1
  55. package/dist/8154.js.map +1 -1
  56. package/dist/8163.js +1 -1
  57. package/dist/8349.js +1 -1
  58. package/dist/8416.js +1 -0
  59. package/dist/8416.js.map +1 -0
  60. package/dist/8618.js +1 -1
  61. package/dist/890.js +1 -1
  62. package/dist/9214.js +1 -1
  63. package/dist/9538.js +1 -1
  64. package/dist/9569.js +1 -1
  65. package/dist/986.js +1 -1
  66. package/dist/9879.js +1 -1
  67. package/dist/9895.js +1 -1
  68. package/dist/9900.js +1 -1
  69. package/dist/9913.js +1 -1
  70. package/dist/main.js +1 -1
  71. package/dist/main.js.map +1 -1
  72. package/dist/openmrs-esm-patient-orders-app.js +1 -1
  73. package/dist/openmrs-esm-patient-orders-app.js.buildmanifest.json +307 -330
  74. package/dist/openmrs-esm-patient-orders-app.js.map +1 -1
  75. package/dist/routes.json +1 -1
  76. package/package.json +5 -4
  77. package/src/api/api.ts +16 -30
  78. package/src/components/general-order-table.scss +4 -1
  79. package/src/components/order-details-table.component.tsx +88 -54
  80. package/src/components/test-order.scss +4 -2
  81. package/src/dashboard.meta.ts +3 -1
  82. package/src/index.ts +12 -9
  83. package/src/lab-results/{lab-results-form.component.tsx → exported-lab-results-form.workspace.tsx} +68 -67
  84. package/src/lab-results/lab-results-form-field.component.tsx +8 -5
  85. package/src/lab-results/lab-results-form.test.tsx +23 -21
  86. package/src/lab-results/lab-results-form.workspace.tsx +25 -0
  87. package/src/lab-results/print-results/print-modal/print-results-modal.tsx +5 -1
  88. package/src/order-basket/exported-order-basket.workspace.tsx +54 -0
  89. 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
  90. package/src/order-basket/general-order-type/add-general-order/add-general-order.workspace.tsx +35 -0
  91. package/src/order-basket/general-order-type/add-general-order/exported-add-general-order.workspace.tsx +32 -0
  92. package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/search-results.component.tsx +21 -15
  93. package/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx +70 -23
  94. package/src/order-basket/general-order-type/{general-order-type.component.tsx → general-order-panel.component.tsx} +35 -52
  95. package/src/order-basket/general-order-type/resources.ts +4 -3
  96. package/src/order-basket/order-basket.component.tsx +213 -0
  97. package/src/order-basket/order-basket.workspace.tsx +35 -235
  98. package/src/order-basket-action-button/order-basket-action-button.component.tsx +35 -0
  99. package/src/order-basket-action-button/order-basket-action-button.test.tsx +28 -61
  100. package/src/order-cancellation-form/cancel-order-form.component.tsx +82 -85
  101. package/src/routes.json +19 -27
  102. package/src/utils/index.ts +15 -3
  103. package/translations/am.json +2 -0
  104. package/translations/ar.json +2 -0
  105. package/translations/ar_SY.json +2 -0
  106. package/translations/bn.json +2 -0
  107. package/translations/de.json +2 -0
  108. package/translations/en.json +5 -9
  109. package/translations/en_US.json +2 -0
  110. package/translations/es.json +2 -0
  111. package/translations/es_MX.json +2 -0
  112. package/translations/fr.json +5 -3
  113. package/translations/he.json +2 -0
  114. package/translations/hi.json +2 -0
  115. package/translations/hi_IN.json +2 -0
  116. package/translations/id.json +2 -0
  117. package/translations/it.json +8 -6
  118. package/translations/ka.json +2 -0
  119. package/translations/km.json +2 -0
  120. package/translations/ku.json +2 -0
  121. package/translations/ky.json +2 -0
  122. package/translations/lg.json +2 -0
  123. package/translations/ne.json +2 -0
  124. package/translations/pl.json +2 -0
  125. package/translations/pt.json +2 -0
  126. package/translations/pt_BR.json +2 -0
  127. package/translations/qu.json +2 -0
  128. package/translations/ro_RO.json +2 -0
  129. package/translations/ru_RU.json +2 -0
  130. package/translations/si.json +2 -0
  131. package/translations/sw.json +2 -0
  132. package/translations/sw_KE.json +2 -0
  133. package/translations/tr.json +2 -0
  134. package/translations/tr_TR.json +2 -0
  135. package/translations/uk.json +2 -0
  136. package/translations/uz.json +2 -0
  137. package/translations/uz@Latn.json +2 -0
  138. package/translations/uz_UZ.json +2 -0
  139. package/translations/vi.json +2 -0
  140. package/translations/zh.json +2 -0
  141. package/translations/zh_CN.json +2 -0
  142. package/dist/1571.js +0 -1
  143. package/dist/1571.js.map +0 -1
  144. package/dist/2537.js +0 -1
  145. package/dist/2537.js.map +0 -1
  146. package/dist/4051.js +0 -1
  147. package/dist/4051.js.map +0 -1
  148. package/dist/4918.js +0 -1
  149. package/dist/4918.js.map +0 -1
  150. package/dist/5048.js +0 -1
  151. package/dist/5048.js.map +0 -1
  152. package/dist/6432.js +0 -1
  153. package/dist/6432.js.map +0 -1
  154. package/dist/717.js +0 -1
  155. package/dist/717.js.map +0 -1
  156. package/dist/7202.js +0 -1
  157. package/dist/7202.js.map +0 -1
  158. package/dist/7337.js +0 -2
  159. package/dist/7337.js.map +0 -1
  160. package/dist/7817.js +0 -2
  161. package/dist/7817.js.map +0 -1
  162. package/dist/8625.js +0 -1
  163. package/dist/8625.js.map +0 -1
  164. package/dist/8960.js +0 -1
  165. package/dist/8960.js.map +0 -1
  166. package/src/order-basket-action-button/order-basket-action-button.extension.tsx +0 -23
  167. /package/dist/{7817.js.LICENSE.txt → 1268.js.LICENSE.txt} +0 -0
  168. /package/dist/{7337.js.LICENSE.txt → 7657.js.LICENSE.txt} +0 -0
  169. /package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/orderable-concept-search.scss +0 -0
  170. /package/src/order-basket/general-order-type/{orderable-concept-search → add-general-order}/search-results.scss +0 -0
@@ -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 { restBaseUrl, showSnackbar, useAbortController, useLayoutType } from '@openmrs/esm-framework';
9
- import { type DefaultPatientWorkspaceProps, type Order } from '@openmrs/esm-patient-common-lib';
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 extends DefaultPatientWorkspaceProps {
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 LabResultsForm: React.FC<LabResultsFormProps> = ({
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
- closeWorkspaceWithSavedChanges();
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
- closeWorkspaceWithSavedChanges();
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
- <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>
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
- <ButtonSet
238
- className={classNames({
239
- [styles.tablet]: isTablet,
240
- [styles.desktop]: !isTablet,
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
- <InlineLoading description={t('saving', 'Saving') + '...'} />
254
- ) : (
255
- t('saveAndClose', 'Save and close')
256
- )}
257
- </Button>
258
- </ButtonSet>
259
- </Form>
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 LabResultsForm;
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(() => `${concept.display} ${formatLabRange(concept)}`, [concept]);
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
- <label className={styles.label}>{labelText}</label>
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.component';
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 testProps = {
43
- closeWorkspace: jest.fn(),
44
- closeWorkspaceWithSavedChanges: jest.fn(),
45
- order: mockOrder as Order,
46
- promptBeforeClosing: jest.fn(),
47
- setTitle: jest.fn(),
48
- patientUuid: mockPatient.id,
49
- patient: mockPatient,
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
- const mockCloseWorkspace = jest.fn();
267
- const mockCloseWorkspaceWithSavedChanges = jest.fn();
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(mockCloseWorkspaceWithSavedChanges).toHaveBeenCalled();
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={<span className={styles.checkboxLabel}>{capitalize(order.concept.display)}</span>}
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, useEffect, useMemo, useRef, useState } from 'react';
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 DefaultWorkspaceProps,
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 OrderableConceptSearchWorkspaceProps extends DefaultWorkspaceProps {
26
- order: OrderBasketItem;
21
+ interface AddGeneralOrderProps {
22
+ initialOrder: OrderBasketItem;
27
23
  orderTypeUuid: string;
28
- orderableConceptClasses: Array<string>;
29
- orderableConceptSets: Array<string>;
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
- const OrderableConceptSearchWorkspace: React.FC<OrderableConceptSearchWorkspaceProps> = ({
41
- order: initialOrder,
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
- useEffect(() => {
47
+ const title = useMemo(() => {
57
48
  if (orderType) {
58
- setTitle(
59
- t(`addOrderableForOrderType`, 'Add {{orderTypeDisplay}}', {
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, setTitle]);
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
- <div className={styles.workspaceWrapper}>
92
- {!isTablet && (
93
- <div className={styles.backButton}>
94
- <Button
95
- iconDescription="Return to order basket"
96
- kind="ghost"
97
- onClick={cancelDrugOrder}
98
- renderIcon={(props: ComponentProps<typeof ArrowLeftIcon>) => <ArrowLeftIcon size={24} {...props} />}
99
- size="sm"
100
- >
101
- <span>{t('backToOrderBasket', 'Back to order basket')}</span>
102
- </Button>
103
- </div>
104
- )}
105
- {currentOrder ? (
106
- <OrderForm
107
- initialOrder={currentOrder}
108
- closeWorkspace={closeWorkspace}
109
- closeWorkspaceWithSavedChanges={closeWorkspaceWithSavedChanges}
110
- promptBeforeClosing={promptBeforeClosing}
111
- orderTypeUuid={orderTypeUuid}
112
- orderableConceptSets={orderableConceptSets}
113
- patientUuid={patientUuid}
114
- patient={patient}
115
- setTitle={() => {}}
116
- />
117
- ) : (
118
- <ConceptSearch
119
- openOrderForm={openOrderForm}
120
- closeWorkspace={closeWorkspace}
121
- orderableConceptSets={orderableConceptSets}
122
- orderTypeUuid={orderTypeUuid}
123
- />
124
- )}
125
- </div>
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: DefaultWorkspaceProps['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({ closeWorkspace, orderTypeUuid, openOrderForm, orderableConceptSets }: ConceptSearchProps) {
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
- export default OrderableConceptSearchWorkspace;
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;