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

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/3d4697b8.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 +242 -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 +277 -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 +277 -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 +310 -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,221 @@
1
+ import React, { AnchorHTMLAttributes, useMemo } from "react";
2
+ import { GroupMember, Value } from "../laboratory-order.resource";
3
+ import { useTranslation } from "react-i18next";
4
+ import {
5
+ DataTable,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableContainer,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ Tile,
14
+ InlineLoading,
15
+ } from "@carbon/react";
16
+ import styles from "./results-summary.scss";
17
+ import { useGetConceptById } from "./results-summary.resource";
18
+
19
+ interface TestsResultsChildrenProps {
20
+ members: GroupMember[];
21
+ }
22
+
23
+ interface ReferenceRangeProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
24
+ conceptUuid: string;
25
+ }
26
+
27
+ interface ColorRangeIndicatorProps
28
+ extends AnchorHTMLAttributes<HTMLAnchorElement> {
29
+ value: number | Value;
30
+ conceptUuid: string;
31
+ }
32
+
33
+ const TestResultsChildren: React.FC<TestsResultsChildrenProps> = ({
34
+ members,
35
+ }) => {
36
+ const { t } = useTranslation();
37
+
38
+ let columns = [
39
+ { id: 1, header: t("test", "Test"), key: "test" },
40
+
41
+ {
42
+ id: 2,
43
+ header: t("results", "Results"),
44
+ key: "value",
45
+ },
46
+ {
47
+ id: 3,
48
+ header: t("range", "Reference Range"),
49
+ key: "range",
50
+ },
51
+ ];
52
+
53
+ const results = useMemo(() => {
54
+ return members?.map((member) => ({
55
+ id: member.uuid,
56
+ test: {
57
+ content: <span>{member?.concept?.display}</span>,
58
+ },
59
+ value: {
60
+ content: (
61
+ <span>
62
+ {typeof member?.value === "number" ? member.value : member.display}
63
+ </span>
64
+ ),
65
+ },
66
+ }));
67
+ }, [members]);
68
+
69
+ if (members === undefined) {
70
+ return <span>No Data</span>;
71
+ }
72
+
73
+ const ReferenceRange: React.FC<ReferenceRangeProps> = ({ conceptUuid }) => {
74
+ const {
75
+ concept: concept,
76
+ isLoading,
77
+ isError,
78
+ } = useGetConceptById(conceptUuid);
79
+
80
+ if (isLoading) {
81
+ return <TableCell>{<InlineLoading status="active" />}</TableCell>;
82
+ }
83
+ if (isError) {
84
+ return <TableCell>{<span>Error</span>}</TableCell>;
85
+ }
86
+ return (
87
+ <TableCell>
88
+ {concept?.hiNormal === undefined || concept?.lowNormal === undefined ? (
89
+ "N/A"
90
+ ) : (
91
+ <div>
92
+ <span>{concept?.lowNormal}</span> : <span>{concept?.hiNormal}</span>
93
+ <span style={{ marginLeft: "10px" }}>{concept?.units}</span>
94
+ </div>
95
+ )}
96
+ </TableCell>
97
+ );
98
+ };
99
+
100
+ const ColorRangeIndicator: React.FC<ColorRangeIndicatorProps> = ({
101
+ value,
102
+ conceptUuid,
103
+ }) => {
104
+ const {
105
+ concept: concept,
106
+ isLoading,
107
+ isError,
108
+ } = useGetConceptById(conceptUuid);
109
+ if (isLoading) {
110
+ return <TableCell>{<InlineLoading status="active" />}</TableCell>;
111
+ }
112
+
113
+ if (isError) {
114
+ return <TableCell>{<span>Error</span>}</TableCell>;
115
+ }
116
+
117
+ if (concept?.datatype.display === "coded") {
118
+ return (
119
+ <TableCell>
120
+ {typeof value === "object" ? value.display : value}
121
+ </TableCell>
122
+ );
123
+ }
124
+
125
+ let range = "";
126
+ if (typeof value === "number") {
127
+ if (concept?.hiCritical && value >= concept?.hiCritical) {
128
+ range = styles.criticallyHigh;
129
+ }
130
+
131
+ if (concept?.hiNormal && value > concept?.hiNormal) {
132
+ range = styles.high;
133
+ }
134
+
135
+ if (concept?.lowCritical && value <= concept?.lowCritical) {
136
+ range = styles.criticallyLow;
137
+ }
138
+
139
+ if (concept?.lowNormal && value < concept?.lowNormal) {
140
+ range = styles.low;
141
+ }
142
+ return <TableCell className={range}>{value}</TableCell>;
143
+ }
144
+ return <TableCell>{value.display}</TableCell>;
145
+ };
146
+
147
+ if (results?.length >= 0) {
148
+ return (
149
+ <div>
150
+ <DataTable rows={results} headers={columns} useZebraStyles>
151
+ {({ rows, headers, getHeaderProps, getTableProps, getRowProps }) => (
152
+ <TableContainer className={styles.tableContainer}>
153
+ <Table
154
+ {...getTableProps()}
155
+ className={styles.activePatientsTable}
156
+ >
157
+ <TableHead>
158
+ <TableRow>
159
+ {headers.map((header) => (
160
+ <TableHeader {...getHeaderProps({ header })}>
161
+ {header.header}
162
+ </TableHeader>
163
+ ))}
164
+ </TableRow>
165
+ </TableHead>
166
+ <TableBody>
167
+ {rows.map((row, index) => {
168
+ return (
169
+ <React.Fragment key={row.id}>
170
+ <TableRow {...getRowProps({ row })}>
171
+ {row.cells.map((cell) =>
172
+ cell.info.header === "range" ? (
173
+ <ReferenceRange
174
+ conceptUuid={members[index].concept.uuid}
175
+ >
176
+ {cell.value?.content}
177
+ </ReferenceRange>
178
+ ) : cell.info.header === "value" ? (
179
+ <ColorRangeIndicator
180
+ conceptUuid={members[index].concept.uuid}
181
+ value={members[index].value}
182
+ />
183
+ ) : (
184
+ <TableCell key={cell.id}>
185
+ {cell.value?.content ?? cell.value}
186
+ </TableCell>
187
+ )
188
+ )}
189
+ </TableRow>
190
+ </React.Fragment>
191
+ );
192
+ })}
193
+ </TableBody>
194
+ </Table>
195
+ {rows.length === 0 ? (
196
+ <div className={styles.tileContainer}>
197
+ <Tile className={styles.tile}>
198
+ <div className={styles.tileContent}>
199
+ <p className={styles.content}>
200
+ {t(
201
+ "noTestOrdersToDisplay",
202
+ "No test orders to display"
203
+ )}
204
+ </p>
205
+ <p className={styles.helper}>
206
+ {t("checkFilters", "Check the filters above")}
207
+ </p>
208
+ </div>
209
+ <p className={styles.separator}>{t("or", "or")}</p>
210
+ </Tile>
211
+ </div>
212
+ ) : null}
213
+ </TableContainer>
214
+ )}
215
+ </DataTable>
216
+ </div>
217
+ );
218
+ }
219
+ };
220
+
221
+ export default TestResultsChildren;
@@ -0,0 +1,148 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import {
4
+ DataTable,
5
+ DataTableSkeleton,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableContainer,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ Tile,
14
+ TableExpandHeader,
15
+ TableExpandRow,
16
+ TableExpandedRow,
17
+ } from "@carbon/react";
18
+ import styles from "./results-summary.scss";
19
+ import RescendTestResultActionMenu from "./test-results-rescend-action-menu.component";
20
+ import { Order } from "../laboratory-order.resource";
21
+ import DeleteTestResultActionMenu from "./test-results-delete-action-menu.component";
22
+ import { Ob } from "../laboratory-item/view-laboratory-item.resource";
23
+ import TestResultsChildren from "./test-children-results.component";
24
+
25
+ interface TestOrdersProps {
26
+ obs: Ob[];
27
+ }
28
+
29
+ const TestsPrintResults: React.FC<TestOrdersProps> = ({ obs }) => {
30
+ const { t } = useTranslation();
31
+
32
+ let columns = [
33
+ { id: 1, header: t("order", "Order"), key: "order" },
34
+ { id: 2, header: t("results", "Results"), key: "result" },
35
+ ];
36
+
37
+ const filteredItems = obs.filter((ob) => ob?.order?.type === "testorder");
38
+
39
+ const tableRows = useMemo(() => {
40
+ return filteredItems?.map((entry) => ({
41
+ ...entry,
42
+ id: entry.uuid,
43
+ members: entry?.groupMembers,
44
+ order: {
45
+ content: <span>{entry.display}</span>,
46
+ },
47
+ result: {
48
+ content: <span>--</span>,
49
+ },
50
+ }));
51
+ }, [filteredItems]);
52
+
53
+ if (filteredItems?.length >= 0) {
54
+ return (
55
+ <div>
56
+ <DataTable
57
+ rows={tableRows}
58
+ headers={columns}
59
+ useZebraStyles
60
+ expanded={false}
61
+ >
62
+ {({ rows, headers, getHeaderProps, getTableProps, getRowProps }) => (
63
+ <TableContainer className={styles.tableContainer}>
64
+ <Table
65
+ {...getTableProps()}
66
+ className={styles.activePatientsTable}
67
+ >
68
+ <TableHead>
69
+ <TableRow>
70
+ <TableExpandHeader />
71
+ {headers.map((header) => (
72
+ <TableHeader {...getHeaderProps({ header })}>
73
+ {header.header}
74
+ </TableHeader>
75
+ ))}
76
+ </TableRow>
77
+ </TableHead>
78
+ <TableBody>
79
+ {rows.map((row, index) => {
80
+ return (
81
+ <React.Fragment key={row.id}>
82
+ <TableExpandRow {...getRowProps({ row })}>
83
+ {row.cells.map((cell) => (
84
+ <TableCell key={cell.id}>
85
+ {cell.value?.content ?? cell.value}
86
+ </TableCell>
87
+ ))}
88
+ </TableExpandRow>
89
+ {row.isExpanded ? (
90
+ <TableExpandedRow
91
+ className={styles.expandedActiveVisitRow}
92
+ colSpan={headers.length + 2}
93
+ >
94
+ <TestResultsChildren
95
+ members={filteredItems[index].groupMembers}
96
+ />
97
+ </TableExpandedRow>
98
+ ) : (
99
+ <TableExpandedRow
100
+ className={styles.hiddenRow}
101
+ colSpan={headers.length + 2}
102
+ />
103
+ )}
104
+ </React.Fragment>
105
+ );
106
+ })}
107
+ </TableBody>
108
+ </Table>
109
+ {rows.length === 0 ? (
110
+ <div className={styles.tileContainer}>
111
+ <Tile className={styles.tile}>
112
+ <div className={styles.tileContent}>
113
+ <p className={styles.content}>
114
+ {t(
115
+ "noTestResultsToDisplay",
116
+ "No test results to display"
117
+ )}
118
+ </p>
119
+ </div>
120
+ </Tile>
121
+ </div>
122
+ ) : null}
123
+ {/* <Pagination
124
+ forwardText="Next page"
125
+ backwardText="Previous page"
126
+ page={currentPage}
127
+ pageSize={currentPageSize}
128
+ pageSizes={pageSizes}
129
+ totalItems={patientQueueEntries?.length}
130
+ className={styles.pagination}
131
+ onChange={({ pageSize, page }) => {
132
+ if (pageSize !== currentPageSize) {
133
+ setPageSize(pageSize);
134
+ }
135
+ if (page !== currentPage) {
136
+ goTo(page);
137
+ }
138
+ }}
139
+ /> */}
140
+ </TableContainer>
141
+ )}
142
+ </DataTable>
143
+ </div>
144
+ );
145
+ }
146
+ };
147
+
148
+ export default TestsPrintResults;
@@ -0,0 +1,27 @@
1
+ import { showModal, useSession } from "@openmrs/esm-framework";
2
+ import React, { useCallback } from "react";
3
+ import { useTranslation } from "react-i18next";
4
+ import { Button, Tooltip } from "@carbon/react";
5
+ import { TrashCan } from "@carbon/react/icons";
6
+
7
+ interface RescendTestResultActionMenuProps {
8
+ closeModal: () => void;
9
+ }
10
+
11
+ const DeleteTestResultActionMenu: React.FC<
12
+ RescendTestResultActionMenuProps
13
+ > = () => {
14
+ const { t } = useTranslation();
15
+
16
+ return (
17
+ <Tooltip align="bottom" label="Delete Results">
18
+ <Button
19
+ kind="ghost"
20
+ iconDescription={t("delete", "Delete Test ")}
21
+ renderIcon={(props) => <TrashCan size={16} {...props} />}
22
+ ></Button>
23
+ </Tooltip>
24
+ );
25
+ };
26
+
27
+ export default DeleteTestResultActionMenu;
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Button, Tooltip } from "@carbon/react";
4
+ import { ArrowRight } from "@carbon/react/icons";
5
+
6
+ interface RescendTestResultActionMenuProps {
7
+ closeModal: () => void;
8
+ }
9
+
10
+ const RescendTestResultActionMenu: React.FC<
11
+ RescendTestResultActionMenuProps
12
+ > = () => {
13
+ const { t } = useTranslation();
14
+
15
+ return (
16
+ <Tooltip align="bottom" label="Rescend Results">
17
+ <Button
18
+ kind="ghost"
19
+ iconDescription={t("rescend", "Rescend Tests ")}
20
+ renderIcon={(props) => <ArrowRight size={16} {...props} />}
21
+ ></Button>
22
+ </Tooltip>
23
+ );
24
+ };
25
+
26
+ export default RescendTestResultActionMenu;
@@ -0,0 +1,153 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import {
4
+ DataTable,
5
+ DataTableSkeleton,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableContainer,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ Tile,
14
+ TableExpandHeader,
15
+ TableExpandRow,
16
+ TableExpandedRow,
17
+ } from "@carbon/react";
18
+ import styles from "./results-summary.scss";
19
+ import DeleteTestResultActionMenu from "./test-results-delete-action-menu.component";
20
+ import { Ob } from "../laboratory-item/view-laboratory-item.resource";
21
+ import TestResultsChildren from "./test-children-results.component";
22
+ import { formatDate, parseDate } from "@openmrs/esm-framework";
23
+
24
+ interface TestOrdersProps {
25
+ obs: Ob[];
26
+ }
27
+
28
+ const TestsResults: React.FC<TestOrdersProps> = ({ obs }) => {
29
+ const { t } = useTranslation();
30
+
31
+ let columns = [
32
+ { id: 0, header: t("order", "Order"), key: "order", align: "center" },
33
+ {
34
+ id: 1,
35
+ header: t("date", "Date"),
36
+ key: "date",
37
+ align: "center",
38
+ },
39
+ {
40
+ id: 2,
41
+ header: t("result", "Results"),
42
+ key: "result",
43
+ align: "center",
44
+ },
45
+
46
+ { id: 3, header: t("actions", "Actions"), key: "actions" },
47
+ ];
48
+
49
+ const obsList = obs.filter((ob) => ob?.order?.type === "testorder");
50
+
51
+ const obsRows = useMemo(() => {
52
+ return obsList.map((ob) => ({
53
+ id: ob.uuid,
54
+ order: {
55
+ content: <span>{ob?.concept?.display}</span>,
56
+ },
57
+ date: {
58
+ content: (
59
+ <span>
60
+ {formatDate(parseDate(ob?.obsDatetime), {
61
+ time: false,
62
+ })}
63
+ </span>
64
+ ),
65
+ },
66
+ result: {
67
+ content: <span>{ob?.display}</span>,
68
+ },
69
+
70
+ actions: {
71
+ content: (
72
+ <>
73
+ <DeleteTestResultActionMenu closeModal={() => true} />
74
+ </>
75
+ ),
76
+ },
77
+ }));
78
+ }, [obsList]);
79
+
80
+ if (obsRows?.length >= 0) {
81
+ return (
82
+ <div>
83
+ <DataTable rows={obsRows} headers={columns} useZebraStyles>
84
+ {({ rows, headers, getHeaderProps, getTableProps, getRowProps }) => (
85
+ <TableContainer className={styles.tableContainer}>
86
+ <Table
87
+ {...getTableProps()}
88
+ className={styles.activePatientsTable}
89
+ >
90
+ <TableHead>
91
+ <TableRow>
92
+ <TableExpandHeader />
93
+ {headers.map((header) => (
94
+ <TableHeader {...getHeaderProps({ header })}>
95
+ {header.header}
96
+ </TableHeader>
97
+ ))}
98
+ </TableRow>
99
+ </TableHead>
100
+ <TableBody>
101
+ {rows.map((row, index) => {
102
+ return (
103
+ <React.Fragment key={row.id}>
104
+ <TableExpandRow {...getRowProps({ row })}>
105
+ {row.cells.map((cell) => (
106
+ <TableCell key={cell.id}>
107
+ {cell.value?.content ?? cell.value}
108
+ </TableCell>
109
+ ))}
110
+ </TableExpandRow>
111
+ {row.isExpanded ? (
112
+ <TableExpandedRow
113
+ className={styles.expandedActiveVisitRow}
114
+ colSpan={headers.length + 2}
115
+ >
116
+ <TestResultsChildren
117
+ members={obsList[index].groupMembers}
118
+ />
119
+ </TableExpandedRow>
120
+ ) : (
121
+ <TableExpandedRow
122
+ className={styles.hiddenRow}
123
+ colSpan={headers.length + 2}
124
+ />
125
+ )}
126
+ </React.Fragment>
127
+ );
128
+ })}
129
+ </TableBody>
130
+ </Table>
131
+ {rows.length === 0 ? (
132
+ <div className={styles.tileContainer}>
133
+ <Tile className={styles.tile}>
134
+ <div className={styles.tileContent}>
135
+ <p className={styles.content}>
136
+ {t(
137
+ "noTestResultsToDisplay",
138
+ "No test results to display"
139
+ )}
140
+ </p>
141
+ </div>
142
+ </Tile>
143
+ </div>
144
+ ) : null}
145
+ </TableContainer>
146
+ )}
147
+ </DataTable>
148
+ </div>
149
+ );
150
+ }
151
+ };
152
+
153
+ export default TestsResults;
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import { useGetConceptById } from "./results-summary.resource";
4
+ import { DataTableSkeleton } from "@carbon/react";
5
+
6
+ interface TestsChildrenDetailProps {
7
+ conceptUuid: string;
8
+ }
9
+
10
+ const TestsChildrenDetail: React.FC<TestsChildrenDetailProps> = ({
11
+ conceptUuid,
12
+ }) => {
13
+ const { t } = useTranslation();
14
+
15
+ const {
16
+ concept: concept,
17
+ isLoading,
18
+ isError,
19
+ } = useGetConceptById(conceptUuid);
20
+
21
+ if (isLoading || isError) {
22
+ return <DataTableSkeleton />;
23
+ }
24
+
25
+ return (
26
+ <>
27
+ <div style={{ display: "flex", flexDirection: "column" }}>
28
+ <span style={{ fontSize: "16px" }}>{concept.display}</span>
29
+ <span>Unit : {concept.units}</span>
30
+ </div>
31
+
32
+ <div
33
+ style={{
34
+ display: "flex",
35
+ flexDirection: "row",
36
+ justifyContent: "space-between",
37
+ }}
38
+ >
39
+ <div style={{ display: "block" }}>
40
+ <span>Ranges</span>
41
+ <div>
42
+ <div>
43
+ <span> High : {concept.hiNormal} </span>
44
+ </div>
45
+ <div>
46
+ <span> Low : {concept?.lowNormal} </span>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </>
52
+ );
53
+ };
54
+ export default TestsChildrenDetail;
@@ -0,0 +1,13 @@
1
+ // email.handlebars
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta http-equiv="x-ua-compatible" content="ie=edge" />
7
+ <title>Results Email</title>
8
+ </head>
9
+ <body>
10
+ <h2>Hello {{name}}! </h2>
11
+ <p>We're glad to send your results. Find attachement below </p>
12
+ </body>
13
+ </html>