@openmrs/esm-patient-medications-app 11.3.1-patch.9064 → 11.3.1-patch.9508
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 +22 -27
- package/dist/1022.js +1 -0
- package/dist/1022.js.map +1 -0
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/3099.js +1 -1
- package/dist/3338.js +2 -0
- package/dist/3338.js.map +1 -0
- package/dist/3584.js +1 -1
- 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/439.js +1 -0
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4944.js +1 -1
- package/dist/4953.js +1 -0
- package/dist/4953.js.map +1 -0
- package/dist/5134.js +2 -0
- package/dist/5134.js.map +1 -0
- 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 -1
- package/dist/5670.js.map +1 -1
- package/dist/6022.js +1 -1
- package/dist/6336.js +1 -0
- package/dist/6336.js.map +1 -0
- package/dist/6468.js +1 -1
- package/dist/6589.js +1 -0
- 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/7151.js +1 -0
- package/dist/7151.js.map +1 -0
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8349.js +1 -1
- package/dist/8371.js +1 -0
- package/dist/8437.js +1 -0
- package/dist/8437.js.map +1 -0
- package/dist/8618.js +1 -1
- package/dist/8803.js +1 -1
- package/dist/8803.js.map +1 -1
- package/dist/8812.js +1 -1
- package/dist/8812.js.map +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-medications-app.js +1 -1
- package/dist/openmrs-esm-patient-medications-app.js.buildmanifest.json +353 -259
- package/dist/openmrs-esm-patient-medications-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +4 -4
- package/src/active-medications/active-medications.component.tsx +1 -1
- package/src/active-medications/active-medications.test.tsx +1 -6
- package/src/add-drug-order/add-drug-order.scss +6 -0
- package/src/add-drug-order/add-drug-order.test.tsx +7 -4
- package/src/add-drug-order/add-drug-order.workspace.tsx +42 -15
- package/src/add-drug-order/drug-order-form.component.tsx +208 -222
- package/src/add-drug-order/drug-order-form.resource.ts +213 -0
- package/src/add-drug-order/drug-order-form.scss +11 -7
- package/src/add-drug-order/drug-search/drug-search-combobox.component.tsx +54 -0
- package/src/add-drug-order/drug-search/drug-search-combobox.test.tsx +56 -0
- package/src/add-drug-order/drug-search/drug-search.component.tsx +3 -1
- package/src/add-drug-order/drug-search/drug-search.resource.tsx +73 -17
- package/src/add-drug-order/drug-search/order-basket-search-results.component.tsx +16 -5
- package/src/add-drug-order/fill-prescription-form.workspace.tsx +111 -0
- package/src/api/api.ts +18 -1
- package/src/components/medications-details-table.component.tsx +3 -3
- package/src/config-schema.ts +15 -2
- package/src/drug-order-basket-panel/drug-order-basket-panel.extension.tsx +22 -5
- package/src/drug-order-basket-panel/drug-order-basket-panel.scss +27 -12
- package/src/drug-order-basket-panel/drug-order-basket-panel.test.tsx +11 -4
- package/src/index.ts +9 -1
- package/src/medications-summary/medications-summary.component.tsx +4 -2
- package/src/past-medications/past-medications.component.tsx +44 -0
- package/src/past-medications/past-medications.scss +7 -0
- package/src/past-medications/past-medications.test.tsx +85 -0
- package/src/routes.json +43 -1
- package/translations/am.json +24 -4
- package/translations/ar.json +24 -4
- package/translations/ar_SY.json +24 -4
- package/translations/bn.json +24 -4
- package/translations/cs.json +115 -0
- package/translations/de.json +24 -4
- package/translations/en.json +22 -4
- package/translations/en_US.json +24 -4
- package/translations/es.json +37 -17
- package/translations/es_MX.json +24 -4
- package/translations/fr.json +30 -10
- package/translations/he.json +24 -4
- package/translations/hi.json +24 -4
- package/translations/hi_IN.json +24 -4
- package/translations/id.json +33 -13
- package/translations/it.json +33 -13
- package/translations/ka.json +31 -11
- package/translations/km.json +24 -4
- package/translations/ku.json +24 -4
- package/translations/ky.json +24 -4
- package/translations/lg.json +24 -4
- package/translations/ne.json +24 -4
- package/translations/pl.json +24 -4
- package/translations/pt.json +33 -13
- package/translations/pt_BR.json +33 -13
- package/translations/qu.json +24 -4
- package/translations/ro_RO.json +33 -13
- package/translations/ru_RU.json +24 -4
- package/translations/si.json +24 -4
- package/translations/sq.json +115 -0
- package/translations/sw.json +24 -4
- package/translations/sw_KE.json +24 -4
- package/translations/tr.json +24 -4
- package/translations/tr_TR.json +24 -4
- package/translations/uk.json +24 -4
- package/translations/uz.json +24 -4
- package/translations/uz@Latn.json +24 -4
- package/translations/uz_UZ.json +24 -4
- package/translations/vi.json +24 -4
- package/translations/zh.json +25 -5
- package/translations/zh_CN.json +33 -13
- package/translations/zh_TW.json +115 -0
- package/dist/2537.js +0 -1
- package/dist/2537.js.map +0 -1
- package/dist/4918.js +0 -1
- package/dist/4918.js.map +0 -1
- package/dist/5837.js +0 -2
- package/dist/5837.js.map +0 -1
- package/dist/6783.js +0 -2
- package/dist/6783.js.map +0 -1
- package/dist/9365.js +0 -1
- package/dist/9365.js.map +0 -1
- package/dist/9647.js +0 -1
- package/dist/9647.js.map +0 -1
- /package/dist/{5837.js.LICENSE.txt → 3338.js.LICENSE.txt} +0 -0
- /package/dist/{6783.js.LICENSE.txt → 5134.js.LICENSE.txt} +0 -0
package/src/api/api.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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';
|
|
4
|
+
import { openmrsFetch, restBaseUrl, useConfig, useOpenmrsFetchAll, type FetchResponse } from '@openmrs/esm-framework';
|
|
5
5
|
import type { DrugOrderPost, PatientOrderFetchResponse, Order } from '@openmrs/esm-patient-common-lib';
|
|
6
6
|
import { type ConfigObject } from '../config-schema';
|
|
7
7
|
import { type DrugOrderBasketItem } from '../types';
|
|
@@ -229,3 +229,20 @@ export function useRequireOutpatientQuantity(): {
|
|
|
229
229
|
|
|
230
230
|
return results;
|
|
231
231
|
}
|
|
232
|
+
|
|
233
|
+
export interface Provider {
|
|
234
|
+
uuid: string;
|
|
235
|
+
person: {
|
|
236
|
+
display?: string;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function useProviders(providerRoles: Array<string>) {
|
|
241
|
+
const rep = 'custom:(uuid,person:(display)';
|
|
242
|
+
const ret = useOpenmrsFetchAll<Provider>(
|
|
243
|
+
providerRoles != null ? `${restBaseUrl}/provider?providerRoles=${providerRoles.join(',')}&v=${rep})` : null,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
ret.data?.sort((a, b) => (a.person?.display ?? '').localeCompare(b.person?.display ?? ''));
|
|
247
|
+
return ret;
|
|
248
|
+
}
|
|
@@ -68,15 +68,15 @@ const MedicationsDetailsTable: React.FC<MedicationsDetailsTableProps> = ({
|
|
|
68
68
|
}) => {
|
|
69
69
|
const pageSize = 5;
|
|
70
70
|
const { t } = useTranslation();
|
|
71
|
-
const launchOrderBasket = useLaunchWorkspaceRequiringVisit('order-basket');
|
|
72
|
-
const launchAddDrugOrder = useLaunchWorkspaceRequiringVisit('add-drug-order');
|
|
71
|
+
const launchOrderBasket = useLaunchWorkspaceRequiringVisit(patient.id, 'order-basket');
|
|
72
|
+
const launchAddDrugOrder = useLaunchWorkspaceRequiringVisit(patient.id, 'add-drug-order');
|
|
73
73
|
const config = useConfig<ConfigObject>();
|
|
74
74
|
const showPrintButton = config.showPrintButton;
|
|
75
75
|
const contentToPrintRef = useRef(null);
|
|
76
76
|
const { excludePatientIdentifierCodeTypes } = useConfig();
|
|
77
77
|
const [isPrinting, setIsPrinting] = useState(false);
|
|
78
78
|
|
|
79
|
-
const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>('medications');
|
|
79
|
+
const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>(patient, 'medications');
|
|
80
80
|
const { results, goTo, currentPage } = usePagination(medications, pageSize);
|
|
81
81
|
|
|
82
82
|
const tableHeaders = [
|
package/src/config-schema.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
closeWorkspace,
|
|
10
10
|
launchWorkspace,
|
|
11
11
|
useLayoutType,
|
|
12
|
+
type Visit,
|
|
12
13
|
} from '@openmrs/esm-framework';
|
|
13
14
|
import { useOrderBasket } from '@openmrs/esm-patient-common-lib';
|
|
14
15
|
import { prepMedicationOrderPostData } from '../api/api';
|
|
@@ -17,13 +18,26 @@ import OrderBasketItemTile from './order-basket-item-tile.component';
|
|
|
17
18
|
import RxIcon from './rx-icon.component';
|
|
18
19
|
import styles from './drug-order-basket-panel.scss';
|
|
19
20
|
|
|
21
|
+
interface OrderBasketSlotProps {
|
|
22
|
+
patientUuid: string;
|
|
23
|
+
patient: fhir.Patient;
|
|
24
|
+
visitContext: Visit;
|
|
25
|
+
mutateVisitContext: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
/**
|
|
21
29
|
* Designs: https://app.zeplin.io/project/60d59321e8100b0324762e05/screen/62c6bb9500e7671a618efa56
|
|
30
|
+
* Slotted into order-basket-slot by default
|
|
22
31
|
*/
|
|
23
|
-
|
|
32
|
+
const DrugOrderBasketPanelExtension: React.FC<OrderBasketSlotProps> = ({ patient }) => {
|
|
24
33
|
const { t } = useTranslation();
|
|
25
34
|
const isTablet = useLayoutType() === 'tablet';
|
|
26
|
-
const
|
|
35
|
+
const responsiveSize = isTablet ? 'md' : 'sm';
|
|
36
|
+
const { orders, setOrders } = useOrderBasket<DrugOrderBasketItem>(
|
|
37
|
+
patient,
|
|
38
|
+
'medications',
|
|
39
|
+
prepMedicationOrderPostData,
|
|
40
|
+
);
|
|
27
41
|
const [isExpanded, setIsExpanded] = useState(orders.length > 0);
|
|
28
42
|
const {
|
|
29
43
|
incompleteOrderBasketItems,
|
|
@@ -94,7 +108,7 @@ export default function DrugOrderBasketPanelExtension() {
|
|
|
94
108
|
<Tile
|
|
95
109
|
className={classNames(isTablet ? styles.tabletTile : styles.desktopTile, { [styles.collapsedTile]: !isExpanded })}
|
|
96
110
|
>
|
|
97
|
-
<div className={styles.
|
|
111
|
+
<div className={classNames(isTablet ? styles.tabletContainer : styles.desktopContainer)}>
|
|
98
112
|
<div className={styles.iconAndLabel}>
|
|
99
113
|
<RxIcon isTablet={isTablet} />
|
|
100
114
|
<h4 className={styles.heading}>{`${t('drugOrders', 'Drug orders')} (${orders.length})`}</h4>
|
|
@@ -106,7 +120,7 @@ export default function DrugOrderBasketPanelExtension() {
|
|
|
106
120
|
renderIcon={(props: ComponentProps<typeof AddIcon>) => <AddIcon size={16} {...props} />}
|
|
107
121
|
iconDescription="Add medication"
|
|
108
122
|
onClick={openDrugSearch}
|
|
109
|
-
size={
|
|
123
|
+
size={responsiveSize}
|
|
110
124
|
>
|
|
111
125
|
{t('add', 'Add')}
|
|
112
126
|
</Button>
|
|
@@ -120,6 +134,7 @@ export default function DrugOrderBasketPanelExtension() {
|
|
|
120
134
|
iconDescription="View"
|
|
121
135
|
disabled={orders.length === 0}
|
|
122
136
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
137
|
+
size={responsiveSize}
|
|
123
138
|
>
|
|
124
139
|
{t('add', 'Add')}
|
|
125
140
|
</Button>
|
|
@@ -194,4 +209,6 @@ export default function DrugOrderBasketPanelExtension() {
|
|
|
194
209
|
)}
|
|
195
210
|
</Tile>
|
|
196
211
|
);
|
|
197
|
-
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export default DrugOrderBasketPanelExtension;
|
|
@@ -3,17 +3,40 @@
|
|
|
3
3
|
@use '@carbon/type';
|
|
4
4
|
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
5
5
|
|
|
6
|
+
.desktopContainer {
|
|
7
|
+
background-color: colors.$gray-10;
|
|
8
|
+
display: flex;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
align-items: center;
|
|
11
|
+
text-align: left;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.tabletContainer {
|
|
15
|
+
@extend .desktopContainer;
|
|
16
|
+
background-color: colors.$white-0;
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
.desktopTile {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
20
|
+
position: relative;
|
|
21
|
+
border-left: layout.$spacing-02 solid colors.$cyan-70;
|
|
22
|
+
background-color: colors.$white-0;
|
|
10
23
|
border-right: none;
|
|
11
24
|
padding: 0;
|
|
25
|
+
|
|
26
|
+
&::after {
|
|
27
|
+
content: '';
|
|
28
|
+
position: absolute;
|
|
29
|
+
top: 0;
|
|
30
|
+
left: 0;
|
|
31
|
+
right: 0;
|
|
32
|
+
height: 1px;
|
|
33
|
+
background-color: colors.$cyan-20;
|
|
34
|
+
}
|
|
12
35
|
}
|
|
13
36
|
|
|
14
37
|
.tabletTile {
|
|
15
38
|
@extend .desktopTile;
|
|
16
|
-
border-top: 1px solid
|
|
39
|
+
border-top: 1px solid colors.$cyan-20;
|
|
17
40
|
}
|
|
18
41
|
|
|
19
42
|
.collapsedTile {
|
|
@@ -29,14 +52,6 @@
|
|
|
29
52
|
border-bottom: 1px solid $grey-2;
|
|
30
53
|
}
|
|
31
54
|
|
|
32
|
-
.container {
|
|
33
|
-
background-color: $ui-02;
|
|
34
|
-
display: flex;
|
|
35
|
-
justify-content: space-between;
|
|
36
|
-
align-items: center;
|
|
37
|
-
text-align: left;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
55
|
.desktopTile .container {
|
|
41
56
|
h4 {
|
|
42
57
|
@include type.type-style('heading-compact-02');
|
|
@@ -3,7 +3,7 @@ import { screen, render } from '@testing-library/react';
|
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { getByTextWithMarkup } from 'tools';
|
|
5
5
|
import { type DrugOrderBasketItem } from '../types';
|
|
6
|
-
import { mockDrugSearchResultApiData, mockPatientDrugOrdersApiData } from '__mocks__';
|
|
6
|
+
import { mockDrugSearchResultApiData, mockFhirPatient, mockPatientDrugOrdersApiData } from '__mocks__';
|
|
7
7
|
import { getTemplateOrderBasketItem } from '../add-drug-order/drug-search/drug-search.resource';
|
|
8
8
|
import DrugOrderBasketPanel from './drug-order-basket-panel.extension';
|
|
9
9
|
|
|
@@ -14,10 +14,17 @@ jest.mock('@openmrs/esm-patient-common-lib', () => ({
|
|
|
14
14
|
useOrderBasket: () => mockUseOrderBasket(),
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
+
const testProps = {
|
|
18
|
+
patient: mockFhirPatient,
|
|
19
|
+
patientUuid: mockFhirPatient.id,
|
|
20
|
+
visitContext: null,
|
|
21
|
+
mutateVisitContext: null,
|
|
22
|
+
};
|
|
23
|
+
|
|
17
24
|
describe('OrderBasketPanel', () => {
|
|
18
25
|
test('renders an empty state when no items are selected in the order basket', () => {
|
|
19
26
|
mockUseOrderBasket.mockReturnValue({ orders: [] });
|
|
20
|
-
render(<DrugOrderBasketPanel />);
|
|
27
|
+
render(<DrugOrderBasketPanel {...testProps} />);
|
|
21
28
|
expect(screen.getByRole('heading', { name: /Drug orders \(0\)/i })).toBeInTheDocument();
|
|
22
29
|
expect(screen.getByRole('button', { name: /Add/i })).toBeInTheDocument();
|
|
23
30
|
});
|
|
@@ -39,7 +46,7 @@ describe('OrderBasketPanel', () => {
|
|
|
39
46
|
orders: orders,
|
|
40
47
|
setOrders: mockSetOrders,
|
|
41
48
|
}));
|
|
42
|
-
const { rerender } = render(<DrugOrderBasketPanel />);
|
|
49
|
+
const { rerender } = render(<DrugOrderBasketPanel {...testProps} />);
|
|
43
50
|
expect(screen.getByText(/Drug orders \(4\)/i)).toBeInTheDocument();
|
|
44
51
|
expect(getByTextWithMarkup(/New\s*Aspirin 81mg — 81mg — Tablet/i)).toBeVisible();
|
|
45
52
|
expect(getByTextWithMarkup(/DOSE\s*Tablet/i)).toBeVisible();
|
|
@@ -49,7 +56,7 @@ describe('OrderBasketPanel', () => {
|
|
|
49
56
|
const removeAspirin81Button = screen.getAllByRole('button', { name: /remove from basket/i })[0];
|
|
50
57
|
expect(removeAspirin81Button).toBeVisible();
|
|
51
58
|
await user.click(removeAspirin81Button);
|
|
52
|
-
rerender(<DrugOrderBasketPanel />); // re-render because the mocked hook does not trigger a render
|
|
59
|
+
rerender(<DrugOrderBasketPanel {...testProps} />); // re-render because the mocked hook does not trigger a render
|
|
53
60
|
await expect(screen.getByText(/Drug Orders \(3\)/i)).toBeInTheDocument();
|
|
54
61
|
});
|
|
55
62
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle
|
|
1
|
+
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
2
2
|
import { createDashboardLink } from '@openmrs/esm-patient-common-lib';
|
|
3
3
|
import { configSchema } from './config-schema';
|
|
4
4
|
import { dashboardMeta, moduleName } from './dashboard.meta';
|
|
5
5
|
import medicationsSummaryComponent from './medications-summary/medications-summary.component';
|
|
6
6
|
import activeMedicationsComponent from './active-medications/active-medications.component';
|
|
7
|
+
import pastMedicationsComponent from './past-medications/past-medications.component';
|
|
7
8
|
|
|
8
9
|
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
9
10
|
|
|
@@ -20,6 +21,8 @@ export const medicationsSummary = getSyncLifecycle(medicationsSummaryComponent,
|
|
|
20
21
|
|
|
21
22
|
export const activeMedications = getSyncLifecycle(activeMedicationsComponent, options);
|
|
22
23
|
|
|
24
|
+
export const pastMedications = getSyncLifecycle(pastMedicationsComponent, options);
|
|
25
|
+
|
|
23
26
|
export const drugOrderPanel = getAsyncLifecycle(
|
|
24
27
|
() => import('./drug-order-basket-panel/drug-order-basket-panel.extension'),
|
|
25
28
|
options,
|
|
@@ -39,3 +42,8 @@ export const addDrugOrderWorkspace = getAsyncLifecycle(
|
|
|
39
42
|
() => import('./add-drug-order/add-drug-order.workspace'),
|
|
40
43
|
options,
|
|
41
44
|
);
|
|
45
|
+
|
|
46
|
+
export const fillPrescriptionFormWorkspace = getAsyncLifecycle(
|
|
47
|
+
() => import('./add-drug-order/fill-prescription-form.workspace'),
|
|
48
|
+
options,
|
|
49
|
+
);
|
|
@@ -12,8 +12,10 @@ export interface MedicationsSummaryProps {
|
|
|
12
12
|
|
|
13
13
|
export default function MedicationsSummary({ patient }: MedicationsSummaryProps) {
|
|
14
14
|
const { t } = useTranslation();
|
|
15
|
-
const launchAddDrugWorkspace =
|
|
16
|
-
|
|
15
|
+
const launchAddDrugWorkspace = useLaunchWorkspaceRequiringVisit<AddDrugOrderWorkspaceAdditionalProps>(
|
|
16
|
+
patient.id,
|
|
17
|
+
'add-drug-order',
|
|
18
|
+
);
|
|
17
19
|
|
|
18
20
|
const {
|
|
19
21
|
data: activeOrders,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { DataTableSkeleton } from '@carbon/react';
|
|
4
|
+
import { EmptyState, ErrorState } from '@openmrs/esm-patient-common-lib';
|
|
5
|
+
import MedicationsDetailsTable from '../components/medications-details-table.component';
|
|
6
|
+
import { usePastPatientOrders } from '../api/api';
|
|
7
|
+
|
|
8
|
+
interface PastMedicationsProps {
|
|
9
|
+
patient: fhir.Patient;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const PastMedications: React.FC<PastMedicationsProps> = ({ patient }) => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const headerTitle = t('pastMedicationsHeaderTitle', 'Past medications');
|
|
15
|
+
const displayText = t('pastMedicationsDisplayText', 'past medications');
|
|
16
|
+
|
|
17
|
+
const { data: pastPatientOrders, error, isLoading, isValidating } = usePastPatientOrders(patient?.id);
|
|
18
|
+
|
|
19
|
+
if (isLoading) {
|
|
20
|
+
return <DataTableSkeleton role="progressbar" />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (error) {
|
|
24
|
+
return <ErrorState error={error} headerTitle={headerTitle} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (pastPatientOrders?.length) {
|
|
28
|
+
return (
|
|
29
|
+
<MedicationsDetailsTable
|
|
30
|
+
isValidating={isValidating}
|
|
31
|
+
title={t('pastMedicationsTableTitle', 'Past Medications')}
|
|
32
|
+
medications={pastPatientOrders}
|
|
33
|
+
showDiscontinueButton={false}
|
|
34
|
+
showModifyButton={false}
|
|
35
|
+
showReorderButton={true}
|
|
36
|
+
patient={patient}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return <EmptyState displayText={displayText} headerTitle={headerTitle} />;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default PastMedications;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { openmrsFetch, useSession } from '@openmrs/esm-framework';
|
|
3
|
+
import { screen, within } from '@testing-library/react';
|
|
4
|
+
import { mockPatientDrugOrdersApiData, mockSessionDataResponse } from '__mocks__';
|
|
5
|
+
import { mockPatient, renderWithSwr, waitForLoadingToFinish } from 'tools';
|
|
6
|
+
import PastMedications from './past-medications.component';
|
|
7
|
+
|
|
8
|
+
const mockUseSession = jest.mocked(useSession);
|
|
9
|
+
const mockOpenmrsFetch = openmrsFetch as jest.Mock;
|
|
10
|
+
|
|
11
|
+
mockUseSession.mockReturnValue(mockSessionDataResponse.data);
|
|
12
|
+
|
|
13
|
+
describe('PastMedications', () => {
|
|
14
|
+
test('renders an empty state view when there are no past medications to display', async () => {
|
|
15
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [] } });
|
|
16
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [] } });
|
|
17
|
+
|
|
18
|
+
renderWithSwr(<PastMedications patient={mockPatient} />);
|
|
19
|
+
|
|
20
|
+
await waitForLoadingToFinish();
|
|
21
|
+
|
|
22
|
+
expect(screen.getByRole('heading', { name: /past medications/i })).toBeInTheDocument();
|
|
23
|
+
expect(screen.getByTitle(/empty data illustration/i)).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByText(/There are no past medications to display for this patient/i)).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('renders an error state view if there is a problem fetching medications data', async () => {
|
|
28
|
+
const error = {
|
|
29
|
+
message: 'You are not logged in',
|
|
30
|
+
response: {
|
|
31
|
+
status: 401,
|
|
32
|
+
statusText: 'Unauthorized',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
mockOpenmrsFetch.mockRejectedValueOnce(error);
|
|
37
|
+
|
|
38
|
+
renderWithSwr(<PastMedications patient={mockPatient} />);
|
|
39
|
+
|
|
40
|
+
await waitForLoadingToFinish();
|
|
41
|
+
|
|
42
|
+
expect(screen.queryByRole('table')).not.toBeInTheDocument();
|
|
43
|
+
expect(screen.getByRole('heading', { name: /past medications/i })).toBeInTheDocument();
|
|
44
|
+
expect(screen.getByText(/Error 401: Unauthorized/i)).toBeInTheDocument();
|
|
45
|
+
expect(screen.getByText(/Sorry, there was a problem displaying this information/i)).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('renders a tabular overview of the past medications recorded for a patient', async () => {
|
|
49
|
+
mockOpenmrsFetch
|
|
50
|
+
.mockReturnValueOnce({
|
|
51
|
+
data: { results: mockPatientDrugOrdersApiData },
|
|
52
|
+
})
|
|
53
|
+
.mockReturnValueOnce({
|
|
54
|
+
data: { results: [] }, // simulate no active orders so all become "past"
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
renderWithSwr(<PastMedications patient={mockPatient} />);
|
|
58
|
+
|
|
59
|
+
await waitForLoadingToFinish();
|
|
60
|
+
|
|
61
|
+
const headingElements = screen.getAllByText(/Past Medications/i);
|
|
62
|
+
headingElements.forEach((headingElement) => {
|
|
63
|
+
expect(headingElement).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const table = screen.getByRole('table');
|
|
67
|
+
expect(table).toBeInTheDocument();
|
|
68
|
+
|
|
69
|
+
const expectedColumnHeaders = [/start date/i, /details/i];
|
|
70
|
+
expectedColumnHeaders.forEach((header) => {
|
|
71
|
+
expect(screen.getByRole('columnheader', { name: header })).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const expectedTableRows = [
|
|
75
|
+
/14-Aug-2023 Admin User Acetaminophen 325 mg — 325mg — tablet DOSE 2 tablet — oral — twice daily — indefinite duration — take it sometimes INDICATION Bad boo-boo/i,
|
|
76
|
+
/14-Aug-2023 Admin User Acetaminophen 325 mg — 325mg — tablet 14-Aug-2023 DOSE 2 tablet — oral — twice daily — indefinite duration INDICATION No good 0/i,
|
|
77
|
+
/14-Aug-2023 Admin User Sulfacetamide 0.1 — 10% DOSE 1 application — for 1 weeks — REFILLS 1 — apply it INDICATION Pain — QUANTITY 8 Application/i,
|
|
78
|
+
/14-Aug-2023 Admin User Aspirin 162.5mg — 162.5mg — tablet DOSE 1 tablet — oral — once daily — for 30 days INDICATION Heart — QUANTITY 30 Tablet/i,
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
expectedTableRows.forEach((row) => {
|
|
82
|
+
expect(within(table).getByRole('row', { name: row })).toBeInTheDocument();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
package/src/routes.json
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
3
|
"backendDependencies": {
|
|
4
|
-
"webservices.rest": "
|
|
4
|
+
"webservices.rest": ">=2.2.0"
|
|
5
|
+
},
|
|
6
|
+
"optionalBackendDependencies": {
|
|
7
|
+
"providermanagement": {
|
|
8
|
+
"version": "^2.16.0",
|
|
9
|
+
"feature": {
|
|
10
|
+
"flagName": "providermanagement-module",
|
|
11
|
+
"label": "Prescribing Clinician Roles",
|
|
12
|
+
"description": "Enable support for configuring prescribing clinicians roles when ordering drugs. Requires the backend providermanagement module."
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"ordertemplates": {
|
|
16
|
+
"version": "^1.0.2",
|
|
17
|
+
"feature": {
|
|
18
|
+
"flagName": "ordertemplates-module",
|
|
19
|
+
"label": "Order Templates",
|
|
20
|
+
"description": "Enable support for order templates when ordering drugs. Requires the backend ordertemplates module."
|
|
21
|
+
}
|
|
22
|
+
}
|
|
5
23
|
},
|
|
6
24
|
"extensions": [
|
|
7
25
|
{
|
|
@@ -21,6 +39,15 @@
|
|
|
21
39
|
},
|
|
22
40
|
"order": 4
|
|
23
41
|
},
|
|
42
|
+
{
|
|
43
|
+
"name": "past-medications-widget",
|
|
44
|
+
"component": "pastMedications",
|
|
45
|
+
"slot": "patient-chart-summary-dashboard-slot",
|
|
46
|
+
"meta": {
|
|
47
|
+
"fullWidth": false
|
|
48
|
+
},
|
|
49
|
+
"order": 5
|
|
50
|
+
},
|
|
24
51
|
{
|
|
25
52
|
"name": "medications-summary-dashboard",
|
|
26
53
|
"component": "medicationsDashboardLink",
|
|
@@ -36,6 +63,14 @@
|
|
|
36
63
|
"component": "drugOrderPanel",
|
|
37
64
|
"slot": "order-basket-slot",
|
|
38
65
|
"order": 1
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "drug-order-form",
|
|
69
|
+
"component": "drugOrderForm"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "drug-search-combobox",
|
|
73
|
+
"component": "drugSearchComboBox"
|
|
39
74
|
}
|
|
40
75
|
],
|
|
41
76
|
"workspaces": [
|
|
@@ -44,6 +79,13 @@
|
|
|
44
79
|
"title": "addDrugOrderWorkspaceTitle",
|
|
45
80
|
"component": "addDrugOrderWorkspace",
|
|
46
81
|
"type": "order"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "fill-prescription-form",
|
|
85
|
+
"title": "fillPrescription",
|
|
86
|
+
"component": "fillPrescriptionFormWorkspace",
|
|
87
|
+
"type": "order"
|
|
47
88
|
}
|
|
89
|
+
|
|
48
90
|
]
|
|
49
91
|
}
|
package/translations/am.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"activeMedicationsDisplayText": "active medications",
|
|
4
4
|
"activeMedicationsHeaderTitle": "Active medications",
|
|
5
5
|
"activeMedicationsTableTitle": "Active Medications",
|
|
6
|
+
"activePrescriptionExists": "Active prescription exists for this drug",
|
|
6
7
|
"add": "Add",
|
|
7
8
|
"addDrugOrderWorkspaceTitle": "Add drug order",
|
|
8
9
|
"backToOrderBasket": "Back to order basket",
|
|
@@ -13,12 +14,16 @@
|
|
|
13
14
|
"discard": "Discard",
|
|
14
15
|
"discontinue": "Discontinue",
|
|
15
16
|
"discontinued": "Discontinued",
|
|
16
|
-
"dispensingInformation": "
|
|
17
|
-
"dosageInstructions": "
|
|
17
|
+
"dispensingInformation": "Dispensing instructions",
|
|
18
|
+
"dosageInstructions": "Dosage instructions",
|
|
18
19
|
"dosageRequiredErrorMessage": "Dosage is required",
|
|
19
20
|
"dose": "Dose",
|
|
20
21
|
"drugAlreadyPrescribed": "Already prescribed",
|
|
22
|
+
"drugName": "Drug name",
|
|
21
23
|
"drugOrders": "Drug orders",
|
|
24
|
+
"drugOrderValidationFailed": "Validation failed",
|
|
25
|
+
"drugOrderValidationFailedDescription": "Please check the form for errors and try again.",
|
|
26
|
+
"drugRequiredErrorMessage": "Drug is required",
|
|
22
27
|
"duration": "Duration",
|
|
23
28
|
"durationUnit": "Duration unit",
|
|
24
29
|
"durationUnitPlaceholder": "Duration Unit",
|
|
@@ -27,13 +32,17 @@
|
|
|
27
32
|
"editDosageUnitsTitle": "Dose unit",
|
|
28
33
|
"editDoseComboBoxPlaceholder": "Dose",
|
|
29
34
|
"editDoseComboBoxTitle": "Dose",
|
|
35
|
+
"editDrugOrderWorkspaceTitle": "Edit drug order",
|
|
30
36
|
"editFrequencyComboBoxTitle": "Frequency",
|
|
31
37
|
"editRouteComboBoxTitle": "Route",
|
|
32
38
|
"error": "Error",
|
|
33
39
|
"errorFetchingDrugOrderTemplates": "Error fetching drug order templates",
|
|
34
40
|
"errorFetchingDrugResults": "Error fetching results for \"{{searchTerm}}\"",
|
|
35
41
|
"errorFetchingOrderConfig": "Error occured when fetching Order config",
|
|
42
|
+
"errorLoadingClinicians": "Error loading clinicians",
|
|
43
|
+
"errorSavingDrugOrder": "Error saving drug order",
|
|
36
44
|
"female": "Female",
|
|
45
|
+
"fillPrescription": "Fill prescription",
|
|
37
46
|
"freeDosageErrorMessage": "Add free dosage note",
|
|
38
47
|
"freeTextDosage": "Free text dosage",
|
|
39
48
|
"goToDrugOrderForm": "Order form",
|
|
@@ -46,8 +55,12 @@
|
|
|
46
55
|
"medicationIndefiniteDuration": "Indefinite duration",
|
|
47
56
|
"Medications": "Medications",
|
|
48
57
|
"modify": "Modify",
|
|
58
|
+
"noCliniciansFound": "No clinicians found",
|
|
59
|
+
"noCliniciansFoundDescription": "Cannot select prescribing clinician because no clinicians with appropriate roles are found. Check configuration.",
|
|
49
60
|
"noResultsForDrugSearch": "No results to display for \"{{searchTerm}}\"",
|
|
50
61
|
"numRefillsErrorMessage": "Number of refills is required",
|
|
62
|
+
"off": "Off",
|
|
63
|
+
"on": "On",
|
|
51
64
|
"onDate": "on",
|
|
52
65
|
"or": "or",
|
|
53
66
|
"orderActionDiscontinue": "Discontinue",
|
|
@@ -55,6 +68,8 @@
|
|
|
55
68
|
"orderActionNew": "New",
|
|
56
69
|
"orderActionRenew": "Renew",
|
|
57
70
|
"orderActionRevise": "Modify",
|
|
71
|
+
"orderCompleted": "Placed orders",
|
|
72
|
+
"ordered": "Placed order for {{order}}",
|
|
58
73
|
"orderForm": "Order Form",
|
|
59
74
|
"other": "Other",
|
|
60
75
|
"pastMedicationsDisplayText": "past medications",
|
|
@@ -63,7 +78,10 @@
|
|
|
63
78
|
"patientInstructions": "Patient instructions",
|
|
64
79
|
"patientInstructionsPlaceholder": "Additional dosing instructions (e.g. \"Take after eating\")",
|
|
65
80
|
"pillDispensedErrorMessage": "Quantity to dispense is required",
|
|
66
|
-
"
|
|
81
|
+
"prescribingClinician": "Prescribing Clinician",
|
|
82
|
+
"prescribingClinicianRequiredErrorMessage": "Prescribing clinician is required",
|
|
83
|
+
"prescriptionDuration": "Prescription duration",
|
|
84
|
+
"prescriptionInfo": "Prescription info",
|
|
67
85
|
"prescriptionRefills": "Prescription refills",
|
|
68
86
|
"print": "Print",
|
|
69
87
|
"printedBy": "Printed by",
|
|
@@ -76,6 +94,7 @@
|
|
|
76
94
|
"removeFromBasket": "Remove from basket",
|
|
77
95
|
"reorder": "Reorder",
|
|
78
96
|
"returnToOrderBasket": "Return to order basket",
|
|
97
|
+
"saveDrugOrderFailed": "Error ordering {{orderName}}",
|
|
79
98
|
"saveOrder": "Save order",
|
|
80
99
|
"searchAgain": "search again",
|
|
81
100
|
"searchFieldPlaceholder": "Search for a drug or orderset (e.g. \"Aspirin\")",
|
|
@@ -91,5 +110,6 @@
|
|
|
91
110
|
"trySearchingAgain": "Please try searching again",
|
|
92
111
|
"tryTo": "Try to",
|
|
93
112
|
"unknown": "Unknown",
|
|
94
|
-
"usingADifferentTerm": "using a different term"
|
|
113
|
+
"usingADifferentTerm": "using a different term",
|
|
114
|
+
"visitRequiredForFillingPrescription": "Patient is required to have an active visit to fill prescription"
|
|
95
115
|
}
|