@openmrs/esm-patient-medications-app 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.
Files changed (153) hide show
  1. package/.turbo/turbo-build.log +25 -27
  2. package/dist/1022.js +1 -0
  3. package/dist/1022.js.map +1 -0
  4. package/dist/1119.js +1 -1
  5. package/dist/1197.js +1 -1
  6. package/dist/1268.js +2 -0
  7. package/dist/1268.js.map +1 -0
  8. package/dist/1780.js +1 -0
  9. package/dist/1780.js.map +1 -0
  10. package/dist/2146.js +1 -1
  11. package/dist/2690.js +1 -1
  12. package/dist/3099.js +1 -1
  13. package/dist/3338.js +2 -0
  14. package/dist/3338.js.map +1 -0
  15. package/dist/3584.js +1 -1
  16. package/dist/4055.js +1 -1
  17. package/dist/4132.js +1 -1
  18. package/dist/4300.js +1 -1
  19. package/dist/4335.js +1 -1
  20. package/dist/4341.js +1 -0
  21. package/dist/4341.js.map +1 -0
  22. package/dist/4618.js +1 -1
  23. package/dist/4652.js +1 -1
  24. package/dist/4944.js +1 -1
  25. package/dist/4953.js +1 -0
  26. package/dist/4953.js.map +1 -0
  27. package/dist/5173.js +1 -1
  28. package/dist/5241.js +1 -1
  29. package/dist/5442.js +1 -1
  30. package/dist/5661.js +1 -1
  31. package/dist/5670.js +1 -1
  32. package/dist/5670.js.map +1 -1
  33. package/dist/6022.js +1 -1
  34. package/dist/6336.js +1 -0
  35. package/dist/6336.js.map +1 -0
  36. package/dist/6468.js +1 -1
  37. package/dist/6679.js +1 -1
  38. package/dist/6840.js +1 -1
  39. package/dist/6859.js +1 -1
  40. package/dist/7097.js +1 -1
  41. package/dist/7159.js +1 -1
  42. package/dist/723.js +1 -1
  43. package/dist/7617.js +1 -1
  44. package/dist/795.js +1 -1
  45. package/dist/8163.js +1 -1
  46. package/dist/8349.js +1 -1
  47. package/dist/8437.js +1 -0
  48. package/dist/8437.js.map +1 -0
  49. package/dist/8618.js +1 -1
  50. package/dist/8812.js +1 -1
  51. package/dist/8812.js.map +1 -1
  52. package/dist/890.js +1 -1
  53. package/dist/9214.js +1 -1
  54. package/dist/9538.js +1 -1
  55. package/dist/9569.js +1 -1
  56. package/dist/986.js +1 -1
  57. package/dist/9879.js +1 -1
  58. package/dist/9895.js +1 -1
  59. package/dist/9900.js +1 -1
  60. package/dist/9913.js +1 -1
  61. package/dist/main.js +1 -1
  62. package/dist/main.js.map +1 -1
  63. package/dist/openmrs-esm-patient-medications-app.js +1 -1
  64. package/dist/openmrs-esm-patient-medications-app.js.buildmanifest.json +324 -296
  65. package/dist/openmrs-esm-patient-medications-app.js.map +1 -1
  66. package/dist/routes.json +1 -1
  67. package/package.json +4 -4
  68. package/src/active-medications/active-medications.component.tsx +8 -2
  69. package/src/active-medications/active-medications.test.tsx +6 -11
  70. package/src/add-drug-order/add-drug-order.component.tsx +168 -0
  71. package/src/add-drug-order/add-drug-order.scss +6 -0
  72. package/src/add-drug-order/add-drug-order.test.tsx +28 -17
  73. package/src/add-drug-order/add-drug-order.workspace.tsx +25 -94
  74. package/src/add-drug-order/drug-order-form.component.tsx +193 -230
  75. package/src/add-drug-order/drug-order-form.resource.ts +212 -0
  76. package/src/add-drug-order/drug-order-form.scss +11 -7
  77. package/src/add-drug-order/drug-search/drug-search-combobox.component.tsx +55 -0
  78. package/src/add-drug-order/drug-search/drug-search-combobox.test.tsx +56 -0
  79. package/src/add-drug-order/drug-search/drug-search.component.tsx +11 -11
  80. package/src/add-drug-order/drug-search/drug-search.resource.tsx +82 -19
  81. package/src/add-drug-order/drug-search/helpers.ts +1 -1
  82. package/src/add-drug-order/drug-search/order-basket-search-results.component.tsx +45 -19
  83. package/src/add-drug-order/exported-add-drug-order.workspace.tsx +30 -0
  84. package/src/add-drug-order/fill-prescription-form.workspace.tsx +112 -0
  85. package/src/api/api.ts +25 -4
  86. package/src/api/order-config.ts +3 -3
  87. package/src/components/medications-details-table.component.tsx +57 -18
  88. package/src/config-schema.ts +15 -2
  89. package/src/drug-order-basket-panel/drug-order-basket-panel.extension.tsx +22 -33
  90. package/src/drug-order-basket-panel/drug-order-basket-panel.test.tsx +13 -6
  91. package/src/drug-order-basket-panel/order-basket-item-tile.component.tsx +1 -1
  92. package/src/index.ts +9 -1
  93. package/src/medications-summary/medications-summary.component.tsx +5 -3
  94. package/src/past-medications/past-medications.component.tsx +44 -0
  95. package/src/past-medications/past-medications.scss +7 -0
  96. package/src/past-medications/past-medications.test.tsx +85 -0
  97. package/src/routes.json +46 -4
  98. package/translations/am.json +20 -4
  99. package/translations/ar.json +20 -4
  100. package/translations/ar_SY.json +20 -4
  101. package/translations/bn.json +20 -4
  102. package/translations/de.json +20 -4
  103. package/translations/en.json +22 -4
  104. package/translations/en_US.json +20 -4
  105. package/translations/es.json +29 -13
  106. package/translations/es_MX.json +20 -4
  107. package/translations/fr.json +26 -10
  108. package/translations/he.json +20 -4
  109. package/translations/hi.json +20 -4
  110. package/translations/hi_IN.json +20 -4
  111. package/translations/id.json +29 -13
  112. package/translations/it.json +29 -13
  113. package/translations/ka.json +27 -11
  114. package/translations/km.json +20 -4
  115. package/translations/ku.json +20 -4
  116. package/translations/ky.json +20 -4
  117. package/translations/lg.json +20 -4
  118. package/translations/ne.json +20 -4
  119. package/translations/pl.json +20 -4
  120. package/translations/pt.json +29 -13
  121. package/translations/pt_BR.json +29 -13
  122. package/translations/qu.json +20 -4
  123. package/translations/ro_RO.json +29 -13
  124. package/translations/ru_RU.json +20 -4
  125. package/translations/si.json +20 -4
  126. package/translations/sw.json +20 -4
  127. package/translations/sw_KE.json +20 -4
  128. package/translations/tr.json +20 -4
  129. package/translations/tr_TR.json +20 -4
  130. package/translations/uk.json +20 -4
  131. package/translations/uz.json +20 -4
  132. package/translations/uz@Latn.json +20 -4
  133. package/translations/uz_UZ.json +20 -4
  134. package/translations/vi.json +20 -4
  135. package/translations/zh.json +21 -5
  136. package/translations/zh_CN.json +29 -13
  137. package/dist/2537.js +0 -1
  138. package/dist/2537.js.map +0 -1
  139. package/dist/4918.js +0 -1
  140. package/dist/4918.js.map +0 -1
  141. package/dist/5837.js +0 -2
  142. package/dist/5837.js.map +0 -1
  143. package/dist/6783.js +0 -2
  144. package/dist/6783.js.map +0 -1
  145. package/dist/8803.js +0 -1
  146. package/dist/8803.js.map +0 -1
  147. package/dist/9365.js +0 -1
  148. package/dist/9365.js.map +0 -1
  149. package/dist/9647.js +0 -1
  150. package/dist/9647.js.map +0 -1
  151. package/src/types.ts +0 -75
  152. /package/dist/{6783.js.LICENSE.txt → 1268.js.LICENSE.txt} +0 -0
  153. /package/dist/{5837.js.LICENSE.txt → 3338.js.LICENSE.txt} +0 -0
