@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.
Files changed (86) hide show
  1. package/.eslintrc +3 -1
  2. package/.husky/pre-commit +0 -0
  3. package/.husky/pre-push +0 -0
  4. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
  5. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +550 -0
  6. package/README.md +35 -0
  7. package/jest.config.js +15 -0
  8. package/package.json +13 -10
  9. package/src/components/action-buttons.component.test.tsx +244 -0
  10. package/src/components/action-buttons.component.tsx +137 -0
  11. package/src/components/action-buttons.scss +5 -0
  12. package/src/components/medication-card.component.test.tsx +40 -0
  13. package/src/components/medication-card.component.tsx +10 -17
  14. package/src/components/medication-card.scss +5 -2
  15. package/src/components/medication-dispense-review.scss +9 -0
  16. package/src/components/medication-event.component.tsx +94 -0
  17. package/src/components/{medication-event-card.scss → medication-event.scss} +0 -9
  18. package/src/components/patient-details.component.tsx +4 -27
  19. package/src/components/patient-details.scss +0 -3
  20. package/src/config-schema.ts +141 -2
  21. package/src/constants.ts +24 -0
  22. package/src/dashboard/dispensing-dashboard.component.tsx +38 -0
  23. package/src/declarations.d.tsx +2 -0
  24. package/src/dispensing-tiles/dispensing-tiles.resource.tsx +2 -0
  25. package/src/dispensing.component.tsx +2 -12
  26. package/src/dispensing.test.tsx +1 -1
  27. package/src/forms/close-dispense-form.component.tsx +236 -0
  28. package/src/forms/dispense-form.component.tsx +124 -142
  29. package/src/forms/{dispense-form.scss → forms.scss} +6 -0
  30. package/src/forms/medication-dispense-review.component.test.tsx +142 -0
  31. package/src/forms/medication-dispense-review.component.tsx +562 -0
  32. package/src/forms/overlay/overlay.component.tsx +3 -1
  33. package/src/forms/pause-dispense-form.component.tsx +236 -0
  34. package/src/history/history-and-comments.component.tsx +326 -0
  35. package/src/history/history-and-comments.scss +57 -0
  36. package/src/index.ts +20 -35
  37. package/src/location/location.resource.test.tsx +189 -0
  38. package/src/location/location.resource.tsx +27 -0
  39. package/src/medication/medication.resource.test.tsx +181 -0
  40. package/src/medication/medication.resource.tsx +54 -0
  41. package/src/medication-dispense/medication-dispense.resource.test.tsx +294 -0
  42. package/src/medication-dispense/medication-dispense.resource.tsx +113 -45
  43. package/src/medication-request/medication-request.resource.test.tsx +1389 -0
  44. package/src/medication-request/medication-request.resource.tsx +145 -84
  45. package/src/pharmacy-header/pharmacy-header.component.tsx +5 -3
  46. package/src/pharmacy-header/pharmacy-header.scss +2 -2
  47. package/src/pharmacy-header/pharmacy-illustration.component.tsx +4 -4
  48. package/src/prescriptions/prescription-details.component.tsx +139 -0
  49. package/src/{components → prescriptions}/prescription-details.scss +13 -3
  50. package/src/{components → prescriptions}/prescription-expanded.component.tsx +9 -36
  51. package/src/{components → prescriptions}/prescription-expanded.scss +0 -18
  52. package/src/prescriptions/prescription-tab-lists.component.tsx +64 -18
  53. package/src/prescriptions/prescription-tab-panel.component.tsx +46 -26
  54. package/src/prescriptions/prescriptions.scss +8 -0
  55. package/src/routes.json +30 -0
  56. package/src/types.ts +196 -71
  57. package/src/utils.test.ts +3001 -0
  58. package/src/utils.ts +663 -42
  59. package/translations/en.json +60 -2
  60. package/translations/fr.json +100 -0
  61. package/.github/workflows/node.js.yml +0 -95
  62. package/dist/247.js +0 -1
  63. package/dist/294.js +0 -2
  64. package/dist/294.js.LICENSE.txt +0 -9
  65. package/dist/299.js +0 -1
  66. package/dist/484.js +0 -1
  67. package/dist/574.js +0 -1
  68. package/dist/595.js +0 -2
  69. package/dist/595.js.LICENSE.txt +0 -1
  70. package/dist/781.js +0 -1
  71. package/dist/900.js +0 -2
  72. package/dist/900.js.LICENSE.txt +0 -29
  73. package/dist/935.js +0 -2
  74. package/dist/935.js.LICENSE.txt +0 -19
  75. package/dist/96.js +0 -1
  76. package/dist/main.js +0 -1
  77. package/dist/openmrs-esm-dispensing-app.js +0 -1
  78. package/dist/openmrs-esm-dispensing-app.js.buildmanifest.json +0 -330
  79. package/dist/openmrs-esm-dispensing-app.old +0 -1
  80. package/jest.config.json +0 -18
  81. package/src/components/history-and-comments.component.tsx +0 -117
  82. package/src/components/history-and-comments.scss +0 -30
  83. package/src/components/medication-dispense-review.component.tsx +0 -263
  84. package/src/components/medication-event-card.component.tsx +0 -90
  85. package/src/components/prescription-details.component.tsx +0 -67
  86. package/src/forms/initialize-dispense-form-from-requests.component.tsx +0 -56
