@openmrs/esm-laboratory-app 1.0.1-pre.96

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 (157) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc +32 -0
  4. package/.husky/pre-commit +4 -0
  5. package/.husky/pre-push +6 -0
  6. package/.prettierignore +14 -0
  7. package/.turbo/turbo-build.log +30 -0
  8. package/.turbo/turbo-lint.log +12 -0
  9. package/.turbo/turbo-typescript.log +0 -0
  10. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
  11. package/.yarn/plugins/@yarnpkg/plugin-outdated.cjs +35 -0
  12. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +550 -0
  13. package/.yarn/versions/1bd1bb50.yml +0 -0
  14. package/README.md +39 -0
  15. package/__mocks__/react-i18next.js +50 -0
  16. package/assets/logo/logo.png +0 -0
  17. package/assets/logo/moh_logo_without_word.png +0 -0
  18. package/assets/screenshots/labs_enter_results.png +0 -0
  19. package/assets/screenshots/labs_general_dashboard.png +0 -0
  20. package/dist/142.js +1 -0
  21. package/dist/142.js.map +1 -0
  22. package/dist/319.js +1 -0
  23. package/dist/36.js +1 -0
  24. package/dist/36.js.map +1 -0
  25. package/dist/395.js +1 -0
  26. package/dist/395.js.map +1 -0
  27. package/dist/453.js +1 -0
  28. package/dist/453.js.map +1 -0
  29. package/dist/533.js +1 -0
  30. package/dist/533.js.map +1 -0
  31. package/dist/56.js +2 -0
  32. package/dist/56.js.LICENSE.txt +32 -0
  33. package/dist/56.js.map +1 -0
  34. package/dist/572.js +1 -0
  35. package/dist/572.js.map +1 -0
  36. package/dist/574.js +1 -0
  37. package/dist/581.js +1 -0
  38. package/dist/581.js.map +1 -0
  39. package/dist/66.js +1 -0
  40. package/dist/66.js.map +1 -0
  41. package/dist/757.js +1 -0
  42. package/dist/769.js +1 -0
  43. package/dist/769.js.map +1 -0
  44. package/dist/770.js +1 -0
  45. package/dist/770.js.map +1 -0
  46. package/dist/800.js +2 -0
  47. package/dist/800.js.LICENSE.txt +3 -0
  48. package/dist/800.js.map +1 -0
  49. package/dist/809.js +1 -0
  50. package/dist/809.js.map +1 -0
  51. package/dist/889.js +1 -0
  52. package/dist/889.js.map +1 -0
  53. package/dist/894.js +2 -0
  54. package/dist/894.js.LICENSE.txt +48 -0
  55. package/dist/894.js.map +1 -0
  56. package/dist/924.js +1 -0
  57. package/dist/924.js.map +1 -0
  58. package/dist/928.js +1 -0
  59. package/dist/928.js.map +1 -0
  60. package/dist/967b98e46b0984c4.png +0 -0
  61. package/dist/97.js +1 -0
  62. package/dist/97.js.map +1 -0
  63. package/dist/983.js +1 -0
  64. package/dist/983.js.map +1 -0
  65. package/dist/main.js +2 -0
  66. package/dist/main.js.LICENSE.txt +48 -0
  67. package/dist/main.js.map +1 -0
  68. package/dist/openmrs-esm-laboratory-app.js +1 -0
  69. package/dist/openmrs-esm-laboratory-app.js.buildmanifest.json +628 -0
  70. package/dist/openmrs-esm-laboratory-app.js.map +1 -0
  71. package/dist/routes.json +1 -0
  72. package/i18next-parser.config.js +89 -0
  73. package/jest.config.js +16 -0
  74. package/package.json +121 -0
  75. package/src/completed-list/completed-list.component.tsx +234 -0
  76. package/src/completed-list/completed-list.resource.ts +0 -0
  77. package/src/completed-list/completed-list.scss +232 -0
  78. package/src/components/create-dashboard-link.component.tsx +44 -0
  79. package/src/components/overlay/hook.ts +47 -0
  80. package/src/components/overlay/overlay.component.tsx +52 -0
  81. package/src/components/overlay/overlay.scss +93 -0
  82. package/src/config-schema.ts +36 -0
  83. package/src/constants.ts +5 -0
  84. package/src/declarations.d.ts +6 -0
  85. package/src/header/laboratory-header.component.tsx +35 -0
  86. package/src/header/laboratory-header.scss +68 -0
  87. package/src/header/laboratory-illustration.component.tsx +13 -0
  88. package/src/index.ts +92 -0
  89. package/src/laboratory.component.tsx +18 -0
  90. package/src/patient-chart/laboratory-item/view-laboratory-item.component.tsx +39 -0
  91. package/src/patient-chart/laboratory-item/view-laboratory-item.resource.ts +290 -0
  92. package/src/patient-chart/laboratory-item/view-laboratory-item.scss +0 -0
  93. package/src/patient-chart/laboratory-order.component.tsx +453 -0
  94. package/src/patient-chart/laboratory-order.resource.ts +437 -0
  95. package/src/patient-chart/laboratory-order.scss +66 -0
  96. package/src/patient-chart/results-summary/print-results-summary.component.tsx +240 -0
  97. package/src/patient-chart/results-summary/print-results-summary.scss +105 -0
  98. package/src/patient-chart/results-summary/print-results-table.component.tsx +163 -0
  99. package/src/patient-chart/results-summary/results/results.component.tsx +25 -0
  100. package/src/patient-chart/results-summary/results/results.resource.ts +50 -0
  101. package/src/patient-chart/results-summary/results/results.scss +0 -0
  102. package/src/patient-chart/results-summary/results-dialog/edit-results-dialog.component.tsx +46 -0
  103. package/src/patient-chart/results-summary/results-summary.component.tsx +98 -0
  104. package/src/patient-chart/results-summary/results-summary.resource.tsx +185 -0
  105. package/src/patient-chart/results-summary/results-summary.scss +154 -0
  106. package/src/patient-chart/results-summary/send-email-dialog.component.tsx +111 -0
  107. package/src/patient-chart/results-summary/test-children-results.component.tsx +221 -0
  108. package/src/patient-chart/results-summary/test-print-results-table.component.tsx +148 -0
  109. package/src/patient-chart/results-summary/test-results-delete-action-menu.component.tsx +27 -0
  110. package/src/patient-chart/results-summary/test-results-rescend-action-menu.component.tsx +26 -0
  111. package/src/patient-chart/results-summary/test-results-table.component.tsx +153 -0
  112. package/src/patient-chart/results-summary/tests-children-detail.component.tsx +54 -0
  113. package/src/patient-chart/results-summary/views/email.handlebars +13 -0
  114. package/src/queue-list/lab-dialogs/add-to-worklist-dialog.component.tsx +323 -0
  115. package/src/queue-list/lab-dialogs/add-to-worklist-dialog.resource.ts +155 -0
  116. package/src/queue-list/lab-dialogs/add-to-worklist-dialog.scss +20 -0
  117. package/src/queue-list/lab-tests/lab-tests.component.tsx +116 -0
  118. package/src/queue-list/lab-tests/lab-tests.resource.ts +17 -0
  119. package/src/queue-list/lab-tests/lab-tests.scss +12 -0
  120. package/src/queue-list/laboratory-patient-list.component.tsx +268 -0
  121. package/src/queue-list/laboratory-patient-list.resource.ts +86 -0
  122. package/src/queue-list/laboratory-queue.component.tsx +120 -0
  123. package/src/queue-list/laboratory-queue.scss +213 -0
  124. package/src/queue-list/laboratory-tabs.component.tsx +81 -0
  125. package/src/queue-list/pick-lab-request-menu.component.tsx +38 -0
  126. package/src/reject-order/reject-order-dialog.component.tsx +114 -0
  127. package/src/reject-order/reject-order-dialog.resource.ts +14 -0
  128. package/src/reject-order/reject-order-dialog.scss +14 -0
  129. package/src/results/result-form.component.tsx +223 -0
  130. package/src/results/result-form.resource.ts +328 -0
  131. package/src/results/result-form.scss +19 -0
  132. package/src/review-list/dialog/review-item.component.tsx +283 -0
  133. package/src/review-list/dialog/review-item.resource.ts +14 -0
  134. package/src/review-list/dialog/review-item.scss +0 -0
  135. package/src/review-list/review-list.component.tsx +269 -0
  136. package/src/review-list/review-list.resource.ts +0 -0
  137. package/src/review-list/review-list.scss +189 -0
  138. package/src/root.component.tsx +15 -0
  139. package/src/root.scss +50 -0
  140. package/src/routes.json +72 -0
  141. package/src/setup-tests.ts +1 -0
  142. package/src/summary-tiles/laboratory-summary-tiles.component.tsx +53 -0
  143. package/src/summary-tiles/laboratory-summary-tiles.scss +12 -0
  144. package/src/summary-tiles/laboratory-summary.resource.tsx +50 -0
  145. package/src/summary-tiles/summary-tile.component.tsx +48 -0
  146. package/src/summary-tiles/summary-tile.scss +43 -0
  147. package/src/types/index.ts +412 -0
  148. package/src/types/patient-queues.ts +189 -0
  149. package/src/utils/functions.ts +246 -0
  150. package/src/work-list/work-list.component.tsx +302 -0
  151. package/src/work-list/work-list.resource.ts +136 -0
  152. package/src/work-list/work-list.scss +215 -0
  153. package/translations/en.json +16 -0
  154. package/translations/es.json +3 -0
  155. package/translations/fr.json +3 -0
  156. package/tsconfig.json +23 -0
  157. package/webpack.config.js +29 -0
