@openmrs/esm-laboratory-app 1.2.1-pre.734 → 1.2.1-pre.740
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/cache/8773ecc69845448d-meta.json +1 -0
- package/.turbo/cache/8773ecc69845448d.tar.zst +0 -0
- package/.turbo/turbo-build.log +2 -2
- package/dist/1120.js +1 -1
- package/dist/1120.js.map +1 -1
- package/dist/1788.js +1 -1
- package/dist/1788.js.map +1 -1
- package/dist/3656.js +1 -1
- package/dist/3656.js.map +1 -1
- package/dist/4069.js +1 -1
- package/dist/4069.js.map +1 -1
- package/dist/4300.js +1 -1
- package/dist/5085.js +1 -1
- package/dist/5085.js.map +1 -1
- package/dist/6134.js +1 -1
- package/dist/6134.js.map +1 -1
- package/dist/7423.js +1 -1
- package/dist/7423.js.map +1 -1
- package/dist/8554.js +1 -1
- package/dist/8554.js.map +1 -1
- package/dist/8667.js +1 -1
- package/dist/8667.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-laboratory-app.js.buildmanifest.json +33 -33
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/components/orders-table/list-order-details.component.tsx +31 -24
- package/src/components/orders-table/list-order-details.scss +0 -1
- package/src/components/orders-table/orders-data-table.component.tsx +143 -56
- package/src/components/orders-table/orders-data-table.test.tsx +215 -0
- package/src/config-schema.ts +20 -1
- package/src/lab-tabs/data-table-extensions/tests-ordered-table.extension.tsx +1 -1
- package/src/lab-tiles/all-lab-requests-tile.component.tsx +1 -1
- package/src/lab-tiles/completed-lab-requests-tile.component.tsx +1 -1
- package/src/lab-tiles/in-progress-lab-requests-tile.component.tsx +1 -1
- package/src/laboratory-resource.ts +24 -33
- package/src/types.ts +20 -17
- package/translations/en.json +2 -2
- package/.turbo/cache/8b374b02de72bb4b-meta.json +0 -1
- package/.turbo/cache/8b374b02de72bb4b.tar.zst +0 -0
|
@@ -218,9 +218,9 @@
|
|
|
218
218
|
"entry": false,
|
|
219
219
|
"recorded": false,
|
|
220
220
|
"reason": "split chunk (cache group: default)",
|
|
221
|
-
"size":
|
|
221
|
+
"size": 40790,
|
|
222
222
|
"sizes": {
|
|
223
|
-
"javascript":
|
|
223
|
+
"javascript": 40790
|
|
224
224
|
},
|
|
225
225
|
"names": [],
|
|
226
226
|
"idHints": [],
|
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
"auxiliaryFiles": [
|
|
235
235
|
"1120.js.map"
|
|
236
236
|
],
|
|
237
|
-
"hash": "
|
|
237
|
+
"hash": "29dddb34ccedc4c9",
|
|
238
238
|
"childrenByOrder": {}
|
|
239
239
|
},
|
|
240
240
|
{
|
|
@@ -264,10 +264,10 @@
|
|
|
264
264
|
"initial": false,
|
|
265
265
|
"entry": false,
|
|
266
266
|
"recorded": false,
|
|
267
|
-
"size":
|
|
267
|
+
"size": 75165,
|
|
268
268
|
"sizes": {
|
|
269
269
|
"consume-shared": 42,
|
|
270
|
-
"javascript":
|
|
270
|
+
"javascript": 75123
|
|
271
271
|
},
|
|
272
272
|
"names": [],
|
|
273
273
|
"idHints": [],
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
"auxiliaryFiles": [
|
|
281
281
|
"1788.js.map"
|
|
282
282
|
],
|
|
283
|
-
"hash": "
|
|
283
|
+
"hash": "9ff21cc90be63cdf",
|
|
284
284
|
"childrenByOrder": {}
|
|
285
285
|
},
|
|
286
286
|
{
|
|
@@ -526,9 +526,9 @@
|
|
|
526
526
|
"initial": false,
|
|
527
527
|
"entry": false,
|
|
528
528
|
"recorded": false,
|
|
529
|
-
"size":
|
|
529
|
+
"size": 606,
|
|
530
530
|
"sizes": {
|
|
531
|
-
"javascript":
|
|
531
|
+
"javascript": 606
|
|
532
532
|
},
|
|
533
533
|
"names": [],
|
|
534
534
|
"idHints": [],
|
|
@@ -542,7 +542,7 @@
|
|
|
542
542
|
"auxiliaryFiles": [
|
|
543
543
|
"3656.js.map"
|
|
544
544
|
],
|
|
545
|
-
"hash": "
|
|
545
|
+
"hash": "004c8b5953bfa4a0",
|
|
546
546
|
"childrenByOrder": {}
|
|
547
547
|
},
|
|
548
548
|
{
|
|
@@ -596,9 +596,9 @@
|
|
|
596
596
|
"initial": false,
|
|
597
597
|
"entry": false,
|
|
598
598
|
"recorded": false,
|
|
599
|
-
"size":
|
|
599
|
+
"size": 655,
|
|
600
600
|
"sizes": {
|
|
601
|
-
"javascript":
|
|
601
|
+
"javascript": 655
|
|
602
602
|
},
|
|
603
603
|
"names": [],
|
|
604
604
|
"idHints": [],
|
|
@@ -612,7 +612,7 @@
|
|
|
612
612
|
"auxiliaryFiles": [
|
|
613
613
|
"4069.js.map"
|
|
614
614
|
],
|
|
615
|
-
"hash": "
|
|
615
|
+
"hash": "b296e202ab7e3a08",
|
|
616
616
|
"childrenByOrder": {}
|
|
617
617
|
},
|
|
618
618
|
{
|
|
@@ -666,9 +666,9 @@
|
|
|
666
666
|
"initial": false,
|
|
667
667
|
"entry": false,
|
|
668
668
|
"recorded": false,
|
|
669
|
-
"size":
|
|
669
|
+
"size": 2175,
|
|
670
670
|
"sizes": {
|
|
671
|
-
"javascript":
|
|
671
|
+
"javascript": 2175
|
|
672
672
|
},
|
|
673
673
|
"names": [],
|
|
674
674
|
"idHints": [],
|
|
@@ -680,7 +680,7 @@
|
|
|
680
680
|
"4300.js"
|
|
681
681
|
],
|
|
682
682
|
"auxiliaryFiles": [],
|
|
683
|
-
"hash": "
|
|
683
|
+
"hash": "71deebbed405413c",
|
|
684
684
|
"childrenByOrder": {}
|
|
685
685
|
},
|
|
686
686
|
{
|
|
@@ -800,9 +800,9 @@
|
|
|
800
800
|
"initial": false,
|
|
801
801
|
"entry": false,
|
|
802
802
|
"recorded": false,
|
|
803
|
-
"size":
|
|
803
|
+
"size": 19613,
|
|
804
804
|
"sizes": {
|
|
805
|
-
"javascript":
|
|
805
|
+
"javascript": 19613
|
|
806
806
|
},
|
|
807
807
|
"names": [],
|
|
808
808
|
"idHints": [],
|
|
@@ -816,7 +816,7 @@
|
|
|
816
816
|
"auxiliaryFiles": [
|
|
817
817
|
"5085.js.map"
|
|
818
818
|
],
|
|
819
|
-
"hash": "
|
|
819
|
+
"hash": "a871c0871547ba58",
|
|
820
820
|
"childrenByOrder": {}
|
|
821
821
|
},
|
|
822
822
|
{
|
|
@@ -980,9 +980,9 @@
|
|
|
980
980
|
"entry": false,
|
|
981
981
|
"recorded": false,
|
|
982
982
|
"reason": "split chunk (cache group: default)",
|
|
983
|
-
"size":
|
|
983
|
+
"size": 148280,
|
|
984
984
|
"sizes": {
|
|
985
|
-
"javascript":
|
|
985
|
+
"javascript": 148280
|
|
986
986
|
},
|
|
987
987
|
"names": [],
|
|
988
988
|
"idHints": [],
|
|
@@ -996,7 +996,7 @@
|
|
|
996
996
|
"auxiliaryFiles": [
|
|
997
997
|
"6134.js.map"
|
|
998
998
|
],
|
|
999
|
-
"hash": "
|
|
999
|
+
"hash": "53524e1d0e200b7e",
|
|
1000
1000
|
"childrenByOrder": {}
|
|
1001
1001
|
},
|
|
1002
1002
|
{
|
|
@@ -1184,9 +1184,9 @@
|
|
|
1184
1184
|
"initial": false,
|
|
1185
1185
|
"entry": false,
|
|
1186
1186
|
"recorded": false,
|
|
1187
|
-
"size":
|
|
1187
|
+
"size": 694,
|
|
1188
1188
|
"sizes": {
|
|
1189
|
-
"javascript":
|
|
1189
|
+
"javascript": 694
|
|
1190
1190
|
},
|
|
1191
1191
|
"names": [],
|
|
1192
1192
|
"idHints": [],
|
|
@@ -1200,7 +1200,7 @@
|
|
|
1200
1200
|
"auxiliaryFiles": [
|
|
1201
1201
|
"7423.js.map"
|
|
1202
1202
|
],
|
|
1203
|
-
"hash": "
|
|
1203
|
+
"hash": "0fe8a00532cf2f89",
|
|
1204
1204
|
"childrenByOrder": {}
|
|
1205
1205
|
},
|
|
1206
1206
|
{
|
|
@@ -1274,9 +1274,9 @@
|
|
|
1274
1274
|
"initial": false,
|
|
1275
1275
|
"entry": false,
|
|
1276
1276
|
"recorded": false,
|
|
1277
|
-
"size":
|
|
1277
|
+
"size": 9606,
|
|
1278
1278
|
"sizes": {
|
|
1279
|
-
"javascript":
|
|
1279
|
+
"javascript": 9606
|
|
1280
1280
|
},
|
|
1281
1281
|
"names": [],
|
|
1282
1282
|
"idHints": [],
|
|
@@ -1290,7 +1290,7 @@
|
|
|
1290
1290
|
"auxiliaryFiles": [
|
|
1291
1291
|
"8554.js.map"
|
|
1292
1292
|
],
|
|
1293
|
-
"hash": "
|
|
1293
|
+
"hash": "63e9e6eda7a0d876",
|
|
1294
1294
|
"childrenByOrder": {}
|
|
1295
1295
|
},
|
|
1296
1296
|
{
|
|
@@ -1320,9 +1320,9 @@
|
|
|
1320
1320
|
"initial": false,
|
|
1321
1321
|
"entry": false,
|
|
1322
1322
|
"recorded": false,
|
|
1323
|
-
"size":
|
|
1323
|
+
"size": 674,
|
|
1324
1324
|
"sizes": {
|
|
1325
|
-
"javascript":
|
|
1325
|
+
"javascript": 674
|
|
1326
1326
|
},
|
|
1327
1327
|
"names": [],
|
|
1328
1328
|
"idHints": [],
|
|
@@ -1336,7 +1336,7 @@
|
|
|
1336
1336
|
"auxiliaryFiles": [
|
|
1337
1337
|
"8667.js.map"
|
|
1338
1338
|
],
|
|
1339
|
-
"hash": "
|
|
1339
|
+
"hash": "170e7977205c7d9a",
|
|
1340
1340
|
"childrenByOrder": {}
|
|
1341
1341
|
},
|
|
1342
1342
|
{
|
|
@@ -1344,10 +1344,10 @@
|
|
|
1344
1344
|
"initial": true,
|
|
1345
1345
|
"entry": true,
|
|
1346
1346
|
"recorded": false,
|
|
1347
|
-
"size":
|
|
1347
|
+
"size": 3972731,
|
|
1348
1348
|
"sizes": {
|
|
1349
1349
|
"consume-shared": 252,
|
|
1350
|
-
"javascript":
|
|
1350
|
+
"javascript": 3951432,
|
|
1351
1351
|
"share-init": 210,
|
|
1352
1352
|
"runtime": 20837
|
|
1353
1353
|
},
|
|
@@ -1364,7 +1364,7 @@
|
|
|
1364
1364
|
"auxiliaryFiles": [
|
|
1365
1365
|
"main.js.map"
|
|
1366
1366
|
],
|
|
1367
|
-
"hash": "
|
|
1367
|
+
"hash": "2b00889e33bc1a34",
|
|
1368
1368
|
"childrenByOrder": {}
|
|
1369
1369
|
},
|
|
1370
1370
|
{
|
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"laboratory"}],"extensions":[{"name":"laboratory-dashboard","slot":"laboratory-dashboard-slot","component":"root"},{"name":"laboratory-dashboard-link","slot":"homepage-dashboard-slot","component":"laboratoryDashboardLink","meta":{"name":"laboratory","slot":"laboratory-dashboard-slot","title":"Laboratory"}},{"name":"pickup-lab-request-modal","component":"pickupLabRequestModal"},{"name":"reject-lab-request-modal","component":"rejectLabRequestModal"},{"name":"all-lab-requests-table","slot":"lab-panels-slot","component":"allLabRequestsTable","meta":{"name":"inprogressPanel","title":"Tests ordered"}},{"name":"inprogress-lab-requests-table","slot":"lab-panels-slot","component":"inprogressLabRequestsTable","meta":{"name":"inprogressPanel","title":"In progress"}},{"name":"completed-lab-requests-table","slot":"lab-panels-slot","component":"completedLabRequestsTable","meta":{"name":"completedPanel","title":"Completed"}},{"name":"tests-ordered-tile-component","slot":"lab-tiles-slot","component":"testOrderedTile","meta":{"name":"testsOrderedTileSlot","title":"Ordered tests"}},{"name":"worklist-tile-component","slot":"lab-tiles-slot","component":"worklistTile","meta":{"name":"worklisTileSlot","title":"Worklist"}},{"name":"completed-tile-component","slot":"lab-tiles-slot","component":"completedTile","meta":{"name":"referredPanleSlot","title":"Referred tests"}},{"name":"declined-tile-component","slot":"lab-panels-slot","component":"declinedLabRequestsTable","meta":{"name":"declinedPanel","title":"Declined tests"}},{"name":"pick-lab-request-action","component":"pickupLabRequestAction","slot":"tests-ordered-actions-slot"},{"name":"reject-lab-request-tests-ordered-action","component":"rejectLabRequestAction","slot":"rejected-ordered-actions-slot"},{"name":"edit-lab-request-tests-ordered-action","component":"editLabRequestAction","slot":"completed-ordered-actions-slot"},{"name":"reject-lab-request-worklist-action","component":"rejectLabRequestAction","slot":"inprogress-tests-actions-slot"},{"name":"add-lab-request-results-action","component":"addLabRequestResultsAction","slot":"inprogress-tests-actions-slot"},{"name":"edit-lab-request-tests-ordered-action","component":"editLabRequestAction","slot":"completed-ordered-actions-slot"}],"version":"1.2.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"laboratory"}],"extensions":[{"name":"laboratory-dashboard","slot":"laboratory-dashboard-slot","component":"root"},{"name":"laboratory-dashboard-link","slot":"homepage-dashboard-slot","component":"laboratoryDashboardLink","meta":{"name":"laboratory","slot":"laboratory-dashboard-slot","title":"Laboratory"}},{"name":"pickup-lab-request-modal","component":"pickupLabRequestModal"},{"name":"reject-lab-request-modal","component":"rejectLabRequestModal"},{"name":"all-lab-requests-table","slot":"lab-panels-slot","component":"allLabRequestsTable","meta":{"name":"inprogressPanel","title":"Tests ordered"}},{"name":"inprogress-lab-requests-table","slot":"lab-panels-slot","component":"inprogressLabRequestsTable","meta":{"name":"inprogressPanel","title":"In progress"}},{"name":"completed-lab-requests-table","slot":"lab-panels-slot","component":"completedLabRequestsTable","meta":{"name":"completedPanel","title":"Completed"}},{"name":"tests-ordered-tile-component","slot":"lab-tiles-slot","component":"testOrderedTile","meta":{"name":"testsOrderedTileSlot","title":"Ordered tests"}},{"name":"worklist-tile-component","slot":"lab-tiles-slot","component":"worklistTile","meta":{"name":"worklisTileSlot","title":"Worklist"}},{"name":"completed-tile-component","slot":"lab-tiles-slot","component":"completedTile","meta":{"name":"referredPanleSlot","title":"Referred tests"}},{"name":"declined-tile-component","slot":"lab-panels-slot","component":"declinedLabRequestsTable","meta":{"name":"declinedPanel","title":"Declined tests"}},{"name":"pick-lab-request-action","component":"pickupLabRequestAction","slot":"tests-ordered-actions-slot"},{"name":"reject-lab-request-tests-ordered-action","component":"rejectLabRequestAction","slot":"rejected-ordered-actions-slot"},{"name":"edit-lab-request-tests-ordered-action","component":"editLabRequestAction","slot":"completed-ordered-actions-slot"},{"name":"reject-lab-request-worklist-action","component":"rejectLabRequestAction","slot":"inprogress-tests-actions-slot"},{"name":"add-lab-request-results-action","component":"addLabRequestResultsAction","slot":"inprogress-tests-actions-slot"},{"name":"edit-lab-request-tests-ordered-action","component":"editLabRequestAction","slot":"completed-ordered-actions-slot"}],"version":"1.2.1-pre.740"}
|
package/package.json
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
StructuredListWrapper,
|
|
11
11
|
} from '@carbon/react';
|
|
12
12
|
import { capitalize } from 'lodash-es';
|
|
13
|
-
import { ExtensionSlot } from '@openmrs/esm-framework';
|
|
14
|
-
import { type
|
|
13
|
+
import { ExtensionSlot, formatDate, parseDate } from '@openmrs/esm-framework';
|
|
14
|
+
import { type GroupedOrders, type OrderAction } from '../../types';
|
|
15
15
|
import styles from './list-order-details.scss';
|
|
16
16
|
|
|
17
17
|
type OrderDetailsRowProps = {
|
|
@@ -19,6 +19,11 @@ type OrderDetailsRowProps = {
|
|
|
19
19
|
value: ReactNode;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
export interface ListOrdersDetailsProps {
|
|
23
|
+
groupedOrders: GroupedOrders;
|
|
24
|
+
actions: Array<OrderAction>;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
const OrderDetailRow = ({ label, value }: OrderDetailsRowProps) => {
|
|
23
28
|
return (
|
|
24
29
|
<StructuredListRow className={styles.orderDetailsRow}>
|
|
@@ -33,48 +38,51 @@ const OrderDetailRow = ({ label, value }: OrderDetailsRowProps) => {
|
|
|
33
38
|
};
|
|
34
39
|
const ListOrderDetails: React.FC<ListOrdersDetailsProps> = ({ groupedOrders }) => {
|
|
35
40
|
const { t } = useTranslation();
|
|
36
|
-
const
|
|
41
|
+
const originalOrders = groupedOrders?.originalOrders ?? [];
|
|
37
42
|
|
|
38
43
|
return (
|
|
39
44
|
<div>
|
|
40
|
-
{
|
|
41
|
-
<div key={
|
|
45
|
+
{originalOrders.map((order) => (
|
|
46
|
+
<div key={order.orderNumber} className={styles.orderDetailsContainer}>
|
|
42
47
|
<StructuredListWrapper className={styles.orderDetailsWrapper}>
|
|
43
48
|
<StructuredListBody>
|
|
44
49
|
<OrderDetailRow
|
|
45
50
|
label={t('urgencyStatus', 'Urgency:')}
|
|
46
51
|
value={
|
|
47
|
-
<div className={styles.priorityPill} data-urgency={
|
|
48
|
-
{capitalize(
|
|
52
|
+
<div className={styles.priorityPill} data-urgency={order.urgency?.replace('_', ' ')}>
|
|
53
|
+
{capitalize(order.urgency?.replace(/_/g, ' '))}
|
|
49
54
|
</div>
|
|
50
55
|
}
|
|
51
56
|
/>
|
|
52
|
-
<OrderDetailRow label={t('testOrdered', 'Test ordered:')} value={
|
|
57
|
+
<OrderDetailRow label={t('testOrdered', 'Test ordered:')} value={order.display} />
|
|
53
58
|
<OrderDetailRow
|
|
54
59
|
label={t('orderStatus', 'Status:')}
|
|
55
60
|
value={
|
|
56
61
|
<div
|
|
57
62
|
className={styles.statusPill}
|
|
58
|
-
data-status={(
|
|
63
|
+
data-status={(order.fulfillerStatus ?? 'Order not picked').replace('_', ' ')}
|
|
59
64
|
>
|
|
60
|
-
{capitalize(
|
|
65
|
+
{capitalize(order.fulfillerStatus?.replace('_', '')) || t('orderNotPicked', 'Order not picked')}
|
|
61
66
|
</div>
|
|
62
67
|
}
|
|
63
68
|
/>
|
|
64
|
-
<OrderDetailRow label={t('orderNumbers', 'Order number:')} value={
|
|
65
|
-
<OrderDetailRow
|
|
66
|
-
|
|
69
|
+
<OrderDetailRow label={t('orderNumbers', 'Order number:')} value={order.orderNumber} />
|
|
70
|
+
<OrderDetailRow
|
|
71
|
+
label={t('orderDate', 'Order date:')}
|
|
72
|
+
value={formatDate(parseDate(order.dateActivated))}
|
|
73
|
+
/>
|
|
74
|
+
<OrderDetailRow label={t('orderedBy', 'Ordered By:')} value={order.orderer.display} />
|
|
67
75
|
<OrderDetailRow
|
|
68
76
|
label={t('orderInstructions', 'Instructions:')}
|
|
69
|
-
value={
|
|
77
|
+
value={order.instructions ?? t('NoInstructionLeft', 'No instructions are provided.')}
|
|
70
78
|
/>
|
|
71
79
|
|
|
72
|
-
{
|
|
73
|
-
<OrderDetailRow label={t('reasonForDecline', 'Reason for decline:')} value={
|
|
80
|
+
{order.fulfillerStatus === 'DECLINED' && (
|
|
81
|
+
<OrderDetailRow label={t('reasonForDecline', 'Reason for decline:')} value={order.fulfillerComment} />
|
|
74
82
|
)}
|
|
75
83
|
</StructuredListBody>
|
|
76
84
|
</StructuredListWrapper>
|
|
77
|
-
{
|
|
85
|
+
{order.fulfillerStatus === 'COMPLETED' && (
|
|
78
86
|
<Accordion>
|
|
79
87
|
<AccordionItem
|
|
80
88
|
title={<span className={styles.accordionTitle}>{t('viewTestResults', 'View test results')}</span>}
|
|
@@ -82,7 +90,7 @@ const ListOrderDetails: React.FC<ListOrdersDetailsProps> = ({ groupedOrders }) =
|
|
|
82
90
|
<div className={styles.viewResults}>
|
|
83
91
|
<ExtensionSlot
|
|
84
92
|
className={styles.labResultSlot}
|
|
85
|
-
state={{ order:
|
|
93
|
+
state={{ order: order }}
|
|
86
94
|
name="completed-lab-order-results-slot"
|
|
87
95
|
/>
|
|
88
96
|
</div>
|
|
@@ -91,20 +99,19 @@ const ListOrderDetails: React.FC<ListOrdersDetailsProps> = ({ groupedOrders }) =
|
|
|
91
99
|
)}
|
|
92
100
|
|
|
93
101
|
<div className={styles.buttonSection}>
|
|
94
|
-
{
|
|
95
|
-
{row.fulfillerStatus === 'New' || row.fulfillerStatus === 'RECEIVED' || row.fulfillerStatus == null ? (
|
|
102
|
+
{order.fulfillerStatus === 'RECEIVED' || order.fulfillerStatus == null ? (
|
|
96
103
|
<>
|
|
97
104
|
<div className={styles.testsOrderedActions}>
|
|
98
|
-
<ExtensionSlot state={{ order:
|
|
99
|
-
<ExtensionSlot state={{ order:
|
|
105
|
+
<ExtensionSlot state={{ order: order }} name="rejected-ordered-actions-slot" />
|
|
106
|
+
<ExtensionSlot state={{ order: order }} name="tests-ordered-actions-slot" />
|
|
100
107
|
</div>
|
|
101
108
|
</>
|
|
102
|
-
) :
|
|
109
|
+
) : order.fulfillerStatus === 'IN_PROGRESS' ? (
|
|
103
110
|
<>
|
|
104
111
|
<div className={styles.testsOrderedActions}>
|
|
105
112
|
<ExtensionSlot
|
|
106
113
|
className={styles.menuLink}
|
|
107
|
-
state={{ order:
|
|
114
|
+
state={{ order: order }}
|
|
108
115
|
name="inprogress-tests-actions-slot"
|
|
109
116
|
/>
|
|
110
117
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
DataTable,
|
|
4
4
|
DataTableSkeleton,
|
|
@@ -22,67 +22,152 @@ import {
|
|
|
22
22
|
TableToolbarSearch,
|
|
23
23
|
Tile,
|
|
24
24
|
} from '@carbon/react';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
ExtensionSlot,
|
|
27
|
+
formatDate,
|
|
28
|
+
parseDate,
|
|
29
|
+
type Patient,
|
|
30
|
+
showModal,
|
|
31
|
+
useConfig,
|
|
32
|
+
usePagination,
|
|
33
|
+
} from '@openmrs/esm-framework';
|
|
26
34
|
import { useTranslation } from 'react-i18next';
|
|
27
|
-
import { type Order } from '@openmrs/esm-patient-common-lib';
|
|
28
|
-
import type
|
|
29
|
-
import { useLabOrders
|
|
35
|
+
import { type Order, type FulfillerStatus } from '@openmrs/esm-patient-common-lib';
|
|
36
|
+
import { type FlattenedOrder, type OrderAction } from '../../types';
|
|
37
|
+
import { useLabOrders } from '../../laboratory-resource';
|
|
30
38
|
import { OrdersDateRangePicker } from './orders-date-range-picker.component';
|
|
31
39
|
import ListOrderDetails from './list-order-details.component';
|
|
32
40
|
import styles from './orders-data-table.scss';
|
|
41
|
+
import { type Config } from '../../config-schema';
|
|
42
|
+
|
|
43
|
+
const labTableColumnSpec = {
|
|
44
|
+
name: {
|
|
45
|
+
// t('patient', 'Patient')
|
|
46
|
+
headerLabelKey: 'patient',
|
|
47
|
+
headerLabelDefault: 'Patient',
|
|
48
|
+
key: 'patientName',
|
|
49
|
+
},
|
|
50
|
+
age: {
|
|
51
|
+
// t('age', 'Age')
|
|
52
|
+
headerLabelKey: 'age',
|
|
53
|
+
headerLabelDefault: 'Age',
|
|
54
|
+
key: 'patientAge',
|
|
55
|
+
},
|
|
56
|
+
sex: {
|
|
57
|
+
// t('sex', 'Sex')
|
|
58
|
+
headerLabelKey: 'sex',
|
|
59
|
+
headerLabelDefault: 'Sex',
|
|
60
|
+
key: 'patientSex',
|
|
61
|
+
},
|
|
62
|
+
totalOrders: {
|
|
63
|
+
// t('totalOrders', 'Total Orders')
|
|
64
|
+
headerLabelKey: 'totalOrders',
|
|
65
|
+
headerLabelDefault: 'Total Orders',
|
|
66
|
+
key: 'totalOrders',
|
|
67
|
+
},
|
|
68
|
+
action: {
|
|
69
|
+
// t('action', 'Action')
|
|
70
|
+
headerLabelKey: 'action',
|
|
71
|
+
headerLabelDefault: 'Action',
|
|
72
|
+
key: 'action',
|
|
73
|
+
},
|
|
74
|
+
patientId: {
|
|
75
|
+
// t('patientId', 'Patient ID')
|
|
76
|
+
headerLabelKey: 'patientId',
|
|
77
|
+
headerLabelDefault: 'Patient ID',
|
|
78
|
+
key: 'patientId',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export interface OrdersDataTableProps {
|
|
83
|
+
/* Whether the data table should include a status filter dropdown */
|
|
84
|
+
useFilter?: boolean;
|
|
85
|
+
actionsSlotName?: string;
|
|
86
|
+
excludeColumns?: Array<string>;
|
|
87
|
+
fulfillerStatus?: FulfillerStatus;
|
|
88
|
+
newOrdersOnly?: boolean;
|
|
89
|
+
excludeCanceledAndDiscontinuedOrders?: boolean;
|
|
90
|
+
actions?: Array<OrderAction>;
|
|
91
|
+
}
|
|
33
92
|
|
|
34
93
|
const OrdersDataTable: React.FC<OrdersDataTableProps> = (props) => {
|
|
35
94
|
const { t } = useTranslation();
|
|
36
95
|
const [filter, setFilter] = useState<FulfillerStatus>(null);
|
|
37
96
|
const [searchString, setSearchString] = useState('');
|
|
97
|
+
const { labTableColumns, patientIdIdentifierTypeUuid } = useConfig<Config>();
|
|
38
98
|
|
|
39
|
-
const { labOrders, isLoading } = useLabOrders(
|
|
40
|
-
props.useFilter ? filter : props.fulfillerStatus,
|
|
41
|
-
props.
|
|
42
|
-
|
|
99
|
+
const { labOrders, isLoading } = useLabOrders({
|
|
100
|
+
status: props.useFilter ? filter : props.fulfillerStatus,
|
|
101
|
+
newOrdersOnly: props.newOrdersOnly,
|
|
102
|
+
excludeCanceled: props.excludeCanceledAndDiscontinuedOrders,
|
|
103
|
+
includePatientId: labTableColumns.includes('patientId'),
|
|
104
|
+
});
|
|
43
105
|
|
|
44
|
-
const flattenedLabOrders:
|
|
106
|
+
const flattenedLabOrders: Array<FlattenedOrder> = useMemo(() => {
|
|
45
107
|
return (
|
|
46
108
|
labOrders?.map((order) => {
|
|
47
109
|
return {
|
|
48
|
-
|
|
110
|
+
id: order.uuid,
|
|
111
|
+
patientUuid: order.patient.uuid,
|
|
112
|
+
orderNumber: order.orderNumber,
|
|
49
113
|
dateActivated: formatDate(parseDate(order.dateActivated)),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
114
|
+
fulfillerStatus: order.fulfillerStatus,
|
|
115
|
+
urgency: order.urgency,
|
|
116
|
+
orderer: order.orderer?.display,
|
|
117
|
+
instructions: order.instructions,
|
|
118
|
+
fulfillerComment: order.fulfillerComment,
|
|
119
|
+
display: order.display,
|
|
55
120
|
};
|
|
56
121
|
}) ?? []
|
|
57
122
|
);
|
|
58
123
|
}, [labOrders]);
|
|
59
124
|
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
const
|
|
63
|
-
if (!acc[item.patientUuid]) {
|
|
64
|
-
acc[item.patientUuid] = [];
|
|
65
|
-
}
|
|
66
|
-
acc[item.patientUuid].push(item);
|
|
67
|
-
return acc;
|
|
68
|
-
}, {});
|
|
125
|
+
const groupedOrdersByPatient = useMemo(() => {
|
|
126
|
+
if (labOrders && labOrders.length > 0) {
|
|
127
|
+
const patientUuids = [...new Set(labOrders.map((order) => order.patient.uuid))];
|
|
69
128
|
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
129
|
+
return patientUuids.map((patientUuid) => {
|
|
130
|
+
const labOrdersForPatient = labOrders.filter((order) => order.patient.uuid === patientUuid);
|
|
131
|
+
const patient: Patient = labOrdersForPatient[0]?.patient;
|
|
132
|
+
const flattenedLabOrdersForPatient = flattenedLabOrders.filter((order) => order.patientUuid === patientUuid);
|
|
133
|
+
return {
|
|
134
|
+
patientId: patient.identifiers?.find(
|
|
135
|
+
(identifier) =>
|
|
136
|
+
identifier.preferred &&
|
|
137
|
+
!identifier.voided &&
|
|
138
|
+
identifier.identifierType.uuid === patientIdIdentifierTypeUuid,
|
|
139
|
+
)?.identifier,
|
|
140
|
+
patientUuid: patientUuid,
|
|
141
|
+
patientName: patient.person.display,
|
|
142
|
+
patientAge: patient.person.age,
|
|
143
|
+
patientSex: patient.person.gender,
|
|
144
|
+
totalOrders: flattenedLabOrdersForPatient.length,
|
|
145
|
+
orders: flattenedLabOrdersForPatient,
|
|
146
|
+
originalOrders: labOrdersForPatient,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
74
149
|
} else {
|
|
75
150
|
return [];
|
|
76
151
|
}
|
|
77
|
-
}
|
|
152
|
+
}, [flattenedLabOrders, labOrders, patientIdIdentifierTypeUuid]);
|
|
78
153
|
|
|
79
|
-
const
|
|
154
|
+
const searchResults = useMemo(() => {
|
|
155
|
+
if (searchString && searchString.trim() !== '') {
|
|
156
|
+
// Normalize the search string to lowercase
|
|
157
|
+
const lowerSearchString = searchString.toLowerCase();
|
|
158
|
+
return groupedOrdersByPatient.filter(
|
|
159
|
+
(orderGroup) =>
|
|
160
|
+
(labTableColumns.includes('name') && orderGroup.patientName.toLowerCase().includes(lowerSearchString)) ||
|
|
161
|
+
(labTableColumns.includes('patientId') && orderGroup.patientId.toLowerCase().includes(lowerSearchString)) ||
|
|
162
|
+
orderGroup.orders.some((order) => order.orderNumber.toLowerCase().includes(lowerSearchString)),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
80
165
|
|
|
81
|
-
|
|
166
|
+
return groupedOrdersByPatient;
|
|
167
|
+
}, [searchString, groupedOrdersByPatient, labTableColumns]);
|
|
82
168
|
|
|
83
169
|
const orderStatuses = [
|
|
84
170
|
{ value: null, display: t('all', 'All') },
|
|
85
|
-
{ value: 'NEW', display: t('newStatus', 'NEW') },
|
|
86
171
|
{ value: 'RECEIVED', display: t('receivedStatus', 'RECEIVED') },
|
|
87
172
|
{ value: 'IN_PROGRESS', display: t('inProgressStatus', 'IN_PROGRESS') },
|
|
88
173
|
{ value: 'COMPLETED', display: t('completedStatus', 'COMPLETED') },
|
|
@@ -92,17 +177,23 @@ const OrdersDataTable: React.FC<OrdersDataTableProps> = (props) => {
|
|
|
92
177
|
];
|
|
93
178
|
|
|
94
179
|
const columns = useMemo(() => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
180
|
+
return labTableColumns
|
|
181
|
+
.map((column) => {
|
|
182
|
+
const spec = labTableColumnSpec[column];
|
|
183
|
+
if (!spec) {
|
|
184
|
+
throw new Error(`Lab table has been configured with an invalid column: ${column}`);
|
|
185
|
+
}
|
|
186
|
+
if (spec.key === 'action') {
|
|
187
|
+
const showActionColumn = flattenedLabOrders.some((order) => order.fulfillerStatus === 'COMPLETED');
|
|
188
|
+
if (!showActionColumn) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { header: t(spec.headerLabelKey, spec.headerLabelDefault), key: spec.key };
|
|
193
|
+
})
|
|
194
|
+
.filter(Boolean)
|
|
195
|
+
.map((column, index) => ({ ...column, id: index }));
|
|
196
|
+
}, [t, flattenedLabOrders, labTableColumns]);
|
|
106
197
|
|
|
107
198
|
const pageSizes = [10, 20, 30, 40, 50];
|
|
108
199
|
const [currentPageSize, setPageSize] = useState(10);
|
|
@@ -127,32 +218,28 @@ const OrdersDataTable: React.FC<OrdersDataTableProps> = (props) => {
|
|
|
127
218
|
};
|
|
128
219
|
|
|
129
220
|
const tableRows = useMemo(() => {
|
|
130
|
-
return paginatedLabOrders.map((
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
totalOrders: order.orders?.length,
|
|
135
|
-
patientAge: order.orders[0]?.patient?.person?.age,
|
|
136
|
-
patientGender: order.orders[0]?.patient?.person?.gender || '',
|
|
137
|
-
action: order.orders.some((o) => o.fulfillerStatus === 'COMPLETED') ? (
|
|
221
|
+
return paginatedLabOrders.map((groupedOrder) => ({
|
|
222
|
+
...groupedOrder,
|
|
223
|
+
id: groupedOrder.patientUuid,
|
|
224
|
+
action: groupedOrder.orders.some((o) => o.fulfillerStatus === 'COMPLETED') ? (
|
|
138
225
|
<div className={styles.actionCell}>
|
|
139
226
|
<OverflowMenu aria-label="Actions" flipped iconDescription="Actions">
|
|
140
227
|
<ExtensionSlot
|
|
141
228
|
className={styles.transitionOverflowMenuItemSlot}
|
|
142
229
|
name="transition-overflow-menu-item-slot"
|
|
143
|
-
state={{ patientUuid:
|
|
230
|
+
state={{ patientUuid: groupedOrder.patientUuid }}
|
|
144
231
|
// Without tabIndex={0} here, the overflow menu incorrectly sets initial focus to the second item instead of the first.
|
|
145
232
|
tabIndex={0}
|
|
146
233
|
/>
|
|
147
234
|
<OverflowMenuItem
|
|
148
235
|
className={styles.menuitem}
|
|
149
236
|
itemText={t('editResults', 'Edit results')}
|
|
150
|
-
onClick={() => handleLaunchModal(
|
|
237
|
+
onClick={() => handleLaunchModal(groupedOrder.originalOrders)}
|
|
151
238
|
/>
|
|
152
239
|
<OverflowMenuItem
|
|
153
240
|
className={styles.menuitem}
|
|
154
241
|
itemText={t('printTestResults', 'Print test results')}
|
|
155
|
-
onClick={() => handlePrintModal(
|
|
242
|
+
onClick={() => handlePrintModal(groupedOrder.originalOrders)}
|
|
156
243
|
/>
|
|
157
244
|
</OverflowMenu>
|
|
158
245
|
</div>
|
|
@@ -218,7 +305,7 @@ const OrdersDataTable: React.FC<OrdersDataTableProps> = (props) => {
|
|
|
218
305
|
<TableExpandedRow colSpan={headers.length + 2}>
|
|
219
306
|
<ListOrderDetails
|
|
220
307
|
actions={props.actions}
|
|
221
|
-
groupedOrders={groupedOrdersByPatient.find((item) => item.
|
|
308
|
+
groupedOrders={groupedOrdersByPatient.find((item) => item.patientUuid === row.id)}
|
|
222
309
|
/>
|
|
223
310
|
</TableExpandedRow>
|
|
224
311
|
) : (
|