@openmrs/esm-dispensing-app 1.0.1-pre.99 → 1.1.0
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/.eslintrc +3 -1
- package/.husky/pre-commit +0 -0
- package/.husky/pre-push +0 -0
- package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
- package/.yarn/plugins/@yarnpkg/plugin-version.cjs +550 -0
- package/README.md +35 -0
- package/jest.config.js +15 -0
- package/package.json +13 -10
- package/src/components/action-buttons.component.test.tsx +244 -0
- package/src/components/action-buttons.component.tsx +137 -0
- package/src/components/action-buttons.scss +5 -0
- package/src/components/medication-card.component.test.tsx +40 -0
- package/src/components/medication-card.component.tsx +10 -17
- package/src/components/medication-card.scss +5 -2
- package/src/components/medication-dispense-review.scss +9 -0
- package/src/components/medication-event.component.tsx +94 -0
- package/src/components/{medication-event-card.scss → medication-event.scss} +0 -9
- package/src/components/patient-details.component.tsx +4 -27
- package/src/components/patient-details.scss +0 -3
- package/src/config-schema.ts +141 -2
- package/src/constants.ts +24 -0
- package/src/dashboard/dispensing-dashboard.component.tsx +38 -0
- package/src/declarations.d.tsx +2 -0
- package/src/dispensing-tiles/dispensing-tiles.resource.tsx +2 -0
- package/src/dispensing.component.tsx +2 -12
- package/src/dispensing.test.tsx +1 -1
- package/src/forms/close-dispense-form.component.tsx +236 -0
- package/src/forms/dispense-form.component.tsx +124 -142
- package/src/forms/{dispense-form.scss → forms.scss} +6 -0
- package/src/forms/medication-dispense-review.component.test.tsx +142 -0
- package/src/forms/medication-dispense-review.component.tsx +562 -0
- package/src/forms/overlay/overlay.component.tsx +3 -1
- package/src/forms/pause-dispense-form.component.tsx +236 -0
- package/src/history/history-and-comments.component.tsx +326 -0
- package/src/history/history-and-comments.scss +57 -0
- package/src/index.ts +20 -35
- package/src/location/location.resource.test.tsx +189 -0
- package/src/location/location.resource.tsx +27 -0
- package/src/medication/medication.resource.test.tsx +181 -0
- package/src/medication/medication.resource.tsx +54 -0
- package/src/medication-dispense/medication-dispense.resource.test.tsx +294 -0
- package/src/medication-dispense/medication-dispense.resource.tsx +113 -45
- package/src/medication-request/medication-request.resource.test.tsx +1389 -0
- package/src/medication-request/medication-request.resource.tsx +145 -84
- package/src/pharmacy-header/pharmacy-header.component.tsx +5 -3
- package/src/pharmacy-header/pharmacy-header.scss +2 -2
- package/src/pharmacy-header/pharmacy-illustration.component.tsx +4 -4
- package/src/prescriptions/prescription-details.component.tsx +139 -0
- package/src/{components → prescriptions}/prescription-details.scss +13 -3
- package/src/{components → prescriptions}/prescription-expanded.component.tsx +9 -36
- package/src/{components → prescriptions}/prescription-expanded.scss +0 -18
- package/src/prescriptions/prescription-tab-lists.component.tsx +64 -18
- package/src/prescriptions/prescription-tab-panel.component.tsx +46 -26
- package/src/prescriptions/prescriptions.scss +8 -0
- package/src/routes.json +30 -0
- package/src/types.ts +196 -71
- package/src/utils.test.ts +3001 -0
- package/src/utils.ts +663 -42
- package/translations/en.json +60 -2
- package/translations/fr.json +100 -0
- package/.github/workflows/node.js.yml +0 -95
- package/dist/247.js +0 -1
- package/dist/294.js +0 -2
- package/dist/294.js.LICENSE.txt +0 -9
- package/dist/299.js +0 -1
- package/dist/484.js +0 -1
- package/dist/574.js +0 -1
- package/dist/595.js +0 -2
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/781.js +0 -1
- package/dist/900.js +0 -2
- package/dist/900.js.LICENSE.txt +0 -29
- package/dist/935.js +0 -2
- package/dist/935.js.LICENSE.txt +0 -19
- package/dist/96.js +0 -1
- package/dist/main.js +0 -1
- package/dist/openmrs-esm-dispensing-app.js +0 -1
- package/dist/openmrs-esm-dispensing-app.js.buildmanifest.json +0 -330
- package/dist/openmrs-esm-dispensing-app.old +0 -1
- package/jest.config.json +0 -18
- package/src/components/history-and-comments.component.tsx +0 -117
- package/src/components/history-and-comments.scss +0 -30
- package/src/components/medication-dispense-review.component.tsx +0 -263
- package/src/components/medication-event-card.component.tsx +0 -90
- package/src/components/prescription-details.component.tsx +0 -67
- package/src/forms/initialize-dispense-form-from-requests.component.tsx +0 -56
package/README.md
CHANGED
|
@@ -5,6 +5,40 @@
|
|
|
5
5
|
This repository is for the OpenMRS Dispensing App. For more information, please see the
|
|
6
6
|
[Design Documentation](https://wiki.openmrs.org/display/projects/Dispensing+Design+Components).
|
|
7
7
|
|
|
8
|
+
## Required Configuration
|
|
9
|
+
|
|
10
|
+
The Dispensing ESM requires the "HL7-MedicationDispenseStatus" concept source and FHIR concept source, as well as "Medication Dispense Status" value/concept set, to be installed in your OpenMRS instance to properly map dispensing statuses.
|
|
11
|
+
|
|
12
|
+
References to the Iniz files that install these concept sources can be found here:
|
|
13
|
+
|
|
14
|
+
https://github.com/openmrs/openmrs-distro-referenceapplication/blob/main/distro/configuration/conceptsources/conceptsources-core_data.csv#L24
|
|
15
|
+
|
|
16
|
+
https://openmrs.slack.com/archives/C02P790SM7X/p1692380047339679?thread_ts=1692338258.965499&cid=C02P790SM7X
|
|
17
|
+
|
|
18
|
+
The Medication Dispense Status value set can be found here:
|
|
19
|
+
|
|
20
|
+
https://app.openconceptlab.org/#/search/?q=medication+dispense+status
|
|
21
|
+
|
|
22
|
+
It also is bundled in the "DrugDispense" OCL package provided by the Reference Application:
|
|
23
|
+
|
|
24
|
+
https://github.com/openmrs/openmrs-distro-referenceapplication/tree/main/distro/configuration/ocl
|
|
25
|
+
|
|
26
|
+
The "DrugDispense" also provides the default value sets for the "Substitution Type", "Substitution Reason", and "Medication Dispense Status Reason".
|
|
27
|
+
|
|
28
|
+
The "Substitution Type" and "Substitution Reason" value sets define the valid answers for both the equivalent questions when substituting a drug. The "Medication Dispense Status Reason" provides the default answers for both the "Reason for Pause" and "Reason for Close" questions.
|
|
29
|
+
|
|
30
|
+
All of these can be customized via the config-schema, see: https://github.com/openmrs/openmrs-esm-dispensing-app/blob/main/src/config-schema.ts
|
|
31
|
+
|
|
32
|
+
## Required privileges
|
|
33
|
+
|
|
34
|
+
Note that following privileges need to be installed and assigned to roles:
|
|
35
|
+
|
|
36
|
+
* "Task: dispensing.create.dispense" - Allows user to Dispense Medication
|
|
37
|
+
* "Task: dispensing.create.dispense.andModifyDetails" - Allows user to modify the Quantity, Drug, Formulation and Dose Instructions (from the values specified in the Order / Medication Request) when Dispensing
|
|
38
|
+
* "Task: dispensing.edit.dispense" - Allows user to edit an existing Medication Dispense
|
|
39
|
+
* "Task: dispensing.delete.dispense" - Allows user to delete an existing Medication Dispense
|
|
40
|
+
* "Task: dispensing.delete.dispense.ifCreator" - Allows user to delete an existing Medication Dispense, *but only* if they created it originally
|
|
41
|
+
|
|
8
42
|
## Running this code
|
|
9
43
|
|
|
10
44
|
```sh
|
|
@@ -30,3 +64,4 @@ For more information, please see the
|
|
|
30
64
|
|
|
31
65
|
In particular, the [Setup](https://openmrs.github.io/openmrs-esm-core/#/getting_started/setup)
|
|
32
66
|
section can help you get started developing microfrontends in general.
|
|
67
|
+
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
transform: {
|
|
3
|
+
"^.+\\.tsx?$": "@swc/jest",
|
|
4
|
+
},
|
|
5
|
+
transformIgnorePatterns: ["/node_modules/(?!@openmrs)"],
|
|
6
|
+
moduleNameMapper: {
|
|
7
|
+
"@openmrs/esm-framework": "@openmrs/esm-framework/mock",
|
|
8
|
+
"\\.(s?css)$": "identity-obj-proxy",
|
|
9
|
+
"^lodash-es/(.*)$": "lodash/$1",
|
|
10
|
+
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js",
|
|
11
|
+
dexie: require.resolve("dexie"),
|
|
12
|
+
},
|
|
13
|
+
setupFilesAfterEnv: ["<rootDir>/src/setup-tests.ts"],
|
|
14
|
+
testEnvironment: "jsdom",
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-dispensing-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Medication dispensing application",
|
|
6
6
|
"browser": "dist/openmrs-esm-dispensing-app.js",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"analyze": "webpack --mode=production --env.analyze=true",
|
|
14
14
|
"prettier": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
15
15
|
"lint": "eslint src --ext js,jsx,ts,tsx",
|
|
16
|
-
"test": "jest --config jest.config.
|
|
16
|
+
"test": "jest --config jest.config.js",
|
|
17
17
|
"typescript": "tsc",
|
|
18
18
|
"verify": "concurrently 'yarn:lint' 'yarn:test' 'yarn:typescript'",
|
|
19
19
|
"coverage": "yarn test --coverage",
|
|
20
|
-
"
|
|
20
|
+
"postinstall": "husky install"
|
|
21
21
|
},
|
|
22
22
|
"browserslist": [
|
|
23
23
|
"extends browserslist-config-openmrs"
|
|
@@ -41,17 +41,16 @@
|
|
|
41
41
|
"url": "https://github.com/openmrs/openmrs-esm-dispensing/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@carbon/react": "
|
|
44
|
+
"@carbon/react": "1.12.0",
|
|
45
45
|
"lodash-es": "^4.17.15",
|
|
46
|
-
"swr": "^1.
|
|
46
|
+
"swr": "^2.1.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@openmrs/esm-framework": "
|
|
49
|
+
"@openmrs/esm-framework": "*",
|
|
50
50
|
"dayjs": "1.x",
|
|
51
51
|
"react": "18.x",
|
|
52
52
|
"react-dom": "18.x",
|
|
53
53
|
"react-i18next": "11.x",
|
|
54
|
-
"react-router-dom": "6.x",
|
|
55
54
|
"rxjs": "6.x"
|
|
56
55
|
},
|
|
57
56
|
"devDependencies": {
|
|
@@ -70,10 +69,12 @@
|
|
|
70
69
|
"@types/webpack-env": "^1.16.0",
|
|
71
70
|
"@typescript-eslint/parser": "^4.26.0",
|
|
72
71
|
"concurrently": "^6.2.0",
|
|
72
|
+
"dayjs": "1.x",
|
|
73
73
|
"eslint": "^7.27.0",
|
|
74
74
|
"eslint-config-prettier": "^8.3.0",
|
|
75
75
|
"eslint-config-ts-react-important-stuff": "^3.0.0",
|
|
76
76
|
"eslint-plugin-prettier": "^3.4.0",
|
|
77
|
+
"eslint-plugin-unused-imports": "^2.0.0",
|
|
77
78
|
"husky": "^6.0.0",
|
|
78
79
|
"identity-obj-proxy": "^3.0.0",
|
|
79
80
|
"jest": "^28.1.2",
|
|
@@ -87,6 +88,8 @@
|
|
|
87
88
|
"react-i18next": "^11.7.0",
|
|
88
89
|
"react-router-dom": "^6.3.0",
|
|
89
90
|
"rxjs": "^6.5.4",
|
|
90
|
-
"typescript": "^4.3.2"
|
|
91
|
-
|
|
92
|
-
}
|
|
91
|
+
"typescript": "^4.3.2",
|
|
92
|
+
"webpack": "^5.88.0"
|
|
93
|
+
},
|
|
94
|
+
"packageManager": "yarn@3.6.0"
|
|
95
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import ActionButtons from "./action-buttons.component";
|
|
4
|
+
import { MedicationRequest, MedicationRequestStatus } from "../types";
|
|
5
|
+
import { useConfig } from "@openmrs/esm-framework";
|
|
6
|
+
|
|
7
|
+
const mockedUseConfig = useConfig as jest.Mock;
|
|
8
|
+
const mockPatientUuid = "558494fe-5850-4b34-a3bf-06550334ba4a";
|
|
9
|
+
const mockEncounterUuid = "7aee7123-9e50-4f72-a636-895d77a63e98";
|
|
10
|
+
|
|
11
|
+
describe("Action Buttons Component tests", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockedUseConfig.mockReturnValue({
|
|
14
|
+
medicationRequestExpirationPeriodInDays: 90,
|
|
15
|
+
actionButtons: {
|
|
16
|
+
pauseButton: {
|
|
17
|
+
enabled: true,
|
|
18
|
+
},
|
|
19
|
+
closeButton: {
|
|
20
|
+
enabled: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
dispenseBehavior: {
|
|
24
|
+
allowModifyingPrescription: false,
|
|
25
|
+
restrictTotalQuantityDispensed: false,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("component should render dispense button if active medication", () => {
|
|
31
|
+
// status = active, and validity period start set to current datetime
|
|
32
|
+
const medicationRequest: MedicationRequest = {
|
|
33
|
+
resourceType: "MedicationRequest",
|
|
34
|
+
id: "d4f69a68-1171-4e47-8693-478df18daf40",
|
|
35
|
+
meta: {
|
|
36
|
+
lastUpdated: "2023-01-24T19:02:04.000-05:00",
|
|
37
|
+
},
|
|
38
|
+
status: MedicationRequestStatus.active,
|
|
39
|
+
intent: "order",
|
|
40
|
+
priority: "routine",
|
|
41
|
+
medicationReference: {
|
|
42
|
+
reference: "Medication/c8d3444c-41a4-48d3-9ec1-811fe7b27d99",
|
|
43
|
+
type: "Medication",
|
|
44
|
+
display: "Ascorbic acid (Vitamin C), 250mg tablet",
|
|
45
|
+
},
|
|
46
|
+
subject: {
|
|
47
|
+
reference: "Patient/558494fe-5850-4b34-a3bf-06550334ba4a",
|
|
48
|
+
type: "Patient",
|
|
49
|
+
display: "Dylan, Bob (ZL EMR ID: Y2CK2G)",
|
|
50
|
+
},
|
|
51
|
+
encounter: {
|
|
52
|
+
reference: "Encounter/7aee7123-9e50-4f72-a636-895d77a63e98",
|
|
53
|
+
type: "Encounter",
|
|
54
|
+
},
|
|
55
|
+
requester: {
|
|
56
|
+
reference: "Practitioner/19e06fb0-22ea-4dd4-aafd-da14c14a1487",
|
|
57
|
+
type: "Practitioner",
|
|
58
|
+
identifier: {
|
|
59
|
+
value: "MAADH",
|
|
60
|
+
},
|
|
61
|
+
display: "Goodrich, Mark (Identifier: MAADH)",
|
|
62
|
+
},
|
|
63
|
+
dosageInstruction: [
|
|
64
|
+
{
|
|
65
|
+
text: "test",
|
|
66
|
+
timing: {
|
|
67
|
+
repeat: {
|
|
68
|
+
duration: 6,
|
|
69
|
+
durationUnit: "d",
|
|
70
|
+
},
|
|
71
|
+
code: {
|
|
72
|
+
coding: [
|
|
73
|
+
{
|
|
74
|
+
code: "37328251-6759-4270-8a2e-8cab2c0b315b",
|
|
75
|
+
display: "OD (once daily)",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
system: "http://snomed.info/sct",
|
|
79
|
+
code: "229797004",
|
|
80
|
+
display: "OD (once daily)",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
text: "OD (once daily)",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
asNeededBoolean: false,
|
|
87
|
+
route: {
|
|
88
|
+
coding: [
|
|
89
|
+
{
|
|
90
|
+
code: "46aaaca8-1f21-410a-aac9-67bfcc1fd577",
|
|
91
|
+
display: "Oral",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
system: "http://snomed.info/sct",
|
|
95
|
+
code: "26643006",
|
|
96
|
+
display: "Oral",
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
text: "Oral",
|
|
100
|
+
},
|
|
101
|
+
doseAndRate: [
|
|
102
|
+
{
|
|
103
|
+
doseQuantity: {
|
|
104
|
+
value: 5.0,
|
|
105
|
+
unit: "Ampule(s)",
|
|
106
|
+
system: "http://snomed.info/sct",
|
|
107
|
+
code: "413516001",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
dispenseRequest: {
|
|
114
|
+
validityPeriod: {
|
|
115
|
+
start: new Date().toISOString(),
|
|
116
|
+
},
|
|
117
|
+
numberOfRepeatsAllowed: 8,
|
|
118
|
+
quantity: {
|
|
119
|
+
value: 7.0,
|
|
120
|
+
unit: "Application",
|
|
121
|
+
system: "http://snomed.info/sct",
|
|
122
|
+
code: "413568008",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const { getByText, container } = render(
|
|
128
|
+
<ActionButtons
|
|
129
|
+
patientUuid={mockPatientUuid}
|
|
130
|
+
encounterUuid={mockEncounterUuid}
|
|
131
|
+
medicationRequestBundle={{ request: medicationRequest, dispenses: [] }}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
expect(getByText("Dispense")).toBeInTheDocument();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// status = active, but validity period start time years in the past
|
|
138
|
+
test("component should not render dispense button if expired medication", () => {
|
|
139
|
+
// status = active, and validity period start set to current datetime
|
|
140
|
+
const medicationRequest: MedicationRequest = {
|
|
141
|
+
resourceType: "MedicationRequest",
|
|
142
|
+
id: "d4f69a68-1171-4e47-8693-478df18daf40",
|
|
143
|
+
meta: {
|
|
144
|
+
lastUpdated: "2023-01-24T19:02:04.000-05:00",
|
|
145
|
+
},
|
|
146
|
+
status: MedicationRequestStatus.active,
|
|
147
|
+
intent: "order",
|
|
148
|
+
priority: "routine",
|
|
149
|
+
medicationReference: {
|
|
150
|
+
reference: "Medication/c8d3444c-41a4-48d3-9ec1-811fe7b27d99",
|
|
151
|
+
type: "Medication",
|
|
152
|
+
display: "Ascorbic acid (Vitamin C), 250mg tablet",
|
|
153
|
+
},
|
|
154
|
+
subject: {
|
|
155
|
+
reference: "Patient/558494fe-5850-4b34-a3bf-06550334ba4a",
|
|
156
|
+
type: "Patient",
|
|
157
|
+
display: "Dylan, Bob (ZL EMR ID: Y2CK2G)",
|
|
158
|
+
},
|
|
159
|
+
encounter: {
|
|
160
|
+
reference: "Encounter/7aee7123-9e50-4f72-a636-895d77a63e98",
|
|
161
|
+
type: "Encounter",
|
|
162
|
+
},
|
|
163
|
+
requester: {
|
|
164
|
+
reference: "Practitioner/19e06fb0-22ea-4dd4-aafd-da14c14a1487",
|
|
165
|
+
type: "Practitioner",
|
|
166
|
+
identifier: {
|
|
167
|
+
value: "MAADH",
|
|
168
|
+
},
|
|
169
|
+
display: "Goodrich, Mark (Identifier: MAADH)",
|
|
170
|
+
},
|
|
171
|
+
dosageInstruction: [
|
|
172
|
+
{
|
|
173
|
+
text: "test",
|
|
174
|
+
timing: {
|
|
175
|
+
repeat: {
|
|
176
|
+
duration: 6,
|
|
177
|
+
durationUnit: "d",
|
|
178
|
+
},
|
|
179
|
+
code: {
|
|
180
|
+
coding: [
|
|
181
|
+
{
|
|
182
|
+
code: "37328251-6759-4270-8a2e-8cab2c0b315b",
|
|
183
|
+
display: "OD (once daily)",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
system: "http://snomed.info/sct",
|
|
187
|
+
code: "229797004",
|
|
188
|
+
display: "OD (once daily)",
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
text: "OD (once daily)",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
asNeededBoolean: false,
|
|
195
|
+
route: {
|
|
196
|
+
coding: [
|
|
197
|
+
{
|
|
198
|
+
code: "46aaaca8-1f21-410a-aac9-67bfcc1fd577",
|
|
199
|
+
display: "Oral",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
system: "http://snomed.info/sct",
|
|
203
|
+
code: "26643006",
|
|
204
|
+
display: "Oral",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
text: "Oral",
|
|
208
|
+
},
|
|
209
|
+
doseAndRate: [
|
|
210
|
+
{
|
|
211
|
+
doseQuantity: {
|
|
212
|
+
value: 5.0,
|
|
213
|
+
unit: "Ampule(s)",
|
|
214
|
+
system: "http://snomed.info/sct",
|
|
215
|
+
code: "413516001",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
dispenseRequest: {
|
|
222
|
+
validityPeriod: {
|
|
223
|
+
start: "2019-01-24T19:02:04.000-05:00",
|
|
224
|
+
},
|
|
225
|
+
numberOfRepeatsAllowed: 8,
|
|
226
|
+
quantity: {
|
|
227
|
+
value: 7.0,
|
|
228
|
+
unit: "Application",
|
|
229
|
+
system: "http://snomed.info/sct",
|
|
230
|
+
code: "413568008",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const { queryByText, container } = render(
|
|
236
|
+
<ActionButtons
|
|
237
|
+
patientUuid={mockPatientUuid}
|
|
238
|
+
encounterUuid={mockEncounterUuid}
|
|
239
|
+
medicationRequestBundle={{ request: medicationRequest, dispenses: [] }}
|
|
240
|
+
/>
|
|
241
|
+
);
|
|
242
|
+
expect(queryByText("Dispense")).not.toBeInTheDocument();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button } from "@carbon/react";
|
|
3
|
+
import { useConfig, useSession } from "@openmrs/esm-framework";
|
|
4
|
+
import styles from "./action-buttons.scss";
|
|
5
|
+
import { useTranslation } from "react-i18next";
|
|
6
|
+
import {
|
|
7
|
+
MedicationDispenseStatus,
|
|
8
|
+
MedicationRequestBundle,
|
|
9
|
+
MedicationRequestStatus,
|
|
10
|
+
} from "../types";
|
|
11
|
+
import { PharmacyConfig } from "../config-schema";
|
|
12
|
+
import { launchOverlay } from "../hooks/useOverlay";
|
|
13
|
+
import {
|
|
14
|
+
computeMedicationRequestStatus,
|
|
15
|
+
computeQuantityRemaining,
|
|
16
|
+
getMostRecentMedicationDispenseStatus,
|
|
17
|
+
} from "../utils";
|
|
18
|
+
import DispenseForm from "../forms/dispense-form.component";
|
|
19
|
+
import { initiateMedicationDispenseBody } from "../medication-dispense/medication-dispense.resource";
|
|
20
|
+
import PauseDispenseForm from "../forms/pause-dispense-form.component";
|
|
21
|
+
import CloseDispenseForm from "../forms/close-dispense-form.component";
|
|
22
|
+
|
|
23
|
+
interface ActionButtonsProps {
|
|
24
|
+
medicationRequestBundle: MedicationRequestBundle;
|
|
25
|
+
patientUuid: string;
|
|
26
|
+
encounterUuid: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const ActionButtons: React.FC<ActionButtonsProps> = ({
|
|
30
|
+
medicationRequestBundle,
|
|
31
|
+
patientUuid,
|
|
32
|
+
encounterUuid,
|
|
33
|
+
}) => {
|
|
34
|
+
const { t } = useTranslation();
|
|
35
|
+
const config = useConfig() as PharmacyConfig;
|
|
36
|
+
const session = useSession();
|
|
37
|
+
const mostRecentMedicationDispenseStatus: MedicationDispenseStatus =
|
|
38
|
+
getMostRecentMedicationDispenseStatus(medicationRequestBundle.dispenses);
|
|
39
|
+
const medicationRequestStatus = computeMedicationRequestStatus(
|
|
40
|
+
medicationRequestBundle.request,
|
|
41
|
+
config.medicationRequestExpirationPeriodInDays
|
|
42
|
+
);
|
|
43
|
+
const dispensable =
|
|
44
|
+
medicationRequestStatus === MedicationRequestStatus.active &&
|
|
45
|
+
mostRecentMedicationDispenseStatus !== MedicationDispenseStatus.declined;
|
|
46
|
+
|
|
47
|
+
const pauseable =
|
|
48
|
+
config.actionButtons.pauseButton.enabled &&
|
|
49
|
+
medicationRequestStatus === MedicationRequestStatus.active &&
|
|
50
|
+
mostRecentMedicationDispenseStatus !== MedicationDispenseStatus.on_hold &&
|
|
51
|
+
mostRecentMedicationDispenseStatus !== MedicationDispenseStatus.declined;
|
|
52
|
+
|
|
53
|
+
const closeable =
|
|
54
|
+
config.actionButtons.closeButton.enabled &&
|
|
55
|
+
medicationRequestStatus === MedicationRequestStatus.active &&
|
|
56
|
+
mostRecentMedicationDispenseStatus !== MedicationDispenseStatus.declined;
|
|
57
|
+
|
|
58
|
+
let quantityRemaining = null;
|
|
59
|
+
if (config.dispenseBehavior.restrictTotalQuantityDispensed) {
|
|
60
|
+
quantityRemaining = computeQuantityRemaining(medicationRequestBundle);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={styles.actionBtns}>
|
|
65
|
+
{dispensable ? (
|
|
66
|
+
<Button
|
|
67
|
+
kind="primary"
|
|
68
|
+
onClick={() =>
|
|
69
|
+
launchOverlay(
|
|
70
|
+
t("dispensePrescription", "Dispense prescription"),
|
|
71
|
+
<DispenseForm
|
|
72
|
+
patientUuid={patientUuid}
|
|
73
|
+
encounterUuid={encounterUuid}
|
|
74
|
+
medicationDispense={initiateMedicationDispenseBody(
|
|
75
|
+
medicationRequestBundle.request,
|
|
76
|
+
session,
|
|
77
|
+
true
|
|
78
|
+
)}
|
|
79
|
+
medicationRequestBundle={medicationRequestBundle}
|
|
80
|
+
quantityRemaining={quantityRemaining}
|
|
81
|
+
mode="enter"
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
{t("dispense", "Dispense")}
|
|
87
|
+
</Button>
|
|
88
|
+
) : null}
|
|
89
|
+
{pauseable ? (
|
|
90
|
+
<Button
|
|
91
|
+
kind="secondary"
|
|
92
|
+
onClick={() =>
|
|
93
|
+
launchOverlay(
|
|
94
|
+
t("pausePrescription", "Pause prescription"),
|
|
95
|
+
<PauseDispenseForm
|
|
96
|
+
patientUuid={patientUuid}
|
|
97
|
+
encounterUuid={encounterUuid}
|
|
98
|
+
medicationDispense={initiateMedicationDispenseBody(
|
|
99
|
+
medicationRequestBundle.request,
|
|
100
|
+
session,
|
|
101
|
+
false
|
|
102
|
+
)}
|
|
103
|
+
mode="enter"
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
>
|
|
108
|
+
{t("pause", "Pause")}
|
|
109
|
+
</Button>
|
|
110
|
+
) : null}
|
|
111
|
+
{closeable ? (
|
|
112
|
+
<Button
|
|
113
|
+
kind="danger"
|
|
114
|
+
onClick={() =>
|
|
115
|
+
launchOverlay(
|
|
116
|
+
t("closePrescription", "Close prescription"),
|
|
117
|
+
<CloseDispenseForm
|
|
118
|
+
patientUuid={patientUuid}
|
|
119
|
+
encounterUuid={encounterUuid}
|
|
120
|
+
medicationDispense={initiateMedicationDispenseBody(
|
|
121
|
+
medicationRequestBundle.request,
|
|
122
|
+
session,
|
|
123
|
+
false
|
|
124
|
+
)}
|
|
125
|
+
mode="enter"
|
|
126
|
+
/>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
>
|
|
130
|
+
{t("close", "Close")}
|
|
131
|
+
</Button>
|
|
132
|
+
) : null}
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export default ActionButtons;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { MedicationReferenceOrCodeableConcept } from "../types";
|
|
4
|
+
import MedicationCard from "./medication-card.component";
|
|
5
|
+
|
|
6
|
+
describe("Medication Card Component tests", () => {
|
|
7
|
+
test("component should render medication card without edit action button", () => {
|
|
8
|
+
const medication: MedicationReferenceOrCodeableConcept = {
|
|
9
|
+
medicationReference: {
|
|
10
|
+
display: "Some Medication",
|
|
11
|
+
reference: "",
|
|
12
|
+
type: "",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const { getByText, container } = render(
|
|
17
|
+
<MedicationCard medication={medication} />
|
|
18
|
+
);
|
|
19
|
+
expect(getByText("Some Medication")).toBeInTheDocument();
|
|
20
|
+
expect(container.querySelector("svg")).not.toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("component should render medication card with edit action button", () => {
|
|
24
|
+
const medication: MedicationReferenceOrCodeableConcept = {
|
|
25
|
+
medicationReference: {
|
|
26
|
+
display: "Some Medication",
|
|
27
|
+
reference: "",
|
|
28
|
+
type: "",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const action = () => 0;
|
|
33
|
+
|
|
34
|
+
const { getByText, container } = render(
|
|
35
|
+
<MedicationCard medication={medication} editAction={action} />
|
|
36
|
+
);
|
|
37
|
+
expect(getByText("Some Medication")).toBeInTheDocument();
|
|
38
|
+
expect(container.querySelector("svg")).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Tile } from "@carbon/react";
|
|
3
|
-
import {
|
|
3
|
+
import { Edit } from "@carbon/react/icons";
|
|
4
|
+
import { MedicationReferenceOrCodeableConcept } from "../types";
|
|
4
5
|
import styles from "./medication-card.scss";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
// TODO this will probably need a fair amount of updates to allow editing
|
|
6
|
+
import { getMedicationDisplay } from "../utils";
|
|
8
7
|
|
|
9
8
|
const MedicationCard: React.FC<{
|
|
10
|
-
medication:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
medication: MedicationReferenceOrCodeableConcept;
|
|
10
|
+
editAction?: Function;
|
|
11
|
+
}> = ({ medication, editAction }) => {
|
|
14
12
|
return (
|
|
15
13
|
<Tile className={styles.medicationTile}>
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
? medication.medicationReference.display
|
|
21
|
-
: medication?.medicationCodeableConcept.text}
|
|
22
|
-
</strong>
|
|
23
|
-
</p>
|
|
24
|
-
</div>
|
|
14
|
+
<p className={styles.medicationName}>
|
|
15
|
+
<strong>{getMedicationDisplay(medication)}</strong>
|
|
16
|
+
</p>
|
|
17
|
+
{editAction && <Edit onClick={editAction} />}
|
|
25
18
|
</Tile>
|
|
26
19
|
);
|
|
27
20
|
};
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
@import "~@openmrs/esm-styleguide/src/vars";
|
|
4
4
|
|
|
5
5
|
.medicationTile {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: row;
|
|
8
|
+
justify-content: space-between;
|
|
6
9
|
width: 100%;
|
|
7
10
|
margin: 2px 0 8px;
|
|
8
|
-
padding: 0
|
|
11
|
+
padding: 0 8px 0 8px;
|
|
9
12
|
background-color: #fff;
|
|
10
|
-
border-left: 4px solid
|
|
13
|
+
border-left: 4px solid var(--brand-03);
|
|
11
14
|
color: $text-02;
|
|
12
15
|
margin-bottom: 1rem !important;
|
|
13
16
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
.medicationDispenseReviewContainer {
|
|
6
6
|
display: flex;
|
|
7
7
|
flex-direction: column;
|
|
8
|
+
padding-bottom: 10px;
|
|
8
9
|
|
|
9
10
|
:global(.cds--css-grid) {
|
|
10
11
|
padding-left:0 !important;
|
|
@@ -19,6 +20,14 @@
|
|
|
19
20
|
gap: spacing.$spacing-05;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
.substitutionReason {
|
|
24
|
+
width:100%;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.substitutionType {
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
.productiveHeading02 {
|
|
23
32
|
color: $color-gray-70;
|
|
24
33
|
@include type.type-style('productive-heading-02');
|