@@ -0,0 +1,142 @@
1
+ import { render } from "@testing-library/react";
2
+ import React from "react";
3
+ import { MedicationDispense, MedicationDispenseStatus } from "../types";
4
+ import MedicationDispenseReview from "./medication-dispense-review.component";
5
+
6
+ jest.mock("@openmrs/esm-framework", () => {
7
+ const originalModule = jest.requireActual("@openmrs/esm-framework");
8
+ return {
9
+ __esModule: true,
10
+ ...originalModule,
11
+ useConfig: jest.fn(() => ({
12
+ dispenseBehavior: {
13
+ allowModifyingPrescription: false,
14
+ restrictTotalQuantityDispensed: false,
15
+ },
16
+ valueSets: {
17
+ substitutionType: { uuid: "123" },
18
+ substitutionReason: { uuid: "abc" },
19
+ },
20
+ })),
21
+ };
22
+ });
23
+
24
+ describe("Medication Dispense Review Component tests", () => {
25
+ test("component should render medication dispense review", () => {
26
+ const medicationDispense: MedicationDispense = {
27
+ resourceType: "MedicationDispense",
28
+ id: "ff2fa24c-b0d4-457c-bbdf-7d6512b8b746",
29
+ type: {
30
+ coding: [
31
+ {
32
+ code: "123",
33
+ },
34
+ ],
35
+ },
36
+ meta: {
37
+ lastUpdated: "2023-01-10T10:52:36.000-05:00",
38
+ },
39
+ status: MedicationDispenseStatus.completed,
40
+ medicationReference: {
41
+ reference: "Medication/78f96684-dfbe-11e9-8a34-2a2ae2dbcce4",
42
+ type: "Medication",
43
+ display: "Lamivudine (3TC), Oral solution, 10mg/mL, 240mL bottle",
44
+ },
45
+ subject: {
46
+ reference: "Patient/b36dbb26-8309-4d51-80ed-07bbb63ab928",
47
+ type: "Patient",
48
+ display: "Test, Fiona (ZL EMR ID: Y480K4)",
49
+ },
50
+ performer: [
51
+ {
52
+ actor: {
53
+ reference: "Practitioner/6558dfef-bfe1-488e-b048-ed286aded924",
54
+ type: "Practitioner",
55
+ identifier: {
56
+ value: "LADAL",
57
+ },
58
+ display: "Dylan, Bob (Identifier: LADAL)",
59
+ },
60
+ },
61
+ ],
62
+ location: {
63
+ reference: "Location/47521fa1-ac7f-4fdb-b167-a86fe223a3d9",
64
+ type: "Location",
65
+ display: "Klinik MNT",
66
+ },
67
+ authorizingPrescription: [
68
+ {
69
+ reference: "MedicationRequest/9e416e5f-a435-433c-99d3-8b2fd1e8a421",
70
+ type: "MedicationRequest",
71
+ },
72
+ ],
73
+ quantity: {
74
+ value: 30.0,
75
+ unit: "Tablet",
76
+ system: "http://snomed.info/sct",
77
+ code: "385055001",
78
+ },
79
+ whenPrepared: "2023-01-10T10:52:22-05:00",
80
+ whenHandedOver: "2023-01-10T10:52:22-05:00",
81
+ dosageInstruction: [
82
+ {
83
+ timing: {
84
+ code: {
85
+ coding: [
86
+ {
87
+ code: "023bee9c-2dbb-483c-8401-46f0ccf6b333",
88
+ display: "ON (night)",
89
+ },
90
+ ],
91
+ text: "ON (night)",
92
+ },
93
+ },
94
+ asNeededBoolean: false,
95
+ route: {
96
+ coding: [
97
+ {
98
+ code: "46aaaca8-1f21-410a-aac9-67bfcc1fd577",
99
+ display: "Oral",
100
+ },
101
+ {
102
+ system: "https://openconceptlab.org/orgs/CIEL/sources/CIEL",
103
+ code: "160240",
104
+ display: "Oral",
105
+ },
106
+ {
107
+ system: "http://snomed.info/sct",
108
+ code: "26643006",
109
+ display: "Oral",
110
+ },
111
+ ],
112
+ text: "Oral",
113
+ },
114
+ doseAndRate: [
115
+ {
116
+ doseQuantity: {
117
+ value: 1.0,
118
+ unit: "Tablet",
119
+ system: "http://snomed.info/sct",
120
+ code: "385055001",
121
+ },
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ substitution: {
127
+ wasSubstituted: false,
128
+ },
129
+ };
130
+
131
+ const mockUpdate: Function = jest.fn();
132
+ const { getByText, container } = render(
133
+ <MedicationDispenseReview
134
+ medicationDispense={medicationDispense}
135
+ updateMedicationDispense={mockUpdate}
136
+ quantityRemaining={30}
137
+ />
138
+ );
139
+
140
+ // TODO test expected views and various interactions
141
+ });
142
+ });
@@ -0,0 +1,562 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Medication, MedicationDispense } from "../types";
3
+ import MedicationCard from "../components/medication-card.component";
4
+ import { TextArea, ComboBox, Dropdown, NumberInput } from "@carbon/react";
5
+ import {
6
+ useLayoutType,
7
+ useConfig,
8
+ useSession,
9
+ userHasAccess,
10
+ } from "@openmrs/esm-framework";
11
+ import { useTranslation } from "react-i18next";
12
+ import {
13
+ getConceptCodingUuid,
14
+ getMedicationReferenceOrCodeableConcept,
15
+ getOpenMRSMedicineDrugName,
16
+ } from "../utils";
17
+ import styles from "../components/medication-dispense-review.scss";
18
+ import {
19
+ useMedicationCodeableConcept,
20
+ useMedicationFormulations,
21
+ } from "../medication/medication.resource";
22
+ import { useMedicationRequest } from "../medication-request/medication-request.resource";
23
+ import { PharmacyConfig } from "../config-schema";
24
+ import {
25
+ useOrderConfig,
26
+ useSubstitutionReasonValueSet,
27
+ useSubstitutionTypeValueSet,
28
+ } from "../medication-dispense/medication-dispense.resource";
29
+ import { PRIVILEGE_CREATE_DISPENSE_MODIFY_DETAILS } from "../constants";
30
+
31
+ interface MedicationDispenseReviewProps {
32
+ medicationDispense: MedicationDispense;
33
+ updateMedicationDispense: Function;
34
+ quantityRemaining: number;
35
+ }
36
+
37
+ const MedicationDispenseReview: React.FC<MedicationDispenseReviewProps> = ({
38
+ medicationDispense,
39
+ updateMedicationDispense,
40
+ quantityRemaining,
41
+ }) => {
42
+ const { t } = useTranslation();
43
+ const config = useConfig() as PharmacyConfig;
44
+ const session = useSession();
45
+ const [isEditingFormulation, setIsEditingFormulation] = useState(false);
46
+ const [isSubstitution, setIsSubstitution] = useState(false);
47
+ // Dosing Unit eg Tablets
48
+ const [drugDosingUnits, setDrugDosingUnits] = useState([]);
49
+ // Dispensing Unit eg Tablets
50
+ const [drugDispensingUnits, setDrugDispensingUnits] = useState([]);
51
+ // Route eg Oral
52
+ const [drugRoutes, setDrugRoutes] = useState([]);
53
+ // Frequency eg Twice daily
54
+ const [orderFrequencies, setOrderFrequencies] = useState([]);
55
+ // type of substitution question
56
+ const [substitutionTypes, setSubstitutionTypes] = useState([]);
57
+ // reason for substitution question
58
+ const [substitutionReasons, setSubstitutionReasons] = useState([]);
59
+ const [userCanModify, setUserCanModify] = useState(false);
60
+
61
+ const isTablet = useLayoutType() === "tablet";
62
+
63
+ const { orderConfigObject } = useOrderConfig();
64
+ const { substitutionTypeValueSet } = useSubstitutionTypeValueSet(
65
+ config.valueSets.substitutionType.uuid
66
+ );
67
+ const { substitutionReasonValueSet } = useSubstitutionReasonValueSet(
68
+ config.valueSets.substitutionReason.uuid
69
+ );
70
+
71
+ const allowEditing = config.dispenseBehavior.allowModifyingPrescription;
72
+
73
+ useEffect(() => {
74
+ if (orderConfigObject) {
75
+ // sync drug route options order config
76
+ const availableRoutes = drugRoutes.map((x) => x.id);
77
+ const otherRouteOptions = [];
78
+ orderConfigObject.drugRoutes.forEach(
79
+ (x) =>
80
+ availableRoutes.includes(x.uuid) ||
81
+ otherRouteOptions.push({ id: x.uuid, text: x.display })
82
+ );
83
+ setDrugRoutes([...drugRoutes, ...otherRouteOptions]);
84
+
85
+ // sync dosage.unit options with what's defined in the order config
86
+ const availableDosingUnits = drugDosingUnits.map((x) => x.id);
87
+ const otherDosingUnits = [];
88
+ orderConfigObject.drugDosingUnits.forEach(
89
+ (x) =>
90
+ availableDosingUnits.includes(x.uuid) ||
91
+ otherDosingUnits.push({ id: x.uuid, text: x.display })
92
+ );
93
+ setDrugDosingUnits([...drugDosingUnits, ...otherDosingUnits]);
94
+
95
+ // sync dispensing unit options with what's defined in the order config
96
+ const availableDispensingUnits = drugDispensingUnits.map((x) => x.id);
97
+ const otherDispensingUnits = [];
98
+ orderConfigObject.drugDispensingUnits.forEach(
99
+ (x) =>
100
+ availableDispensingUnits.includes(x.uuid) ||
101
+ otherDispensingUnits.push({ id: x.uuid, text: x.display })
102
+ );
103
+ setDrugDispensingUnits([...drugDispensingUnits, ...otherDispensingUnits]);
104
+
105
+ // sync order frequency options with order config
106
+ const availableFrequencies = orderFrequencies.map((x) => x.id);
107
+ const otherFrequencyOptions = [];
108
+ orderConfigObject.orderFrequencies.forEach(
109
+ (x) =>
110
+ availableFrequencies.includes(x.uuid) ||
111
+ otherFrequencyOptions.push({ id: x.uuid, text: x.display })
112
+ );
113
+ setOrderFrequencies([...orderFrequencies, ...otherFrequencyOptions]);
114
+ }
115
+ // eslint-disable-next-line react-hooks/exhaustive-deps
116
+ }, [orderConfigObject]);
117
+
118
+ useEffect(() => {
119
+ const substitutionTypeOptions = [];
120
+
121
+ if (substitutionTypeValueSet?.compose?.include) {
122
+ const uuidValueSet = substitutionTypeValueSet.compose.include.find(
123
+ (include) => !include.system
124
+ );
125
+ if (uuidValueSet) {
126
+ uuidValueSet.concept?.forEach((concept) =>
127
+ substitutionTypeOptions.push({
128
+ id: concept.code,
129
+ text: concept.display,
130
+ })
131
+ );
132
+ }
133
+ substitutionTypeOptions.sort((a, b) => a.text.localeCompare(b.text));
134
+ }
135
+ setSubstitutionTypes(substitutionTypeOptions);
136
+ }, [substitutionTypeValueSet]);
137
+
138
+ useEffect(() => {
139
+ const substitutionReasonOptions = [];
140
+
141
+ if (substitutionReasonValueSet?.compose?.include) {
142
+ const uuidValueSet = substitutionReasonValueSet.compose.include.find(
143
+ (include) => !include.system
144
+ );
145
+ if (uuidValueSet) {
146
+ uuidValueSet.concept?.forEach((concept) =>
147
+ substitutionReasonOptions.push({
148
+ id: concept.code,
149
+ text: concept.display,
150
+ })
151
+ );
152
+ }
153
+ substitutionReasonOptions.sort((a, b) => a.text.localeCompare(b.text));
154
+ }
155
+ setSubstitutionReasons(substitutionReasonOptions);
156
+ }, [substitutionReasonValueSet]);
157
+
158
+ // if this an order for a drug by concept, but not a particular formulation, we already have access to concept uuid
159
+ const existingMedicationCodeableConceptUuid = getConceptCodingUuid(
160
+ getMedicationReferenceOrCodeableConcept(medicationDispense)
161
+ ?.medicationCodeableConcept?.coding
162
+ );
163
+ // otherwise we need to fetch the medication details to get the codeable concept
164
+ // (note that React Hooks should not be called conditionally, so we *always* call this hook, but it will return null if existingMedicationCodeableConcept is defined
165
+ const { medicationCodeableConceptUuid } = useMedicationCodeableConcept(
166
+ existingMedicationCodeableConceptUuid,
167
+ getMedicationReferenceOrCodeableConcept(medicationDispense)
168
+ .medicationReference?.reference
169
+ );
170
+ // get the formulations
171
+ const { medicationFormulations } = useMedicationFormulations(
172
+ existingMedicationCodeableConceptUuid
173
+ ? existingMedicationCodeableConceptUuid
174
+ : medicationCodeableConceptUuid
175
+ ? medicationCodeableConceptUuid
176
+ : null
177
+ );
178
+
179
+ // get the medication request associated with this dispense event
180
+ // (we fetch this so that we can use it below to determine if the formulation dispensed varies from what was
181
+ // ordered; it's slightly inefficient/awkward to fetch it from the server here because we *have* fetched it earlier,
182
+ // it just seems cleaner to fetch it here rather than to make sure we pass it down through various components; with
183
+ // SWR handling caching, we may want to consider pulling more down into this)
184
+ const { medicationRequest } = useMedicationRequest(
185
+ medicationDispense.authorizingPrescription
186
+ ? medicationDispense.authorizingPrescription[0].reference
187
+ : null,
188
+ config.refreshInterval
189
+ );
190
+
191
+ // check to see if the current dispense would be a substitution, and update accordingly
192
+ useEffect(() => {
193
+ if (
194
+ medicationRequest?.medicationReference?.reference &&
195
+ medicationDispense?.medicationReference?.reference &&
196
+ medicationRequest.medicationReference.reference !=
197
+ medicationDispense.medicationReference.reference
198
+ ) {
199
+ setIsSubstitution(true);
200
+ updateMedicationDispense({
201
+ ...medicationDispense,
202
+ substitution: {
203
+ ...medicationDispense.substitution,
204
+ wasSubstituted: true,
205
+ },
206
+ });
207
+ } else {
208
+ setIsSubstitution(false);
209
+ updateMedicationDispense({
210
+ ...medicationDispense,
211
+ substitution: {
212
+ wasSubstituted: false,
213
+ reason: [
214
+ {
215
+ coding: [{ code: null }],
216
+ },
217
+ ],
218
+ type: { coding: [{ code: null }] },
219
+ },
220
+ });
221
+ }
222
+ }, [
223
+ medicationDispense.medicationReference,
224
+ medicationRequest?.medicationReference,
225
+ ]);
226
+
227
+ useEffect(() => {
228
+ setUserCanModify(
229
+ session?.user &&
230
+ userHasAccess(PRIVILEGE_CREATE_DISPENSE_MODIFY_DETAILS, session.user)
231
+ );
232
+ }, [session]);
233
+
234
+ return (
235
+ <div className={styles.medicationDispenseReviewContainer}>
236
+ {!isEditingFormulation ? (
237
+ <MedicationCard
238
+ medication={getMedicationReferenceOrCodeableConcept(
239
+ medicationDispense
240
+ )}
241
+ editAction={
242
+ userCanModify && allowEditing
243
+ ? () => setIsEditingFormulation(true)
244
+ : null
245
+ }
246
+ />
247
+ ) : (
248
+ <Dropdown
249
+ id="medicationFormulation"
250
+ light={isTablet}
251
+ items={medicationFormulations}
252
+ itemToString={(item: Medication) => getOpenMRSMedicineDrugName(item)}
253
+ initialSelectedItem={{
254
+ ...medicationFormulations?.find(
255
+ (formulation) =>
256
+ formulation.id ===
257
+ medicationDispense.medicationReference?.reference.split("/")[1]
258
+ ),
259
+ }}
260
+ onChange={({ selectedItem }) => {
261
+ updateMedicationDispense({
262
+ ...medicationDispense,
263
+ medicationCodeableConcept: undefined,
264
+ medicationReference: {
265
+ reference: "Medication/" + selectedItem?.id,
266
+ display: getOpenMRSMedicineDrugName(selectedItem),
267
+ },
268
+ });
269
+ setIsEditingFormulation(false);
270
+ }}
271
+ required
272
+ />
273
+ )}
274
+
275
+ {isSubstitution && (
276
+ <div className={styles.dispenseDetailsContainer}>
277
+ <ComboBox
278
+ className={styles.substitutionType}
279
+ id="substitutionType"
280
+ light={isTablet}
281
+ items={substitutionTypes}
282
+ titleText={t("substitutionType", "Type of substitution")}
283
+ itemToString={(item) => item?.text}
284
+ initialSelectedItem={{
285
+ id: medicationDispense.substitution.type?.coding[0]?.code,
286
+ text: medicationDispense.substitution.type?.text,
287
+ }}
288
+ onChange={({ selectedItem }) => {
289
+ updateMedicationDispense({
290
+ ...medicationDispense,
291
+ substitution: {
292
+ ...medicationDispense.substitution,
293
+ type: {
294
+ coding: [
295
+ {
296
+ code: selectedItem?.id,
297
+ },
298
+ ],
299
+ },
300
+ },
301
+ });
302
+ }}
303
+ />
304
+ </div>
305
+ )}
306
+
307
+ {isSubstitution && (
308
+ <div className={styles.dispenseDetailsContainer}>
309
+ <ComboBox
310
+ className={styles.substitutionReason}
311
+ id="substitutionReason"
312
+ light={isTablet}
313
+ items={substitutionReasons}
314
+ titleText={t("substitutionReason", "Reason for substitution")}
315
+ itemToString={(item) => item?.text}
316
+ initialSelectedItem={{
317
+ id: medicationDispense.substitution.reason[0]?.coding[0]?.code,
318
+ text: medicationDispense.substitution.reason[0]?.text,
319
+ }}
320
+ onChange={({ selectedItem }) => {
321
+ updateMedicationDispense({
322
+ ...medicationDispense,
323
+ substitution: {
324
+ ...medicationDispense.substitution,
325
+ reason: [
326
+ {
327
+ coding: [
328
+ {
329
+ code: selectedItem?.id,
330
+ },
331
+ ],
332
+ },
333
+ ],
334
+ },
335
+ });
336
+ }}
337
+ />
338
+ </div>
339
+ )}
340
+
341
+ <div className={styles.dispenseDetailsContainer}>
342
+ <NumberInput
343
+ allowEmpty={false}
344
+ disabled={!userCanModify}
345
+ hideSteppers={true}
346
+ id="quantity"
347
+ invalidText={t("numberIsNotValid", "Number is not valid")}
348
+ label={
349
+ t("quantity", "Quantity") +
350
+ (config.dispenseBehavior.restrictTotalQuantityDispensed
351
+ ? " (" +
352
+ t("maxQuantityRemaining", "Maximum quantity remaining:") +
353
+ " " +
354
+ quantityRemaining +
355
+ ")"
356
+ : "")
357
+ }
358
+ min={0}
359
+ max={
360
+ config.dispenseBehavior.restrictTotalQuantityDispensed
361
+ ? quantityRemaining
362
+ : undefined
363
+ }
364
+ value={medicationDispense.quantity.value}
365
+ onChange={(e) => {
366
+ updateMedicationDispense({
367
+ ...medicationDispense,
368
+ quantity: {
369
+ ...medicationDispense.quantity,
370
+ value: e.target?.value ? parseFloat(e.target.value) : "",
371
+ },
372
+ });
373
+ }}
374
+ />
375
+
376
+ <ComboBox
377
+ id="quantityUnits"
378
+ disabled={!userCanModify || !allowEditing}
379
+ light={isTablet}
380
+ items={drugDispensingUnits}
381
+ titleText={t("drugDispensingUnit", "Dispensing unit")}
382
+ itemToString={(item) => item?.text}
383
+ initialSelectedItem={{
384
+ id: medicationDispense.quantity.code,
385
+ text: medicationDispense.quantity.unit,
386
+ }}
387
+ onChange={({ selectedItem }) => {
388
+ updateMedicationDispense({
389
+ ...medicationDispense,
390
+ // note that we specifically recreate doesQuantity to overwrite any unit or system properties that may have been set
391
+ quantity: {
392
+ value: medicationDispense.quantity.value,
393
+ code: selectedItem?.id,
394
+ },
395
+ });
396
+ }}
397
+ required
398
+ />
399
+ </div>
400
+
401
+ <div className={styles.dispenseDetailsContainer}>
402
+ <NumberInput
403
+ allowEmpty={false}
404
+ disabled={!userCanModify || !allowEditing}
405
+ hideSteppers={true}
406
+ id="dosingQuanity"
407
+ invalidText={t("numberIsNotValid", "Number is not valid")}
408
+ min={0}
409
+ label={t("dose", "Dose")}
410
+ value={
411
+ medicationDispense.dosageInstruction[0].doseAndRate[0].doseQuantity
412
+ .value
413
+ }
414
+ onChange={(e) => {
415
+ updateMedicationDispense({
416
+ ...medicationDispense,
417
+ dosageInstruction: [
418
+ {
419
+ ...medicationDispense.dosageInstruction[0],
420
+ doseAndRate: [
421
+ {
422
+ ...medicationDispense.dosageInstruction[0].doseAndRate[0],
423
+ doseQuantity: {
424
+ ...medicationDispense.dosageInstruction[0]
425
+ .doseAndRate[0].doseQuantity,
426
+ value: e.target?.value
427
+ ? parseFloat(e.target.value)
428
+ : "",
429
+ },
430
+ },
431
+ ],
432
+ },
433
+ ],
434
+ });
435
+ }}
436
+ />
437
+
438
+ <ComboBox
439
+ id="dosingUnits"
440
+ disabled={!userCanModify || !allowEditing}
441
+ light={isTablet}
442
+ items={drugDosingUnits}
443
+ titleText={t("doseUnit", "Dose unit")}
444
+ itemToString={(item) => item?.text}
445
+ initialSelectedItem={{
446
+ id: medicationDispense.dosageInstruction[0].doseAndRate[0]
447
+ .doseQuantity?.code,
448
+ text: medicationDispense.dosageInstruction[0].doseAndRate[0]
449
+ .doseQuantity?.unit,
450
+ }}
451
+ onChange={({ selectedItem }) => {
452
+ updateMedicationDispense({
453
+ ...medicationDispense,
454
+ dosageInstruction: [
455
+ {
456
+ ...medicationDispense.dosageInstruction[0],
457
+ doseAndRate: [
458
+ {
459
+ doseQuantity: {
460
+ // note that we specifically recreate doesQuantity to overwrite any unit or system properties that may have been set
461
+ value:
462
+ medicationDispense.dosageInstruction[0].doseAndRate[0]
463
+ .doseQuantity?.value,
464
+ code: selectedItem?.id,
465
+ },
466
+ },
467
+ ],
468
+ },
469
+ ],
470
+ });
471
+ }}
472
+ required
473
+ />
474
+
475
+ <ComboBox
476
+ id="editRoute"
477
+ disabled={!userCanModify || !allowEditing}
478
+ light={isTablet}
479
+ items={drugRoutes}
480
+ initialSelectedItem={{
481
+ id: medicationDispense.dosageInstruction[0].route?.coding[0]?.code,
482
+ text: medicationDispense.dosageInstruction[0].route?.text,
483
+ }}
484
+ titleText={t("route", "Route")}
485
+ itemToString={(item) => item?.text}
486
+ onChange={({ selectedItem }) => {
487
+ updateMedicationDispense({
488
+ ...medicationDispense,
489
+ dosageInstruction: [
490
+ {
491
+ ...medicationDispense.dosageInstruction[0],
492
+ route: {
493
+ coding: [
494
+ {
495
+ code: selectedItem?.id,
496
+ },
497
+ ],
498
+ },
499
+ },
500
+ ],
501
+ });
502
+ }}
503
+ required
504
+ />
505
+ </div>
506
+
507
+ <ComboBox
508
+ id="frequency"
509
+ disabled={!userCanModify || !allowEditing}
510
+ light={isTablet}
511
+ items={orderFrequencies}
512
+ initialSelectedItem={{
513
+ id: medicationDispense.dosageInstruction[0].timing?.code?.coding[0]
514
+ ?.code,
515
+ text: medicationDispense.dosageInstruction[0].timing?.code?.text,
516
+ }}
517
+ titleText={t("frequency", "Frequency")}
518
+ itemToString={(item) => item?.text}
519
+ onChange={({ selectedItem }) => {
520
+ updateMedicationDispense({
521
+ ...medicationDispense,
522
+ dosageInstruction: [
523
+ {
524
+ ...medicationDispense.dosageInstruction[0],
525
+ timing: {
526
+ ...medicationDispense.dosageInstruction[0].timing,
527
+ code: {
528
+ coding: [
529
+ {
530
+ code: selectedItem?.id,
531
+ },
532
+ ],
533
+ },
534
+ },
535
+ },
536
+ ],
537
+ });
538
+ }}
539
+ required
540
+ />
541
+
542
+ <TextArea
543
+ labelText={t("patientInstructions", "Patient instructions")}
544
+ value={medicationDispense.dosageInstruction[0].text}
545
+ maxLength={65535}
546
+ onChange={(e) => {
547
+ updateMedicationDispense({
548
+ ...medicationDispense,
549
+ dosageInstruction: [
550
+ {
551
+ ...medicationDispense.dosageInstruction[0],
552
+ text: e.target.value,
553
+ },
554
+ ],
555
+ });
556
+ }}
557
+ />
558
+ </div>
559
+ );
560
+ };
561
+
562
+ export default MedicationDispenseReview;