@@ -3,15 +3,15 @@ import classNames from 'classnames';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Button, ButtonSkeleton, SkeletonText, Tile } from '@carbon/react';
5
5
  import { ShoppingCartArrowUp } from '@carbon/react/icons';
6
- import { useOrderBasket, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
6
+ import { type DrugOrderBasketItem, useOrderBasket } from '@openmrs/esm-patient-common-lib';
7
7
  import {
8
8
  ArrowRightIcon,
9
- closeWorkspace,
10
9
  ShoppingCartArrowDownIcon,
11
10
  useConfig,
12
11
  useLayoutType,
13
12
  UserHasAccess,
14
- launchWorkspace,
13
+ type Visit,
14
+ type Workspace2DefinitionProps,
15
15
  } from '@openmrs/esm-framework';
16
16
  import { type ConfigObject } from '../../config-schema';
17
17
  import { prepMedicationOrderPostData, useActivePatientOrders } from '../../api/api';
@@ -22,24 +22,32 @@ import {
22
22
  useDrugSearch,
23
23
  useDrugTemplate,
24
24
  } from './drug-search.resource';
25
- import type { DrugOrderBasketItem } from '../../types';
26
25
  import styles from './order-basket-search-results.scss';
27
26
 
28
27
  export interface OrderBasketSearchResultsProps {
28
+ patient: fhir.Patient;
29
29
  searchTerm: string;
30
+ closeWorkspace: Workspace2DefinitionProps['closeWorkspace'];
30
31
  openOrderForm: (searchResult: DrugOrderBasketItem) => void;
31
32
  focusAndClearSearchInput: () => void;
33
+ visit: Visit;
32
34
  }
33
35
 
34
36
  interface DrugSearchResultItemProps {
37
+ patient: fhir.Patient;
35
38
  drug: DrugSearchResult;
36
39
  openOrderForm: (searchResult: DrugOrderBasketItem) => void;
40
+ visit: Visit;
41
+ closeWorkspace: Workspace2DefinitionProps['closeWorkspace'];
37
42
  }
38
43
 
39
44
  export default function OrderBasketSearchResults({
40
45
  searchTerm,
41
46
  openOrderForm,
42
47
  focusAndClearSearchInput,
48
+ patient,
49
+ visit,
50
+ closeWorkspace,
43
51
  }: OrderBasketSearchResultsProps) {
44
52
  const { t } = useTranslation();
45
53
  const isTablet = useLayoutType() === 'tablet';
@@ -105,24 +113,45 @@ export default function OrderBasketSearchResults({
105
113
  </Button>
106
114
  </div>
107
115
  <div className={styles.resultsContainer}>
108
- {drugs?.map((drug) => <DrugSearchResultItem key={drug.uuid} drug={drug} openOrderForm={openOrderForm} />)}
116
+ {drugs?.map((drug) => (
117
+ <DrugSearchResultItem
118
+ key={drug.uuid}
119
+ patient={patient}
120
+ drug={drug}
121
+ openOrderForm={openOrderForm}
122
+ visit={visit}
123
+ closeWorkspace={closeWorkspace}
124
+ />
125
+ ))}
109
126
  </div>
110
127
  </div>
111
128
  );
112
129
  }
113
130
 
114
- const DrugSearchResultItem: React.FC<DrugSearchResultItemProps> = ({ drug, openOrderForm }) => {
131
+ const DrugSearchResultItem: React.FC<DrugSearchResultItemProps> = ({
132
+ patient,
133
+ drug,
134
+ openOrderForm,
135
+ visit,
136
+ closeWorkspace,
137
+ }) => {
115
138
  const isTablet = useLayoutType() === 'tablet';
116
- const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>('medications', prepMedicationOrderPostData);
117
- const { patientUuid } = usePatientChartStore();
139
+ const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>(
140
+ patient,
141
+ 'medications',
142
+ prepMedicationOrderPostData,
143
+ );
144
+ const patientUuid = patient.id;
118
145
  const { data: activeOrders, isLoading: isLoadingActiveOrders } = useActivePatientOrders(patientUuid);
119
146
  const drugAlreadyInBasket = useMemo(
120
- () => orders?.some((order) => ordersEqual(order, getTemplateOrderBasketItem(drug))),
121
- [orders, drug],
147
+ () => orders?.some((order) => ordersEqual(order, getTemplateOrderBasketItem(drug, visit))),
148
+ [orders, drug, visit],
122
149
  );
150
+ // TODO: use the backend instead of this to determine whether the drug formulation can be ordered
151
+ // See: https://openmrs.atlassian.net/browse/RESTWS-1003
123
152
  const drugAlreadyPrescribed = useMemo(
124
153
  () => activeOrders?.some((order) => order?.drug?.uuid === drug?.uuid),
125
- [activeOrders, drug],
154
+ [activeOrders, drug?.uuid],
126
155
  );
127
156
 
128
157
  const { templates, error: fetchingDrugOrderTemplatesError } = useDrugTemplate(drug?.uuid);
@@ -131,9 +160,9 @@ const DrugSearchResultItem: React.FC<DrugSearchResultItemProps> = ({ drug, openO
131
160
  const drugItemTemplateOptions: Array<DrugOrderBasketItem> = useMemo(
132
161
  () =>
133
162
  templates?.length
134
- ? templates.map((template) => getTemplateOrderBasketItem(drug, config?.daysDurationUnit, template))
135
- : [getTemplateOrderBasketItem(drug, config?.daysDurationUnit)],
136
- [templates, drug, config?.daysDurationUnit],
163
+ ? templates.map((template) => getTemplateOrderBasketItem(drug, visit, config?.daysDurationUnit, template))
164
+ : [getTemplateOrderBasketItem(drug, visit, config?.daysDurationUnit)],
165
+ [templates, drug, config?.daysDurationUnit, visit],
137
166
  );
138
167
 
139
168
  const addToBasket = useCallback(
@@ -141,12 +170,9 @@ const DrugSearchResultItem: React.FC<DrugSearchResultItemProps> = ({ drug, openO
141
170
  // Directly adding the order to basket should be marked as incomplete
142
171
  searchResult.isOrderIncomplete = true;
143
172
  setOrders([...orders, searchResult]);
144
- closeWorkspace('add-drug-order', {
145
- ignoreChanges: true,
146
- onWorkspaceClose: () => launchWorkspace('order-basket'),
147
- });
173
+ closeWorkspace();
148
174
  },
149
- [orders, setOrders],
175
+ [orders, setOrders, closeWorkspace],
150
176
  );
151
177
 
152
178
  const removeFromBasket = useCallback(
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { type Workspace2DefinitionProps } from '@openmrs/esm-framework';
3
+ import { type ExportedOrderBasketWindowProps, type DrugOrderBasketItem } from '@openmrs/esm-patient-common-lib';
4
+ import AddDrugOrder from './add-drug-order.component';
5
+
6
+ export interface AddDrugOrderWorkspaceAdditionalProps {
7
+ order: DrugOrderBasketItem;
8
+ }
9
+
10
+ /**
11
+ * This workspace displays the drug order form for adding or editing a drug order.
12
+ * On form submission, it saves the drug order to the (frontend) order basket.
13
+ * For a form that submits the drug order directly on submit,
14
+ * see fill-prescription-form.workspace.tsx
15
+ */
16
+ export default function ExportedAddDrugOrderWorkspace({
17
+ workspaceProps: { order: initialOrder },
18
+ windowProps: { patient, patientUuid, visitContext },
19
+ closeWorkspace,
20
+ }: Workspace2DefinitionProps<AddDrugOrderWorkspaceAdditionalProps, ExportedOrderBasketWindowProps, {}>) {
21
+ return (
22
+ <AddDrugOrder
23
+ initialOrder={initialOrder}
24
+ patient={patient}
25
+ patientUuid={patientUuid}
26
+ visitContext={visitContext}
27
+ closeWorkspace={closeWorkspace}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,112 @@
1
+ import React, { useCallback } from 'react';
2
+ import {
3
+ type ConfigObject,
4
+ type DefaultWorkspaceProps,
5
+ showSnackbar,
6
+ useConfig,
7
+ useSession,
8
+ useVisit,
9
+ } from '@openmrs/esm-framework';
10
+ import { type DrugOrderBasketItem, type EncounterPost, postEncounter } from '@openmrs/esm-patient-common-lib';
11
+ import { prepMedicationOrderPostData } from '../api';
12
+ import DrugOrderForm from './drug-order-form.component';
13
+ import { useTranslation } from 'react-i18next';
14
+
15
+ export interface FillPrescriptionFormProps extends DefaultWorkspaceProps {
16
+ patient: fhir.Patient;
17
+ onAfterSave?(patient: fhir.Patient, encounterUuid: string);
18
+ }
19
+
20
+ /**
21
+ * This form is meant for backfilling prescriptions from paper forms, and is similar to the drug order form in the
22
+ * order basket, except:
23
+ * - it contains additional fields select the drug (inline), the prescribing clinician and the prescribing date.
24
+ * - when submitting the form, it directly submits the order instead of saving to the order basket
25
+ *
26
+ * This component is not used in the medications app itself, but is intended to be used in other apps
27
+ * (like dispensing).
28
+ */
29
+ const FillPrescriptionForm: React.FC<FillPrescriptionFormProps> = ({
30
+ patient,
31
+ closeWorkspace,
32
+ closeWorkspaceWithSavedChanges,
33
+ promptBeforeClosing,
34
+ onAfterSave,
35
+ }) => {
36
+ const { sessionLocation } = useSession();
37
+ const { t } = useTranslation();
38
+ const { drugOrderEncounterType } = useConfig<ConfigObject>();
39
+ const patientUuid = patient.id;
40
+ const { activeVisit, isLoading: isLoadingVisit } = useVisit(patientUuid);
41
+
42
+ const submitDrugOrder = useCallback(
43
+ async (finalizedOrder: DrugOrderBasketItem) => {
44
+ const drugOrderPost = prepMedicationOrderPostData(finalizedOrder, patientUuid, null);
45
+ const encounter: EncounterPost = {
46
+ patient: patientUuid,
47
+ location: sessionLocation.uuid,
48
+ encounterType: drugOrderEncounterType,
49
+ encounterDatetime: new Date(),
50
+ visit: null, // let the server's logic associate the encounter with a visit
51
+ obs: [],
52
+ orders: [drugOrderPost],
53
+ };
54
+ try {
55
+ const encounterUuid = await postEncounter(encounter);
56
+ showSnackbar({
57
+ isLowContrast: true,
58
+ kind: 'success',
59
+ title: t('orderCompleted', 'Placed orders'),
60
+ subtitle: t('ordered', 'Placed order for {{order}}', {
61
+ order: finalizedOrder.drug.display,
62
+ }),
63
+ });
64
+ closeWorkspaceWithSavedChanges();
65
+ onAfterSave?.(patient, encounterUuid);
66
+ } catch (e) {
67
+ showSnackbar({
68
+ isLowContrast: true,
69
+ kind: 'error',
70
+ title: t('saveDrugOrderFailed', 'Error ordering {{orderName}}', {
71
+ orderName: finalizedOrder.drug.display,
72
+ }),
73
+ subtitle: e?.responseBody?.error?.translatedMessage ?? e?.responseBody?.error?.message,
74
+ });
75
+ }
76
+ },
77
+ [
78
+ patientUuid,
79
+ closeWorkspaceWithSavedChanges,
80
+ sessionLocation.uuid,
81
+ t,
82
+ drugOrderEncounterType,
83
+ onAfterSave,
84
+ patient,
85
+ ],
86
+ );
87
+
88
+ if (!isLoadingVisit && !activeVisit) {
89
+ return (
90
+ <div>
91
+ {t('visitRequiredForFillingPrescription', 'Patient is required to have an active visit to fill prescription')}
92
+ </div>
93
+ );
94
+ }
95
+ return (
96
+ <DrugOrderForm
97
+ initialOrderBasketItem={{ action: 'NEW' } as DrugOrderBasketItem}
98
+ patient={patient}
99
+ onSave={submitDrugOrder}
100
+ saveButtonText={t('fillPrescription', 'Fill prescription')}
101
+ onCancel={closeWorkspace}
102
+ allowSelectingPrescribingClinician={true}
103
+ visitContext={activeVisit}
104
+ allowSelectingDrug={true}
105
+ setHasUnsavedChanges={function (hasUnsavedChanges: boolean): void {
106
+ throw new Error('Function not implemented.');
107
+ }}
108
+ />
109
+ );
110
+ };
111
+
112
+ export default FillPrescriptionForm;
package/src/api/api.ts CHANGED
@@ -1,17 +1,21 @@
1
1
  import { useCallback, useMemo } from 'react';
2
2
  import useSWR, { useSWRConfig } from 'swr';
3
3
  import useSWRImmutable from 'swr/immutable';
4
- import { openmrsFetch, restBaseUrl, useConfig, type FetchResponse } from '@openmrs/esm-framework';
5
- import type { DrugOrderPost, PatientOrderFetchResponse, Order } from '@openmrs/esm-patient-common-lib';
4
+ import { openmrsFetch, restBaseUrl, useConfig, useOpenmrsFetchAll, type FetchResponse } from '@openmrs/esm-framework';
5
+ import type {
6
+ DrugOrderBasketItem,
7
+ DrugOrderPost,
8
+ PatientOrderFetchResponse,
9
+ Order,
10
+ } from '@openmrs/esm-patient-common-lib';
6
11
  import { type ConfigObject } from '../config-schema';
7
- import { type DrugOrderBasketItem } from '../types';
8
12
 
9
13
  export const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0';
10
14
 
11
15
  const customRepresentation =
12
16
  'custom:(uuid,dosingType,orderNumber,accessionNumber,' +
13
17
  'patient:ref,action,careSetting:ref,previousOrder:ref,dateActivated,scheduledDate,dateStopped,autoExpireDate,' +
14
- 'orderType:ref,encounter:ref,orderer:(uuid,display,person:(display)),orderReason,orderReasonNonCoded,orderType,urgency,instructions,' +
18
+ 'orderType:ref,encounter:(uuid,display,visit),orderer:(uuid,display,person:(display)),orderReason,orderReasonNonCoded,orderType,urgency,instructions,' +
15
19
  'commentToFulfiller,fulfillerStatus,drug:(uuid,display,strength,dosageForm:(display,uuid),concept),dose,doseUnits:ref,' +
16
20
  'frequency:ref,asNeeded,asNeededCondition,quantity,quantityUnits:ref,numRefills,dosingInstructions,' +
17
21
  'duration,durationUnits:ref,route:ref,brandName,dispenseAsWritten)';
@@ -229,3 +233,20 @@ export function useRequireOutpatientQuantity(): {
229
233
 
230
234
  return results;
231
235
  }
236
+
237
+ export interface Provider {
238
+ uuid: string;
239
+ person: {
240
+ display?: string;
241
+ };
242
+ }
243
+
244
+ export function useProviders(providerRoles: Array<string>) {
245
+ const rep = 'custom:(uuid,person:(display)';
246
+ const ret = useOpenmrsFetchAll<Provider>(
247
+ providerRoles != null ? `${restBaseUrl}/provider?providerRoles=${providerRoles.join(',')}&v=${rep})` : null,
248
+ );
249
+
250
+ ret.data?.sort((a, b) => (a.person?.display ?? '').localeCompare(b.person?.display ?? ''));
251
+ return ret;
252
+ }
@@ -1,13 +1,13 @@
1
1
  import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
- import { useMemo } from 'react';
3
- import useSWRImmutable from 'swr/immutable';
4
2
  import {
5
3
  type DosingUnit,
6
4
  type DurationUnit,
7
5
  type MedicationFrequency,
8
6
  type MedicationRoute,
9
7
  type QuantityUnit,
10
- } from '../types';
8
+ } from '@openmrs/esm-patient-common-lib';
9
+ import { useMemo } from 'react';
10
+ import useSWRImmutable from 'swr/immutable';
11
11
 
12
12
  export interface ConceptName {
13
13
  uuid: string;
@@ -21,10 +21,15 @@ import {
21
21
  import {
22
22
  CardHeader,
23
23
  compare,
24
+ type DrugOrderBasketItem,
24
25
  PatientChartPagination,
25
26
  type Order,
26
27
  useLaunchWorkspaceRequiringVisit,
27
28
  useOrderBasket,
29
+ type PatientWorkspaceGroupProps,
30
+ invalidateVisitByUuid,
31
+ invalidateVisitAndEncounterData,
32
+ type OrderBasketWindowProps,
28
33
  } from '@openmrs/esm-patient-common-lib';
29
34
  import {
30
35
  AddIcon,
@@ -36,14 +41,16 @@ import {
36
41
  useLayoutType,
37
42
  usePagination,
38
43
  UserIcon,
44
+ launchWorkspace2,
45
+ type Encounter,
39
46
  } from '@openmrs/esm-framework';
40
47
  import { useTranslation } from 'react-i18next';
41
48
  import { useReactToPrint } from 'react-to-print';
42
- import { type AddDrugOrderWorkspaceAdditionalProps } from '../add-drug-order/add-drug-order.workspace';
43
- import { type DrugOrderBasketItem } from '../types';
44
49
  import { type ConfigObject } from '../config-schema';
45
50
  import PrintComponent from '../print/print.component';
46
51
  import styles from './medications-details-table.scss';
52
+ import { useSWRConfig } from 'swr';
53
+ import { type AddDrugOrderWorkspaceProps } from '../add-drug-order/add-drug-order.workspace';
47
54
 
48
55
  export interface MedicationsDetailsTableProps {
49
56
  isValidating?: boolean;
@@ -68,15 +75,14 @@ const MedicationsDetailsTable: React.FC<MedicationsDetailsTableProps> = ({
68
75
  }) => {
69
76
  const pageSize = 5;
70
77
  const { t } = useTranslation();
71
- const launchOrderBasket = useLaunchWorkspaceRequiringVisit('order-basket');
72
- const launchAddDrugOrder = useLaunchWorkspaceRequiringVisit('add-drug-order');
78
+ const launchOrderBasket = useLaunchWorkspaceRequiringVisit(patient.id, 'order-basket');
73
79
  const config = useConfig<ConfigObject>();
74
80
  const showPrintButton = config.showPrintButton;
75
81
  const contentToPrintRef = useRef(null);
76
82
  const { excludePatientIdentifierCodeTypes } = useConfig();
77
83
  const [isPrinting, setIsPrinting] = useState(false);
78
84
 
79
- const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>('medications');
85
+ const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>(patient, 'medications');
80
86
  const { results, goTo, currentPage } = usePagination(medications, pageSize);
81
87
 
82
88
  const tableHeaders = [
@@ -251,7 +257,7 @@ const MedicationsDetailsTable: React.FC<MedicationsDetailsTableProps> = ({
251
257
  kind="ghost"
252
258
  renderIcon={(props: ComponentProps<typeof AddIcon>) => <AddIcon size={16} {...props} />}
253
259
  iconDescription="Launch order basket"
254
- onClick={launchAddDrugOrder}
260
+ onClick={() => launchOrderBasket({}, { encounterUuid: '' })}
255
261
  >
256
262
  {t('add', 'Add')}
257
263
  </Button>
@@ -298,14 +304,13 @@ const MedicationsDetailsTable: React.FC<MedicationsDetailsTableProps> = ({
298
304
  {!isPrinting && (
299
305
  <TableCell className="cds--table-column-menu">
300
306
  <OrderBasketItemActions
307
+ patient={patient}
301
308
  showDiscontinueButton={showDiscontinueButton}
302
309
  showModifyButton={showModifyButton}
303
310
  showReorderButton={showReorderButton}
304
311
  medication={medications[rowIndex]}
305
312
  items={orders}
306
313
  setItems={setOrders}
307
- openOrderBasket={launchOrderBasket}
308
- openDrugOrderForm={launchAddDrugOrder}
309
314
  />
310
315
  </TableCell>
311
316
  )}
@@ -337,27 +342,39 @@ function InfoTooltip({ orderer }: { orderer: string }) {
337
342
  }
338
343
 
339
344
  function OrderBasketItemActions({
345
+ patient,
340
346
  showDiscontinueButton,
341
347
  showModifyButton,
342
348
  showReorderButton,
343
349
  medication,
344
350
  items,
345
351
  setItems,
346
- openOrderBasket,
347
- openDrugOrderForm,
348
352
  }: {
353
+ patient: fhir.Patient;
349
354
  showDiscontinueButton: boolean;
350
355
  showModifyButton: boolean;
351
356
  showReorderButton: boolean;
352
357
  medication: Order;
353
358
  items: Array<DrugOrderBasketItem>;
354
359
  setItems: (items: Array<DrugOrderBasketItem>) => void;
355
- openOrderBasket: () => void;
356
- openDrugOrderForm: (additionalProps?: AddDrugOrderWorkspaceAdditionalProps) => void;
357
360
  }) {
361
+ const { mutate: globalMutate } = useSWRConfig();
358
362
  const { t } = useTranslation();
359
363
  const isTablet = useLayoutType() === 'tablet';
360
364
  const alreadyInBasket = items.some((x) => x.uuid === medication.uuid);
365
+
366
+ const workspaceGroupProps: PatientWorkspaceGroupProps = useMemo(
367
+ () => ({
368
+ patient,
369
+ patientUuid: patient.id,
370
+ visitContext: medication.encounter.visit,
371
+ mutateVisitContext: () => {
372
+ invalidateVisitByUuid(globalMutate, medication.encounter.visit?.uuid);
373
+ invalidateVisitAndEncounterData(globalMutate, patient.id);
374
+ },
375
+ }),
376
+ [patient, medication, globalMutate],
377
+ );
361
378
  const handleDiscontinueClick = useCallback(() => {
362
379
  setItems([
363
380
  ...items,
@@ -403,10 +420,17 @@ function OrderBasketItemActions({
403
420
  value: medication.quantityUnits?.display,
404
421
  valueCoded: medication.quantityUnits?.uuid,
405
422
  },
423
+ encounterUuid: medication.encounter?.uuid,
424
+ visit: medication.encounter?.visit,
406
425
  },
407
426
  ]);
408
- openOrderBasket();
409
- }, [items, setItems, medication, openOrderBasket]);
427
+ launchWorkspace2<{}, OrderBasketWindowProps, PatientWorkspaceGroupProps>(
428
+ 'order-basket',
429
+ {},
430
+ { encounterUuid: medication.encounter.uuid },
431
+ workspaceGroupProps,
432
+ );
433
+ }, [items, setItems, medication, workspaceGroupProps]);
410
434
 
411
435
  const handleModifyClick = useCallback(() => {
412
436
  const newItem: DrugOrderBasketItem = {
@@ -451,10 +475,18 @@ function OrderBasketItemActions({
451
475
  value: medication.quantityUnits?.display,
452
476
  valueCoded: medication.quantityUnits?.uuid,
453
477
  },
478
+ encounterUuid: medication.encounter?.uuid,
479
+ visit: medication.encounter?.visit,
454
480
  };
455
481
  setItems([...items, newItem]);
456
- openDrugOrderForm({ order: newItem });
457
- }, [items, setItems, medication, openDrugOrderForm]);
482
+
483
+ launchWorkspace2<AddDrugOrderWorkspaceProps, OrderBasketWindowProps, PatientWorkspaceGroupProps>(
484
+ 'add-drug-order',
485
+ { order: newItem },
486
+ { encounterUuid: medication.encounter.uuid },
487
+ workspaceGroupProps,
488
+ );
489
+ }, [items, setItems, medication, workspaceGroupProps]);
458
490
 
459
491
  const handleReorderClick = useCallback(() => {
460
492
  setItems([
@@ -501,10 +533,17 @@ function OrderBasketItemActions({
501
533
  value: medication.quantityUnits?.display,
502
534
  valueCoded: medication.quantityUnits?.uuid,
503
535
  },
536
+ encounterUuid: medication.encounter?.uuid,
537
+ visit: medication.encounter?.visit,
504
538
  },
505
539
  ]);
506
- openOrderBasket();
507
- }, [items, setItems, medication, openOrderBasket]);
540
+ launchWorkspace2<{}, OrderBasketWindowProps, PatientWorkspaceGroupProps>(
541
+ 'order-basket',
542
+ {},
543
+ { encounterUuid: medication.encounter.uuid },
544
+ workspaceGroupProps,
545
+ );
546
+ }, [items, setItems, medication, workspaceGroupProps]);
508
547
 
509
548
  return (
510
549
  <OverflowMenu aria-label="Actions menu" selectorPrimaryFocus={'#modify'} flipped size={isTablet ? 'lg' : 'md'}>
@@ -2,11 +2,10 @@ import { Type, validator } from '@openmrs/esm-framework';
2
2
 
3
3
  export const configSchema = {
4
4
  daysDurationUnit: {
5
- _description:
6
- 'The default medication duration unit is days. The concept for that medication duration unit is specified here.',
7
5
  uuid: {
8
6
  _type: Type.ConceptUuid,
9
7
  _default: '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
8
+ _description: 'The uuid of the concept of medication duration unit in days',
10
9
  },
11
10
  display: {
12
11
  _type: Type.String,
@@ -37,6 +36,18 @@ export const configSchema = {
37
36
  _description: 'Whether to require an indication when placing a medication order',
38
37
  _default: true,
39
38
  },
39
+ prescriberProviderRoles: {
40
+ _type: Type.Array,
41
+ _description:
42
+ 'Array of provider roles uuids. If specified, the drug order form shows the "Prescribing Clinician" dropdown listing all providers with one of the specified roles. (The dropdown is hidden if no providers match the role criteria.) This feature requires the providermanagement backend module. Note that, in any case, any user who can submit the drug order form may still do so with themselves as the prescriber.',
43
+ _default: [],
44
+ },
45
+ drugOrderEncounterType: {
46
+ _type: Type.UUID,
47
+ _description:
48
+ 'The encounter type of the encounter for the Fill Prescription form. Defaults to the "Order" encounter type.',
49
+ _default: '39da3525-afe4-45ff-8977-c53b7b359158',
50
+ },
40
51
  };
41
52
 
42
53
  export interface ConfigObject {
@@ -48,4 +59,6 @@ export interface ConfigObject {
48
59
  showPrintButton: boolean;
49
60
  debounceDelayInMs: number;
50
61
  requireIndication: boolean;
62
+ prescriberProviderRoles: Array<string>;
63
+ drugOrderEncounterType: string;
51
64
  }
@@ -2,28 +2,31 @@ import React, { type ComponentProps, useCallback, useEffect, useMemo, useState }
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import classNames from 'classnames';
4
4
  import { Button, Tile } from '@carbon/react';
5
+ import { AddIcon, ChevronDownIcon, ChevronUpIcon, useLayoutType } from '@openmrs/esm-framework';
5
6
  import {
6
- AddIcon,
7
- ChevronDownIcon,
8
- ChevronUpIcon,
9
- closeWorkspace,
10
- launchWorkspace,
11
- useLayoutType,
12
- } from '@openmrs/esm-framework';
13
- import { useOrderBasket } from '@openmrs/esm-patient-common-lib';
7
+ type OrderBasketExtensionProps,
8
+ useOrderBasket,
9
+ type DrugOrderBasketItem,
10
+ } from '@openmrs/esm-patient-common-lib';
14
11
  import { prepMedicationOrderPostData } from '../api/api';
15
- import type { DrugOrderBasketItem } from '../types';
16
12
  import OrderBasketItemTile from './order-basket-item-tile.component';
17
13
  import RxIcon from './rx-icon.component';
18
14
  import styles from './drug-order-basket-panel.scss';
19
15
 
20
16
  /**
17
+ * The extension is slotted into order-basket-slot in the main Order Basket workspace by default.
18
+ * It renders the "Add +" button for drug orders, and lists pending drug orders in the order basket.
19
+ *
21
20
  * Designs: https://app.zeplin.io/project/60d59321e8100b0324762e05/screen/62c6bb9500e7671a618efa56
22
21
  */
23
- export default function DrugOrderBasketPanelExtension() {
22
+ function DrugOrderBasketPanelExtension({ patient, launchDrugOrderForm }: OrderBasketExtensionProps) {
24
23
  const { t } = useTranslation();
25
24
  const isTablet = useLayoutType() === 'tablet';
26
- const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>('medications', prepMedicationOrderPostData);
25
+ const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>(
26
+ patient,
27
+ 'medications',
28
+ prepMedicationOrderPostData,
29
+ );
27
30
  const [isExpanded, setIsExpanded] = useState(orders.length > 0);
28
31
  const {
29
32
  incompleteOrderBasketItems,
@@ -61,22 +64,6 @@ export default function DrugOrderBasketPanelExtension() {
61
64
  };
62
65
  }, [orders]);
63
66
 
64
- const openDrugSearch = () => {
65
- closeWorkspace('order-basket', {
66
- ignoreChanges: true,
67
- onWorkspaceClose: () => launchWorkspace('add-drug-order'),
68
- closeWorkspaceGroup: false,
69
- });
70
- };
71
-
72
- const openDrugForm = (order: DrugOrderBasketItem) => {
73
- closeWorkspace('order-basket', {
74
- ignoreChanges: true,
75
- onWorkspaceClose: () => launchWorkspace('add-drug-order', { order }),
76
- closeWorkspaceGroup: false,
77
- });
78
- };
79
-
80
67
  const removeMedication = useCallback(
81
68
  (order: DrugOrderBasketItem) => {
82
69
  const newOrders = [...orders];
@@ -105,7 +92,7 @@ export default function DrugOrderBasketPanelExtension() {
105
92
  kind="ghost"
106
93
  renderIcon={(props: ComponentProps<typeof AddIcon>) => <AddIcon size={16} {...props} />}
107
94
  iconDescription="Add medication"
108
- onClick={openDrugSearch}
95
+ onClick={() => launchDrugOrderForm()}
109
96
  size={isTablet ? 'md' : 'sm'}
110
97
  >
111
98
  {t('add', 'Add')}
@@ -133,7 +120,7 @@ export default function DrugOrderBasketPanelExtension() {
133
120
  <OrderBasketItemTile
134
121
  key={index}
135
122
  orderBasketItem={order}
136
- onItemClick={() => openDrugForm(order)}
123
+ onItemClick={() => launchDrugOrderForm(order)}
137
124
  onRemoveClick={() => removeMedication(order)}
138
125
  />
139
126
  ))}
@@ -145,7 +132,7 @@ export default function DrugOrderBasketPanelExtension() {
145
132
  <OrderBasketItemTile
146
133
  key={index}
147
134
  orderBasketItem={order}
148
- onItemClick={() => openDrugForm(order)}
135
+ onItemClick={() => launchDrugOrderForm(order)}
149
136
  onRemoveClick={() => removeMedication(order)}
150
137
  />
151
138
  ))}
@@ -158,7 +145,7 @@ export default function DrugOrderBasketPanelExtension() {
158
145
  <OrderBasketItemTile
159
146
  key={index}
160
147
  orderBasketItem={item}
161
- onItemClick={() => openDrugForm(item)}
148
+ onItemClick={() => launchDrugOrderForm(item)}
162
149
  onRemoveClick={() => removeMedication(item)}
163
150
  />
164
151
  ))}
@@ -171,7 +158,7 @@ export default function DrugOrderBasketPanelExtension() {
171
158
  <OrderBasketItemTile
172
159
  key={index}
173
160
  orderBasketItem={item}
174
- onItemClick={() => openDrugForm(item)}
161
+ onItemClick={() => launchDrugOrderForm(item)}
175
162
  onRemoveClick={() => removeMedication(item)}
176
163
  />
177
164
  ))}
@@ -184,7 +171,7 @@ export default function DrugOrderBasketPanelExtension() {
184
171
  <OrderBasketItemTile
185
172
  key={index}
186
173
  orderBasketItem={item}
187
- onItemClick={() => openDrugForm(item)}
174
+ onItemClick={() => launchDrugOrderForm(item)}
188
175
  onRemoveClick={() => removeMedication(item)}
189
176
  />
190
177
  ))}
@@ -195,3 +182,5 @@ export default function DrugOrderBasketPanelExtension() {
195
182
  </Tile>
196
183
  );
197
184
  }
185
+
186
+ export default DrugOrderBasketPanelExtension;