@@ -0,0 +1,453 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from "react";
8
+ import { useTranslation } from "react-i18next";
9
+ import { EmptyState } from "@ohri/openmrs-esm-ohri-commons-lib";
10
+ import styles from "./laboratory-order.scss";
11
+ import {
12
+ usePagination,
13
+ useSession,
14
+ formatDate,
15
+ openmrsFetch,
16
+ parseDate,
17
+ ErrorState,
18
+ useLayoutType,
19
+ showModal,
20
+ } from "@openmrs/esm-framework";
21
+
22
+ import {
23
+ DataTable,
24
+ DataTableSkeleton,
25
+ Table,
26
+ TableBody,
27
+ TableCell,
28
+ TableContainer,
29
+ TableHead,
30
+ TableHeader,
31
+ TableRow,
32
+ TableToolbar,
33
+ TableToolbarContent,
34
+ TableToolbarSearch,
35
+ Layer,
36
+ Tag,
37
+ DataTableHeader,
38
+ Tile,
39
+ Pagination,
40
+ TableExpandHeader,
41
+ TableExpandRow,
42
+ TableExpandedRow,
43
+ Button,
44
+ } from "@carbon/react";
45
+ import { Printer, MailAll, Edit } from "@carbon/react/icons";
46
+
47
+ import ViewLaboratoryItemActionMenu from "./laboratory-item/view-laboratory-item.component";
48
+ import { getOrderColor, useLabOrders } from "./laboratory-order.resource";
49
+ import TestsResults from "./results-summary/test-results-table.component";
50
+ import { useReactToPrint } from "react-to-print";
51
+ import SendEmailDialog from "./results-summary/send-email-dialog.component";
52
+ import PrintResultsSummary from "./results-summary/print-results-summary.component";
53
+ import { EncounterResponse } from "./laboratory-item/view-laboratory-item.resource";
54
+ import { useGetPatientByUuid } from "../utils/functions";
55
+
56
+ interface LaboratoryOrderOverviewProps {
57
+ patientUuid: string;
58
+ }
59
+
60
+ interface PrintProps {
61
+ encounter: EncounterResponse;
62
+ }
63
+
64
+ type FilterProps = {
65
+ rowIds: Array<string>;
66
+ headers: any;
67
+ cellsById: any;
68
+ inputValue: string;
69
+ getCellId: (row, key) => string;
70
+ };
71
+
72
+ const LaboratoryOrder: React.FC<LaboratoryOrderOverviewProps> = ({
73
+ patientUuid,
74
+ }) => {
75
+ const { t } = useTranslation();
76
+
77
+ const isTablet = useLayoutType() === "tablet";
78
+
79
+ const {
80
+ labRequests,
81
+ isLoading: loading,
82
+ isError,
83
+ } = useLabOrders(patientUuid);
84
+
85
+ const pageSizes = [10, 20, 30, 40, 50];
86
+ const [page, setPage] = useState(1);
87
+ const [currentPageSize, setPageSize] = useState(10);
88
+ const [nextOffSet, setNextOffSet] = useState(0);
89
+
90
+ const {
91
+ goTo,
92
+ results: paginatedLabEntries,
93
+ currentPage,
94
+ } = usePagination(labRequests, currentPageSize);
95
+
96
+ let columns = [
97
+ {
98
+ id: 0,
99
+ header: t("orderDate", "Test Date"),
100
+ key: "orderDate",
101
+ },
102
+ { id: 1, header: t("tests", "Tests"), key: "orders" },
103
+ { id: 2, header: t("location", "Location"), key: "location" },
104
+ { id: 3, header: t("status", "Status"), key: "status" },
105
+ { id: 4, header: t("actions", "Action"), key: "actions" },
106
+ ];
107
+
108
+ const [searchTerm, setSearchTerm] = useState("");
109
+ const [items, setItems] = useState(paginatedLabEntries);
110
+ const [initialTests] = useState(paginatedLabEntries);
111
+
112
+ const handleChange = useCallback(
113
+ (val) => {
114
+ const searchText = val?.target?.value;
115
+ setSearchTerm(searchText);
116
+ if (searchText?.trim() == "") {
117
+ setItems(initialTests);
118
+ } else {
119
+ let filteredItems = [];
120
+ items.map((item) => {
121
+ const newArray = item?.orders?.filter(
122
+ (order) =>
123
+ order?.concept?.display
124
+ .toLowerCase()
125
+ .startsWith(searchText?.toLowerCase()) == true
126
+ );
127
+ if (newArray.length >= 1) {
128
+ filteredItems.push(item);
129
+ }
130
+ });
131
+
132
+ setItems(filteredItems);
133
+ }
134
+ },
135
+ [items, initialTests]
136
+ );
137
+
138
+ const EmailButtonAction: React.FC = () => {
139
+ const launchSendEmailModal = useCallback(() => {
140
+ const dispose = showModal("send-email-dialog", {
141
+ closeModal: () => dispose(),
142
+ });
143
+ }, []);
144
+
145
+ return (
146
+ <Button
147
+ kind="ghost"
148
+ size="sm"
149
+ onClick={(e) => launchSendEmailModal()}
150
+ renderIcon={(props) => <MailAll size={16} {...props} />}
151
+ />
152
+ );
153
+ };
154
+
155
+ const PrintButtonAction: React.FC<PrintProps> = ({ encounter }) => {
156
+ const { patient } = useGetPatientByUuid(encounter.patient.uuid);
157
+
158
+ const [isPrinting, setIsPrinting] = useState(false);
159
+
160
+ const contentToPrintRef = useRef(null);
161
+
162
+ const onBeforeGetContentResolve = useRef(null);
163
+
164
+ useEffect(() => {
165
+ if (onBeforeGetContentResolve.current) {
166
+ onBeforeGetContentResolve.current();
167
+ }
168
+ }, [isPrinting]);
169
+
170
+ const handlePrint = useReactToPrint({
171
+ content: () => contentToPrintRef.current,
172
+ onBeforeGetContent: () =>
173
+ new Promise((resolve) => {
174
+ onBeforeGetContentResolve.current = resolve;
175
+ setIsPrinting(true);
176
+ }),
177
+ onAfterPrint: () => {
178
+ onBeforeGetContentResolve.current = null;
179
+ setIsPrinting(false);
180
+ },
181
+ });
182
+
183
+ return (
184
+ <div>
185
+ <div ref={contentToPrintRef}>
186
+ <PrintResultsSummary
187
+ encounterResponse={encounter}
188
+ patient={patient}
189
+ />
190
+ </div>
191
+ <Button
192
+ kind="ghost"
193
+ size="sm"
194
+ onClick={handlePrint}
195
+ renderIcon={(props) => <Printer size={16} {...props} />}
196
+ />
197
+ </div>
198
+ );
199
+ };
200
+ const handleFilter = ({
201
+ rowIds,
202
+ headers,
203
+ cellsById,
204
+ inputValue,
205
+ getCellId,
206
+ }: FilterProps): Array<string> => {
207
+ return rowIds.filter((rowId) =>
208
+ headers.some(({ key }) => {
209
+ const cellId = getCellId(rowId, key);
210
+ const filterableValue = cellsById[cellId].value;
211
+ const filterTerm = inputValue.toLowerCase();
212
+
213
+ if (typeof filterableValue === "boolean") {
214
+ return false;
215
+ }
216
+
217
+ return ("" + filterableValue).toLowerCase().includes(filterTerm);
218
+ })
219
+ );
220
+ };
221
+
222
+ const tableRows = useMemo(() => {
223
+ return paginatedLabEntries?.map((entry) => ({
224
+ ...entry,
225
+ id: entry.uuid,
226
+ orderDate: {
227
+ content: (
228
+ <span>
229
+ {formatDate(parseDate(entry.encounterDatetime), {
230
+ time: false,
231
+ })}
232
+ </span>
233
+ ),
234
+ },
235
+ orders: {
236
+ content: (
237
+ <>
238
+ {entry.orders.map((order) => {
239
+ if (order?.type === "testorder") {
240
+ return (
241
+ <Tag
242
+ style={{
243
+ background: `${getOrderColor(
244
+ order.dateActivated,
245
+ order.dateStopped
246
+ )}`,
247
+ color: "white",
248
+ }}
249
+ role="tooltip"
250
+ >
251
+ {order?.concept?.display}
252
+ </Tag>
253
+ );
254
+ }
255
+ })}
256
+ </>
257
+ ),
258
+ },
259
+ location: {
260
+ content: <span>{entry.location.display}</span>,
261
+ },
262
+ status: {
263
+ content: <span>--</span>,
264
+ },
265
+ actions: {
266
+ content: (
267
+ <div>
268
+ <PrintButtonAction encounter={entry} />
269
+ {/* <EmailButtonAction /> */}
270
+ </div>
271
+ ),
272
+ },
273
+ }));
274
+ }, [paginatedLabEntries]);
275
+
276
+ if (loading) {
277
+ return <DataTableSkeleton role="progressbar" />;
278
+ }
279
+
280
+ if (isError) {
281
+ return <ErrorState error={isError} headerTitle={"Error"} />;
282
+ }
283
+
284
+ if (items?.length >= 0) {
285
+ return (
286
+ <div>
287
+ <DataTable
288
+ rows={tableRows}
289
+ headers={columns}
290
+ useZebraStyles
291
+ filterRows={handleFilter}
292
+ size={isTablet ? "lg" : "sm"}
293
+ experimentalAutoAlign={true}
294
+ >
295
+ {({ rows, headers, getHeaderProps, getTableProps, getRowProps }) => (
296
+ <TableContainer className={styles.tableContainer}>
297
+ <TableToolbar
298
+ style={{
299
+ position: "static",
300
+ height: "3rem",
301
+ overflow: "visible",
302
+ backgroundColor: "color",
303
+ }}
304
+ >
305
+ <TableToolbarContent>
306
+ <div
307
+ style={{
308
+ fontSize: "10px",
309
+ margin: "5px",
310
+ display: "flex",
311
+ flexDirection: "row",
312
+ alignItems: "center",
313
+ }}
314
+ >
315
+ Key:
316
+ <Tag
317
+ size="sm"
318
+ style={{
319
+ background: "#6F6F6F",
320
+ color: "white",
321
+ }}
322
+ title="Result Requested"
323
+ >
324
+ {"Requested"}
325
+ </Tag>
326
+ <Tag
327
+ size="sm"
328
+ style={{
329
+ background: "green",
330
+ color: "white",
331
+ }}
332
+ title="Result Complete"
333
+ >
334
+ {"Completed"}
335
+ </Tag>
336
+ <Tag
337
+ size="sm"
338
+ style={{
339
+ background: "red",
340
+ color: "white",
341
+ }}
342
+ title="Result Rejected"
343
+ >
344
+ {"Rejected"}
345
+ </Tag>
346
+ </div>
347
+ <Layer>
348
+ <TableToolbarSearch
349
+ expanded={true}
350
+ value={searchTerm}
351
+ onChange={handleChange}
352
+ placeholder={t("searchThisList", "Search this list")}
353
+ size="sm"
354
+ />
355
+ </Layer>
356
+ </TableToolbarContent>
357
+ </TableToolbar>
358
+ <Table
359
+ {...getTableProps()}
360
+ className={styles.activePatientsTable}
361
+ >
362
+ <TableHead>
363
+ <TableRow>
364
+ <TableExpandHeader />
365
+ {headers.map((header) => (
366
+ <TableHeader {...getHeaderProps({ header })}>
367
+ {header.header}
368
+ </TableHeader>
369
+ ))}
370
+ </TableRow>
371
+ </TableHead>
372
+ <TableBody>
373
+ {rows.map((row, index) => {
374
+ return (
375
+ <React.Fragment key={row.id}>
376
+ <TableExpandRow {...getRowProps({ row })}>
377
+ {row.cells.map((cell) => (
378
+ <TableCell key={cell.id}>
379
+ {cell.value?.content ?? cell.value}
380
+ </TableCell>
381
+ ))}
382
+ </TableExpandRow>
383
+ {row.isExpanded ? (
384
+ <TableExpandedRow
385
+ className={styles.expandedActiveVisitRow}
386
+ colSpan={headers.length + 2}
387
+ >
388
+ <TestsResults
389
+ obs={paginatedLabEntries[index].obs}
390
+ />
391
+ </TableExpandedRow>
392
+ ) : (
393
+ <TableExpandedRow
394
+ className={styles.hiddenRow}
395
+ colSpan={headers.length + 2}
396
+ />
397
+ )}
398
+ </React.Fragment>
399
+ );
400
+ })}
401
+ </TableBody>
402
+ </Table>
403
+ {rows.length === 0 ? (
404
+ <div className={styles.tileContainer}>
405
+ <Tile className={styles.tile}>
406
+ <div className={styles.tileContent}>
407
+ <p className={styles.content}>
408
+ {t(
409
+ "noTestOrdersToDisplay",
410
+ "No test orders to display"
411
+ )}
412
+ </p>
413
+ <p className={styles.helper}>
414
+ {t("checkFilters", "Check the filters above")}
415
+ </p>
416
+ </div>
417
+ <p className={styles.separator}>{t("or", "or")}</p>
418
+ </Tile>
419
+ </div>
420
+ ) : null}
421
+ <Pagination
422
+ forwardText="Next page"
423
+ backwardText="Previous page"
424
+ page={currentPage}
425
+ pageSize={currentPageSize}
426
+ pageSizes={pageSizes}
427
+ totalItems={paginatedLabEntries?.length}
428
+ className={styles.pagination}
429
+ onChange={({ pageSize, page }) => {
430
+ if (pageSize !== currentPageSize) {
431
+ setPageSize(pageSize);
432
+ }
433
+ if (page !== currentPage) {
434
+ goTo(page);
435
+ }
436
+ }}
437
+ />
438
+ </TableContainer>
439
+ )}
440
+ </DataTable>
441
+ </div>
442
+ );
443
+ }
444
+
445
+ // return (
446
+ // <div>
447
+ // {/* <div className={styles.headerBtnContainer}></div> */}
448
+ // <EmptyState displayText={"Tests Ordered"} headerTitle={"Tests Ordered"} />
449
+ // </div>
450
+ // );
451
+ };
452
+
453
+ export default LaboratoryOrder;