@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
@@ -4,10 +4,12 @@ import { ArrowLeft, Close } from "@carbon/react/icons";
4
4
  import { useLayoutType } from "@openmrs/esm-framework";
5
5
  import { closeOverlay, useOverlay } from "../../hooks/useOverlay";
6
6
  import styles from "./overlay.scss";
7
+ import { useTranslation } from "react-i18next";
7
8
 
8
9
  const Overlay: React.FC = () => {
9
10
  const { header, component, isOverlayOpen } = useOverlay();
10
11
  const layout = useLayoutType();
12
+ const { t } = useTranslation();
11
13
  return (
12
14
  <>
13
15
  {isOverlayOpen && (
@@ -31,7 +33,7 @@ const Overlay: React.FC = () => {
31
33
  ) : (
32
34
  <Header
33
35
  onClick={() => closeOverlay()}
34
- aria-label="Tablet overlay"
36
+ aria-label={t("tabletOverlay", "Tablet overlay")}
35
37
  className={styles.tabletOverlayHeader}
36
38
  >
37
39
  <Button hasIconOnly>
@@ -0,0 +1,236 @@
1
+ import React, { useEffect, useMemo, useState } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import {
4
+ ExtensionSlot,
5
+ showNotification,
6
+ showToast,
7
+ useConfig,
8
+ useLayoutType,
9
+ usePatient,
10
+ } from "@openmrs/esm-framework";
11
+ import { Button, ComboBox, InlineLoading } from "@carbon/react";
12
+ import {
13
+ saveMedicationDispense,
14
+ useReasonForPauseValueSet,
15
+ } from "../medication-dispense/medication-dispense.resource";
16
+ import { closeOverlay } from "../hooks/useOverlay";
17
+ import styles from "./forms.scss";
18
+ import { PharmacyConfig } from "../config-schema";
19
+ import { updateMedicationRequestFulfillerStatus } from "../medication-request/medication-request.resource";
20
+ import { getUuidFromReference, revalidate } from "../utils";
21
+ import {
22
+ MedicationDispense,
23
+ MedicationDispenseStatus,
24
+ MedicationRequestFulfillerStatus,
25
+ } from "../types";
26
+
27
+ interface PauseDispenseFormProps {
28
+ medicationDispense: MedicationDispense;
29
+ mode: "enter" | "edit";
30
+ patientUuid?: string;
31
+ encounterUuid: string;
32
+ }
33
+
34
+ const PauseDispenseForm: React.FC<PauseDispenseFormProps> = ({
35
+ medicationDispense,
36
+ mode,
37
+ patientUuid,
38
+ encounterUuid,
39
+ }) => {
40
+ const { t } = useTranslation();
41
+ const config = useConfig() as PharmacyConfig;
42
+ const isTablet = useLayoutType() === "tablet";
43
+ const { patient, isLoading } = usePatient(patientUuid);
44
+
45
+ // Keep track of medication dispense payload
46
+ const [medicationDispensePayload, setMedicationDispensePayload] =
47
+ useState<MedicationDispense>();
48
+
49
+ // whether or not the form is valid and ready to submit
50
+ const [isValid, setIsValid] = useState(false);
51
+
52
+ // to prevent duplicate submits
53
+ const [isSubmitting, setIsSubmitting] = useState(false);
54
+ const [reasonsForPause, setReasonsForPause] = useState([]);
55
+ const { reasonForPauseValueSet } = useReasonForPauseValueSet(
56
+ config.valueSets.reasonForPause.uuid
57
+ );
58
+
59
+ useEffect(() => {
60
+ const reasonForPauseOptions = [];
61
+
62
+ if (reasonForPauseValueSet?.compose?.include) {
63
+ const uuidValueSet = reasonForPauseValueSet.compose.include.find(
64
+ (include) => !include.system
65
+ );
66
+ if (uuidValueSet) {
67
+ uuidValueSet.concept?.forEach((concept) =>
68
+ reasonForPauseOptions.push({
69
+ id: concept.code,
70
+ text: concept.display,
71
+ })
72
+ );
73
+ reasonForPauseOptions.sort((a, b) => a.text.localeCompare(b.text));
74
+ }
75
+ }
76
+ setReasonsForPause(reasonForPauseOptions);
77
+ }, [reasonForPauseValueSet]);
78
+
79
+ const handleSubmit = () => {
80
+ if (!isSubmitting) {
81
+ setIsSubmitting(true);
82
+ const abortController = new AbortController();
83
+ saveMedicationDispense(
84
+ medicationDispensePayload,
85
+ MedicationDispenseStatus.on_hold,
86
+ abortController
87
+ )
88
+ .then((response) => {
89
+ // only update request status when added a new dispense event, not updating
90
+ if (response.ok && !medicationDispense.id) {
91
+ return updateMedicationRequestFulfillerStatus(
92
+ getUuidFromReference(
93
+ medicationDispensePayload.authorizingPrescription[0].reference // assumes authorizing prescription exist
94
+ ),
95
+ MedicationRequestFulfillerStatus.on_hold
96
+ );
97
+ } else {
98
+ return response;
99
+ }
100
+ })
101
+ .then((response) => {
102
+ if (response.ok) {
103
+ closeOverlay();
104
+ revalidate(encounterUuid);
105
+ showToast({
106
+ critical: true,
107
+ kind: "success",
108
+ description: t(
109
+ mode === "enter"
110
+ ? "medicationDispensePaused"
111
+ : "medicationDispenseUpdated",
112
+ mode === "enter"
113
+ ? "Medication dispense paused."
114
+ : "Dispense record successfully updated."
115
+ ),
116
+ title: t(
117
+ mode === "enter"
118
+ ? "medicationDispensePaused"
119
+ : "medicationDispenseUpdated",
120
+ mode === "enter"
121
+ ? "Medication dispense paused."
122
+ : "Dispense record successfully updated."
123
+ ),
124
+ });
125
+ }
126
+ })
127
+ .catch((error) => {
128
+ showNotification({
129
+ title: t(
130
+ mode === "enter"
131
+ ? "medicationDispensePauseError"
132
+ : "medicationDispenseUpdatedError",
133
+ mode === "enter"
134
+ ? "Error pausing medication dispense."
135
+ : "Error updating dispense record"
136
+ ),
137
+ kind: "error",
138
+ critical: true,
139
+ description: error?.message,
140
+ });
141
+ setIsSubmitting(false);
142
+ });
143
+ }
144
+ };
145
+
146
+ const checkIsValid = () => {
147
+ if (
148
+ medicationDispensePayload &&
149
+ medicationDispensePayload.statusReasonCodeableConcept?.coding[0].code
150
+ ) {
151
+ setIsValid(true);
152
+ } else {
153
+ setIsValid(false);
154
+ }
155
+ };
156
+
157
+ // initialize the internal dispense payload with the dispenses passed in as props
158
+ useEffect(
159
+ () => setMedicationDispensePayload(medicationDispense),
160
+ [medicationDispense]
161
+ );
162
+
163
+ // check is valid on any changes
164
+ useEffect(checkIsValid, [medicationDispensePayload]);
165
+
166
+ const bannerState = useMemo(() => {
167
+ if (patient) {
168
+ return {
169
+ patient,
170
+ patientUuid,
171
+ hideActionsOverflow: true,
172
+ };
173
+ }
174
+ }, [patient, patientUuid]);
175
+
176
+ return (
177
+ <div className="">
178
+ <div className={styles.formWrapper}>
179
+ {isLoading && (
180
+ <InlineLoading
181
+ className={styles.bannerLoading}
182
+ iconDescription="Loading"
183
+ description="Loading banner"
184
+ status="active"
185
+ />
186
+ )}
187
+ {patient && (
188
+ <ExtensionSlot name="patient-header-slot" state={bannerState} />
189
+ )}
190
+ <section className={styles.formGroup}>
191
+ <ComboBox
192
+ id="reasonForPause"
193
+ light={isTablet}
194
+ items={reasonsForPause}
195
+ titleText={t("reasonForPause", "Reason for pause")}
196
+ itemToString={(item) => item?.text}
197
+ initialSelectedItem={{
198
+ id: medicationDispense.statusReasonCodeableConcept?.coding[0]
199
+ ?.code,
200
+ text: medicationDispense.statusReasonCodeableConcept?.text,
201
+ }}
202
+ onChange={({ selectedItem }) => {
203
+ setMedicationDispensePayload({
204
+ ...medicationDispensePayload,
205
+ statusReasonCodeableConcept: {
206
+ coding: [
207
+ {
208
+ code: selectedItem?.id,
209
+ },
210
+ ],
211
+ },
212
+ });
213
+ }}
214
+ />
215
+ </section>
216
+ <section className={styles.buttonGroup}>
217
+ <Button
218
+ disabled={isSubmitting}
219
+ onClick={() => closeOverlay()}
220
+ kind="secondary"
221
+ >
222
+ {t("cancel", "Cancel")}
223
+ </Button>
224
+ <Button disabled={!isValid || isSubmitting} onClick={handleSubmit}>
225
+ {t(
226
+ mode === "enter" ? "pause" : "saveChanges",
227
+ mode === "enter" ? "Pause" : "Save changes"
228
+ )}
229
+ </Button>
230
+ </section>
231
+ </div>
232
+ </div>
233
+ );
234
+ };
235
+
236
+ export default PauseDispenseForm;
@@ -0,0 +1,326 @@
1
+ import React from "react";
2
+ import {
3
+ DataTableSkeleton,
4
+ OverflowMenu,
5
+ OverflowMenuItem,
6
+ Tag,
7
+ Tile,
8
+ } from "@carbon/react";
9
+ import { useTranslation } from "react-i18next";
10
+ import {
11
+ formatDatetime,
12
+ parseDate,
13
+ Session,
14
+ useConfig,
15
+ userHasAccess,
16
+ useSession,
17
+ } from "@openmrs/esm-framework";
18
+ import styles from "./history-and-comments.scss";
19
+ import {
20
+ updateMedicationRequestFulfillerStatus,
21
+ usePrescriptionDetails,
22
+ } from "../medication-request/medication-request.resource";
23
+ import { deleteMedicationDispense } from "../medication-dispense/medication-dispense.resource";
24
+ import MedicationEvent from "../components/medication-event.component";
25
+ import { launchOverlay } from "../hooks/useOverlay";
26
+ import { PharmacyConfig } from "../config-schema";
27
+ import DispenseForm from "../forms/dispense-form.component";
28
+ import {
29
+ MedicationDispense,
30
+ MedicationDispenseStatus,
31
+ MedicationRequestBundle,
32
+ } from "../types";
33
+ import {
34
+ PRIVILEGE_DELETE_DISPENSE,
35
+ PRIVILEGE_DELETE_DISPENSE_THIS_PROVIDER_ONLY,
36
+ PRIVILEGE_EDIT_DISPENSE,
37
+ } from "../constants";
38
+ import {
39
+ computeNewFulfillerStatusAfterDelete,
40
+ computeQuantityRemaining,
41
+ getDateRecorded,
42
+ getFulfillerStatus,
43
+ getMedicationRequestBundleContainingMedicationDispense,
44
+ getUuidFromReference,
45
+ revalidate,
46
+ sortMedicationDispensesByDateRecorded,
47
+ } from "../utils";
48
+ import PauseDispenseForm from "../forms/pause-dispense-form.component";
49
+ import CloseDispenseForm from "../forms/close-dispense-form.component";
50
+
51
+ const HistoryAndComments: React.FC<{
52
+ encounterUuid: string;
53
+ patientUuid: string;
54
+ }> = ({ encounterUuid, patientUuid }) => {
55
+ const { t } = useTranslation();
56
+ const session = useSession();
57
+ const config = useConfig() as PharmacyConfig;
58
+ const { medicationRequestBundles, prescriptionDate, isError, isLoading } =
59
+ usePrescriptionDetails(encounterUuid, config.refreshInterval);
60
+
61
+ const userCanEdit: Function = (session: Session) =>
62
+ session?.user && userHasAccess(PRIVILEGE_EDIT_DISPENSE, session.user);
63
+
64
+ const userCanDelete: Function = (
65
+ session: Session,
66
+ medicationDispense: MedicationDispense
67
+ ) => {
68
+ if (session?.user) {
69
+ if (userHasAccess(PRIVILEGE_DELETE_DISPENSE, session.user)) {
70
+ return true;
71
+ } else if (
72
+ userHasAccess(
73
+ PRIVILEGE_DELETE_DISPENSE_THIS_PROVIDER_ONLY,
74
+ session.user
75
+ ) &&
76
+ session.currentProvider?.uuid &&
77
+ medicationDispense.performer?.find(
78
+ (performer) =>
79
+ performer?.actor?.reference?.length > 1 &&
80
+ performer.actor.reference.split("/")[1] ===
81
+ session.currentProvider.uuid
82
+ ) != null
83
+ ) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ };
89
+
90
+ const generateForm: Function = (
91
+ medicationDispense: MedicationDispense,
92
+ medicationRequestBundle: MedicationRequestBundle
93
+ ) => {
94
+ if (medicationDispense.status === MedicationDispenseStatus.completed) {
95
+ // note that since this is an edit, quantity remaining needs to include quantity that is part of this dispense
96
+ let quantityRemaining = null;
97
+ if (config.dispenseBehavior.restrictTotalQuantityDispensed) {
98
+ quantityRemaining =
99
+ computeQuantityRemaining(medicationRequestBundle) +
100
+ (medicationDispense?.quantity
101
+ ? medicationDispense.quantity.value
102
+ : 0);
103
+ }
104
+
105
+ return (
106
+ <DispenseForm
107
+ patientUuid={patientUuid}
108
+ encounterUuid={encounterUuid}
109
+ medicationDispense={medicationDispense}
110
+ medicationRequestBundle={medicationRequestBundle}
111
+ quantityRemaining={quantityRemaining}
112
+ mode="edit"
113
+ />
114
+ );
115
+ } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) {
116
+ return (
117
+ <PauseDispenseForm
118
+ patientUuid={patientUuid}
119
+ encounterUuid={encounterUuid}
120
+ medicationDispense={medicationDispense}
121
+ mode="edit"
122
+ />
123
+ );
124
+ } else if (
125
+ medicationDispense.status === MedicationDispenseStatus.declined
126
+ ) {
127
+ return (
128
+ <CloseDispenseForm
129
+ patientUuid={patientUuid}
130
+ encounterUuid={encounterUuid}
131
+ medicationDispense={medicationDispense}
132
+ mode="edit"
133
+ />
134
+ );
135
+ }
136
+ };
137
+
138
+ const generateOverlayText: Function = (
139
+ medicationDispense: MedicationDispense
140
+ ) => {
141
+ if (medicationDispense.status === MedicationDispenseStatus.completed) {
142
+ return t("editDispenseRecord", "Edit Dispense Record");
143
+ } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) {
144
+ return t("editPauseRecord", "Edit Pause Record");
145
+ } else if (
146
+ medicationDispense.status === MedicationDispenseStatus.declined
147
+ ) {
148
+ return t("editCloseeRecord", "Edit Close Record");
149
+ }
150
+ };
151
+
152
+ const generateMedicationDispenseActionMenu: Function = (
153
+ medicationDispense: MedicationDispense,
154
+ medicationRequestBundle: MedicationRequestBundle
155
+ ) => {
156
+ const editable = userCanEdit(session);
157
+ const deletable = userCanDelete(session, medicationDispense);
158
+
159
+ if (!editable && !deletable) {
160
+ return null;
161
+ } else {
162
+ return (
163
+ <OverflowMenu
164
+ ariaLabel={t(
165
+ "medicationDispenseActionMenu",
166
+ "Medication Dispense Action Menu"
167
+ )}
168
+ flipped={true}
169
+ className={styles.medicationEventActionMenu}
170
+ >
171
+ {editable && (
172
+ <OverflowMenuItem
173
+ onClick={() =>
174
+ launchOverlay(
175
+ generateOverlayText(medicationDispense),
176
+ generateForm(medicationDispense, medicationRequestBundle)
177
+ )
178
+ }
179
+ itemText={t("editRecord", "Edit Record")}
180
+ ></OverflowMenuItem>
181
+ )}
182
+ {deletable && (
183
+ <OverflowMenuItem
184
+ onClick={() => {
185
+ handleDelete(medicationDispense, medicationRequestBundle);
186
+ }}
187
+ itemText={t("delete", "Delete")}
188
+ ></OverflowMenuItem>
189
+ )}
190
+ </OverflowMenu>
191
+ );
192
+ }
193
+ };
194
+
195
+ const generateDispenseTag: Function = (
196
+ medicationDispense: MedicationDispense
197
+ ) => {
198
+ if (medicationDispense.status === MedicationDispenseStatus.completed) {
199
+ return <Tag type="gray">{t("dispensed", "Dispensed")}</Tag>;
200
+ } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) {
201
+ return <Tag type="red">{t("paused", "Paused")}</Tag>;
202
+ } else if (
203
+ medicationDispense.status === MedicationDispenseStatus.declined
204
+ ) {
205
+ return <Tag type="red">{t("closed", "Closed")}</Tag>;
206
+ } else {
207
+ return null;
208
+ }
209
+ };
210
+
211
+ const generateDispenseVerbiage: Function = (
212
+ medicationDispense: MedicationDispense
213
+ ) => {
214
+ if (medicationDispense.status === MedicationDispenseStatus.completed) {
215
+ return t("dispensedMedication", "dispensed medication");
216
+ } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) {
217
+ return t("pausedDispense", "paused dispense");
218
+ } else if (
219
+ medicationDispense.status === MedicationDispenseStatus.declined
220
+ ) {
221
+ return t("closedDispense", "closed dispense");
222
+ } else {
223
+ return null;
224
+ }
225
+ };
226
+
227
+ const handleDelete: Function = (
228
+ medicationDispense: MedicationDispense,
229
+ medicationRequestBundle: MedicationRequestBundle
230
+ ) => {
231
+ const currentFulfillerStatus = getFulfillerStatus(
232
+ medicationRequestBundle.request
233
+ );
234
+ const newFulfillerStatus = computeNewFulfillerStatusAfterDelete(
235
+ medicationDispense,
236
+ medicationRequestBundle,
237
+ config.dispenseBehavior.restrictTotalQuantityDispensed
238
+ );
239
+ if (currentFulfillerStatus !== newFulfillerStatus) {
240
+ updateMedicationRequestFulfillerStatus(
241
+ getUuidFromReference(
242
+ medicationDispense.authorizingPrescription[0].reference // assumes authorizing prescription exist
243
+ ),
244
+ newFulfillerStatus
245
+ ).then(() => {
246
+ revalidate(encounterUuid);
247
+ });
248
+ }
249
+ // do the actual delete
250
+ deleteMedicationDispense(medicationDispense.id).then(() => {
251
+ revalidate(encounterUuid);
252
+ });
253
+ };
254
+
255
+ // TODO: assumption is dispenses always are after requests?
256
+ return (
257
+ <div className={styles.historyAndCommentsContainer}>
258
+ {isLoading && <DataTableSkeleton role="progressbar" />}
259
+ {isError && <p>{t("error", "Error")}</p>}
260
+ {medicationRequestBundles &&
261
+ medicationRequestBundles
262
+ .flatMap(
263
+ (medicationDispenseBundle) => medicationDispenseBundle.dispenses
264
+ )
265
+ .sort(sortMedicationDispensesByDateRecorded)
266
+ .map((dispense) => {
267
+ return (
268
+ <div key={dispense.id}>
269
+ <h5
270
+ style={{
271
+ paddingTop: "8px",
272
+ paddingBottom: "8px",
273
+ fontSize: "0.9rem",
274
+ }}
275
+ >
276
+ {dispense.performer && dispense.performer[0]?.actor?.display}{" "}
277
+ {generateDispenseVerbiage(dispense)} -{" "}
278
+ {formatDatetime(parseDate(getDateRecorded(dispense)))}
279
+ </h5>
280
+ <Tile className={styles.dispenseTile}>
281
+ {generateMedicationDispenseActionMenu(
282
+ dispense,
283
+ getMedicationRequestBundleContainingMedicationDispense(
284
+ medicationRequestBundles,
285
+ dispense
286
+ )
287
+ )}
288
+ <MedicationEvent
289
+ medicationEvent={dispense}
290
+ status={generateDispenseTag(dispense)}
291
+ />
292
+ </Tile>
293
+ </div>
294
+ );
295
+ })}
296
+ {medicationRequestBundles &&
297
+ medicationRequestBundles
298
+ .flatMap((medicationRequestBundle) => medicationRequestBundle.request)
299
+ .map((request) => {
300
+ return (
301
+ <div key={request.id}>
302
+ <h5
303
+ style={{
304
+ paddingTop: "8px",
305
+ paddingBottom: "8px",
306
+ fontSize: "0.9rem",
307
+ }}
308
+ >
309
+ {request.requester.display}{" "}
310
+ {t("orderedMedication ", "ordered medication")} -{" "}
311
+ {formatDatetime(prescriptionDate)}
312
+ </h5>
313
+ <Tile className={styles.requestTile}>
314
+ <MedicationEvent
315
+ medicationEvent={request}
316
+ status={<Tag type="green">{t("ordered", "Ordered")}</Tag>}
317
+ />
318
+ </Tile>
319
+ </div>
320
+ );
321
+ })}
322
+ </div>
323
+ );
324
+ };
325
+
326
+ export default HistoryAndComments;
@@ -0,0 +1,57 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @import "~@openmrs/esm-styleguide/src/vars";
4
+
5
+ .historyAndCommentsContainer {
6
+ max-width: 80%;
7
+
8
+ form {
9
+ display: flex;
10
+ flex-flow: row wrap;
11
+ align-items: center;
12
+ }
13
+
14
+ form input {
15
+ vertical-align: middle;
16
+ margin: 0 10px 0 0;
17
+ padding: 10px;
18
+ border: 1px solid #8d8d8d !important;
19
+ background-color: white;
20
+ }
21
+ form button {
22
+ width: 90px;
23
+ height: 20px;
24
+ min-height: 2.5rem !important;
25
+ }
26
+
27
+ form :global(.cds--btn--ghost) {
28
+ width: 8rem !important;
29
+ }
30
+
31
+ .requestTile {
32
+ width: 100%;
33
+ margin: 2px 0 8px;
34
+ padding: 0 8px 0 8px;
35
+ background-color: #fff;
36
+ border-left: 4px solid var(--brand-03);
37
+ color: $text-02;
38
+ margin-bottom: 1rem !important;
39
+ }
40
+
41
+ .dispenseTile {
42
+ width: 100%;
43
+ margin: 2px 0 8px;
44
+ padding: 0 8px 0 8px;
45
+ background-color: #fff;
46
+ border-left: 4px solid red;
47
+ color: $text-02;
48
+ margin-bottom: 1rem !important;
49
+ }
50
+
51
+ .medicationEventActionMenu {
52
+ float: right;
53
+ }
54
+
55
+ }
56
+
57
+