@thrustdevs/esm-procedure-orders-app 1.0.2-pre.6

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 (207) hide show
  1. package/.turbo/turbo-build.log +41 -0
  2. package/README.md +7 -0
  3. package/dist/122.js +1 -0
  4. package/dist/122.js.map +1 -0
  5. package/dist/144.js +2 -0
  6. package/dist/144.js.LICENSE.txt +19 -0
  7. package/dist/144.js.map +1 -0
  8. package/dist/182.js +1 -0
  9. package/dist/182.js.map +1 -0
  10. package/dist/205.js +1 -0
  11. package/dist/205.js.map +1 -0
  12. package/dist/216.js +2 -0
  13. package/dist/216.js.LICENSE.txt +9 -0
  14. package/dist/216.js.map +1 -0
  15. package/dist/290.js +2 -0
  16. package/dist/290.js.LICENSE.txt +5 -0
  17. package/dist/290.js.map +1 -0
  18. package/dist/300.js +1 -0
  19. package/dist/341.js +2 -0
  20. package/dist/341.js.LICENSE.txt +29 -0
  21. package/dist/341.js.map +1 -0
  22. package/dist/41.js +2 -0
  23. package/dist/41.js.LICENSE.txt +9 -0
  24. package/dist/41.js.map +1 -0
  25. package/dist/470.js +1 -0
  26. package/dist/470.js.map +1 -0
  27. package/dist/495.js +1 -0
  28. package/dist/495.js.map +1 -0
  29. package/dist/506.js +2 -0
  30. package/dist/506.js.LICENSE.txt +39 -0
  31. package/dist/506.js.map +1 -0
  32. package/dist/537.js +1 -0
  33. package/dist/537.js.map +1 -0
  34. package/dist/647.js +2 -0
  35. package/dist/647.js.LICENSE.txt +5 -0
  36. package/dist/647.js.map +1 -0
  37. package/dist/7.js +1 -0
  38. package/dist/7.js.map +1 -0
  39. package/dist/719.js +2 -0
  40. package/dist/719.js.LICENSE.txt +5 -0
  41. package/dist/719.js.map +1 -0
  42. package/dist/720.js +1 -0
  43. package/dist/720.js.map +1 -0
  44. package/dist/876.js +1 -0
  45. package/dist/876.js.map +1 -0
  46. package/dist/883.js +1 -0
  47. package/dist/883.js.map +1 -0
  48. package/dist/89.js +1 -0
  49. package/dist/89.js.map +1 -0
  50. package/dist/892.js +1 -0
  51. package/dist/892.js.map +1 -0
  52. package/dist/895.js +1 -0
  53. package/dist/895.js.map +1 -0
  54. package/dist/913.js +2 -0
  55. package/dist/913.js.LICENSE.txt +32 -0
  56. package/dist/913.js.map +1 -0
  57. package/dist/924.js +1 -0
  58. package/dist/924.js.map +1 -0
  59. package/dist/943.js +1 -0
  60. package/dist/943.js.map +1 -0
  61. package/dist/99.js +2 -0
  62. package/dist/99.js.LICENSE.txt +5 -0
  63. package/dist/99.js.map +1 -0
  64. package/dist/kenyaemr-esm-procedure-orders-app.js +1 -0
  65. package/dist/kenyaemr-esm-procedure-orders-app.js.buildmanifest.json +786 -0
  66. package/dist/kenyaemr-esm-procedure-orders-app.js.map +1 -0
  67. package/dist/main.js +2 -0
  68. package/dist/main.js.LICENSE.txt +35 -0
  69. package/dist/main.js.map +1 -0
  70. package/dist/routes.json +1 -0
  71. package/jest.config.js +8 -0
  72. package/package.json +55 -0
  73. package/src/completed-list/completed-list.component.tsx +40 -0
  74. package/src/completed-list/completed-list.resource.ts +0 -0
  75. package/src/completed-list/completed-list.scss +223 -0
  76. package/src/components/create-dashboard-link.component.tsx +35 -0
  77. package/src/components/overlay/hook.ts +47 -0
  78. package/src/components/overlay/overlay.component.tsx +42 -0
  79. package/src/components/overlay/overlay.scss +92 -0
  80. package/src/config-schema.ts +78 -0
  81. package/src/constants.ts +5 -0
  82. package/src/declarations.d.ts +6 -0
  83. package/src/empty-state/empty-state-component.tsx +21 -0
  84. package/src/empty-state/empty-state.scss +23 -0
  85. package/src/form/post-procedures/post-procedure-form.component.tsx +468 -0
  86. package/src/form/post-procedures/post-procedure-form.scss +189 -0
  87. package/src/form/post-procedures/post-procedure.resource.tsx +71 -0
  88. package/src/form/procedures-orders/add-procedures-order/add-procedures-order.scss +44 -0
  89. package/src/form/procedures-orders/add-procedures-order/add-procedures-order.workspace.tsx +93 -0
  90. package/src/form/procedures-orders/add-procedures-order/procedures-order-form.component.tsx +476 -0
  91. package/src/form/procedures-orders/add-procedures-order/procedures-order-form.scss +80 -0
  92. package/src/form/procedures-orders/add-procedures-order/procedures-order.ts +17 -0
  93. package/src/form/procedures-orders/add-procedures-order/procedures-type-search.scss +115 -0
  94. package/src/form/procedures-orders/add-procedures-order/procedures-type-search.tsx +236 -0
  95. package/src/form/procedures-orders/add-procedures-order/useProceduresTypes.ts +93 -0
  96. package/src/form/procedures-orders/api.ts +282 -0
  97. package/src/form/procedures-orders/order-config.ts +48 -0
  98. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-icon.component.tsx +39 -0
  99. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-item-tile.component.tsx +100 -0
  100. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-item-tile.scss +72 -0
  101. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-panel.extension.tsx +190 -0
  102. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-panel.scss +74 -0
  103. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket.scss +55 -0
  104. package/src/header/procedure-header.component.tsx +32 -0
  105. package/src/header/procedure-header.scss +70 -0
  106. package/src/header/procedure-illustration.component.tsx +52 -0
  107. package/src/hooks/useOrdersWorklist.ts +70 -0
  108. package/src/hooks/useSearchGroupedResults.ts +22 -0
  109. package/src/hooks/useSearchResults.ts +39 -0
  110. package/src/index.ts +59 -0
  111. package/src/left-panel-link.tsx +40 -0
  112. package/src/not-done-list/not-done-list.component.tsx +44 -0
  113. package/src/not-done-list/not-done.scss +207 -0
  114. package/src/patient-chart/patient-procedure-order-results-table.resource.ts +43 -0
  115. package/src/patient-chart/patient-procedure-order-results.component.tsx +12 -0
  116. package/src/patient-chart/patient-procedure-order-results.resource.ts +485 -0
  117. package/src/patient-chart/patient-procedure-results.component.tsx +30 -0
  118. package/src/patient-chart/procedure-active-order/procedure-active-order-results.component.tsx +390 -0
  119. package/src/patient-chart/procedure-active-order/procedure-active-order-results.scss +78 -0
  120. package/src/patient-chart/procedure-order-referals/procedure-order-referals.component.tsx +394 -0
  121. package/src/patient-chart/procedure-order-referals/procedure-order-referals.resource.tsx +0 -0
  122. package/src/patient-chart/procedure-order-referals/procedure-order-referals.scss +78 -0
  123. package/src/patient-chart/procedure-past-test/laboratory-past-test-order-results.component.tsx +366 -0
  124. package/src/patient-chart/procedure-past-test/laboratory-past-test-order-results.scss +74 -0
  125. package/src/patient-chart/procedure-tabs/laboratory-order-tabs.component.tsx +44 -0
  126. package/src/patient-chart/procedure-tabs/laboratory-order-tabs.scss +7 -0
  127. package/src/patient-chart/procedure-workspaces/laboratory-referral.workspace.component.tsx +11 -0
  128. package/src/patient-chart/procedure-workspaces/laboratory-referral.workspace.scss +0 -0
  129. package/src/patient-chart/results-summary/print-results-summary.component.tsx +152 -0
  130. package/src/patient-chart/results-summary/print-results-summary.scss +80 -0
  131. package/src/patient-chart/results-summary/print-results-table.component.tsx +134 -0
  132. package/src/patient-chart/results-summary/results-summary.resource.tsx +174 -0
  133. package/src/patient-chart/results-summary/results-summary.scss +158 -0
  134. package/src/patient-chart/results-summary/send-email-dialog.component.tsx +59 -0
  135. package/src/patient-chart/results-summary/test-children-results.component.tsx +177 -0
  136. package/src/patient-chart/results-summary/test-print-results-table.component.tsx +105 -0
  137. package/src/patient-chart/results-summary/test-results-table.component.tsx +103 -0
  138. package/src/print/print-procedure-results.component.tsx +49 -0
  139. package/src/print/print-procedure.component.tsx +105 -0
  140. package/src/print/print-procedure.scss +98 -0
  141. package/src/procedure-tabs/completed-tab.component.tsx +12 -0
  142. package/src/procedure-tabs/not-done-tab.component.tsx +12 -0
  143. package/src/procedure-tabs/referred-tab.component.tsx +12 -0
  144. package/src/procedure-tabs/work-list-tab.component.tsx +13 -0
  145. package/src/procedure.component.tsx +24 -0
  146. package/src/procedures-ordered/_pick-procedure-request-menu.component.tsx +33 -0
  147. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.component.tsx +105 -0
  148. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.resource.ts +106 -0
  149. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.scss +38 -0
  150. package/src/procedures-ordered/pick-procedure-request-menu.component.tsx +32 -0
  151. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.component.tsx +300 -0
  152. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.resource.ts +153 -0
  153. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.scss +38 -0
  154. package/src/procedures-ordered/procedure-instructions/instructions.scss +24 -0
  155. package/src/procedures-ordered/procedure-instructions/procedure-instructions-menu.component.tsx +32 -0
  156. package/src/procedures-ordered/procedure-instructions/procedure-instructions.component.tsx +78 -0
  157. package/src/procedures-ordered/procedure-instructions/procedure-instructions.scss +24 -0
  158. package/src/procedures-ordered/procedure-queue.scss +211 -0
  159. package/src/procedures-ordered/procedure-tabs.component.tsx +104 -0
  160. package/src/procedures-ordered/procedure-tests/procedure-tests.component.tsx +83 -0
  161. package/src/procedures-ordered/procedure-tests/procedure-tests.resource.ts +14 -0
  162. package/src/procedures-ordered/procedure-tests/procedure-tests.scss +12 -0
  163. package/src/procedures-ordered/procedures-ordered-list.component.tsx +38 -0
  164. package/src/procedures-ordered/reject-order-dialog/reject-order-dialog.scss +14 -0
  165. package/src/procedures-ordered/reject-order-dialog/reject-procedure-order-dialog.component.tsx +98 -0
  166. package/src/procedures-ordered/reject-reason/procedure-reject-reason-menu.component.tsx +32 -0
  167. package/src/procedures-ordered/reject-reason/procedure-reject-reason.component.tsx +40 -0
  168. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.component.tsx +42 -0
  169. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.scss +14 -0
  170. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.test.tsx +67 -0
  171. package/src/referred-procedures/referred-procedures.component.tsx +37 -0
  172. package/src/results/result-form-field.component.tsx +141 -0
  173. package/src/results/result-form.component.tsx +120 -0
  174. package/src/results/result-form.resource.ts +361 -0
  175. package/src/results/result-form.scss +22 -0
  176. package/src/root.component.tsx +16 -0
  177. package/src/routes.json +152 -0
  178. package/src/setup-tests.ts +7 -0
  179. package/src/shared/ui/common/action-button/action-button.component.tsx +68 -0
  180. package/src/shared/ui/common/action-button/action-button.scss +12 -0
  181. package/src/shared/ui/common/action-button/order-action-extension.component.tsx +21 -0
  182. package/src/shared/ui/common/grouped-orders-table.component.tsx +176 -0
  183. package/src/shared/ui/common/grouped-orders-table.scss +30 -0
  184. package/src/shared/ui/common/grouped-procedure-types.ts +47 -0
  185. package/src/shared/ui/common/list-order-details.component.tsx +171 -0
  186. package/src/shared/ui/common/list-order-details.resource.ts +41 -0
  187. package/src/shared/ui/common/list-order-details.scss +118 -0
  188. package/src/shared/ui/common/orders-date-range-picker.scss +15 -0
  189. package/src/shared/ui/common/orders-date-range-picker.tsx +38 -0
  190. package/src/summary-tiles/procedure-summary-tiles.component.tsx +36 -0
  191. package/src/summary-tiles/procedure-summary-tiles.scss +11 -0
  192. package/src/summary-tiles/procedure-summary.resource.tsx +79 -0
  193. package/src/summary-tiles/summary-tile.component.tsx +41 -0
  194. package/src/summary-tiles/summary-tile.scss +53 -0
  195. package/src/types/index.ts +661 -0
  196. package/src/types/patient-queue.ts +77 -0
  197. package/src/ui-components/overflow-menu.component.tsx +74 -0
  198. package/src/ui-components/overflow-menu.scss +39 -0
  199. package/src/utils/functions.ts +236 -0
  200. package/src/utils/orders-table/orders-data-table.component.tsx +129 -0
  201. package/src/utils/orders-table/orders-data-table.scss +50 -0
  202. package/src/work-list/work-list.component.tsx +38 -0
  203. package/src/work-list/work-list.resource.ts +26 -0
  204. package/src/work-list/work-list.scss +207 -0
  205. package/translations/en.json +141 -0
  206. package/tsconfig.json +5 -0
  207. package/webpack.config.js +1 -0
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button } from '@carbon/react';
4
+ import { showModal, launchWorkspace } from '@openmrs/esm-framework';
5
+ import { type Order } from '@openmrs/esm-patient-common-lib';
6
+ import OrderActionExtension from './order-action-extension.component';
7
+ import { type Result } from '../../../../types';
8
+ import { launchOverlay } from '../../../../components/overlay/hook';
9
+ import PostProcedureForm from '../../../../form/post-procedures/post-procedure-form.component';
10
+ import styles from './action-button.scss';
11
+
12
+ type ActionButtonProps = {
13
+ action: {
14
+ actionName: string;
15
+ };
16
+ order: Result;
17
+ patientUuid: string;
18
+ };
19
+
20
+ const ActionButton: React.FC<ActionButtonProps> = ({ action, order, patientUuid }) => {
21
+ const { t } = useTranslation();
22
+
23
+ const handleOpenProcedureResultForm = () => {
24
+ launchWorkspace('procedure-report-form', {
25
+ patientUuid,
26
+ order,
27
+ });
28
+ };
29
+ switch (action.actionName) {
30
+ case 'add-procedure-to-worklist-dialog':
31
+ return <OrderActionExtension order={order as unknown as Order} />;
32
+
33
+ case 'postProcedureResultForm':
34
+ return (
35
+ <Button kind="primary" onClick={handleOpenProcedureResultForm} size="md" className={styles.actionButtons}>
36
+ {t('procedureResultForm', 'Procedure Result Form')}
37
+ </Button>
38
+ );
39
+
40
+ case 'reject-procedure-order-dialog':
41
+ return (
42
+ <Button
43
+ kind={action.actionName === 'reject-procedure-order-dialog' ? 'danger' : 'tertiary'}
44
+ onClick={() => {
45
+ const dispose = showModal(action.actionName, {
46
+ closeModal: () => dispose(),
47
+ order: order,
48
+ });
49
+ }}
50
+ size="md"
51
+ className={styles.actionButtons}>
52
+ {t(
53
+ action.actionName.replace(/-/g, ''),
54
+ action.actionName
55
+ .split('-')
56
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
57
+ .join(' ')
58
+ .replace('Modal', ''),
59
+ )}
60
+ </Button>
61
+ );
62
+
63
+ default:
64
+ return null;
65
+ }
66
+ };
67
+
68
+ export default ActionButton;
@@ -0,0 +1,12 @@
1
+ @use '@carbon/type';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/colors';
4
+
5
+ .actionButtons {
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ text-align: center;
10
+ padding: 0 layout.$spacing-04;
11
+ font-size: type.type-scale(2);
12
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { type Order } from '@openmrs/esm-patient-common-lib';
3
+ import { ExtensionSlot, restBaseUrl } from '@openmrs/esm-framework';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ type OrderActionProps = {
7
+ order: Order;
8
+ };
9
+
10
+ const OrderActionExtension: React.FC<OrderActionProps> = ({ order }) => {
11
+ const { t } = useTranslation();
12
+ const state = {
13
+ order: order,
14
+ modalName: 'add-procedure-to-worklist-dialog',
15
+ actionText: t('pickProcedureOrder', 'Pick Procedure Order'),
16
+ additionalProps: { mutateUrl: `${restBaseUrl}/order` },
17
+ };
18
+ return <ExtensionSlot name="procedure-orders-action" state={state} />;
19
+ };
20
+
21
+ export default OrderActionExtension;
@@ -0,0 +1,176 @@
1
+ import {
2
+ DataTable,
3
+ Search,
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableContainer,
8
+ TableExpandedRow,
9
+ TableExpandHeader,
10
+ TableExpandRow,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ } from '@carbon/react';
15
+ import { usePagination } from '@openmrs/esm-framework';
16
+ import { CardHeader } from '@openmrs/esm-patient-common-lib';
17
+ import upperCase from 'lodash-es/upperCase';
18
+ import { default as React, useMemo, useState } from 'react';
19
+ import { useTranslation } from 'react-i18next';
20
+ import EmptyState from '../../../empty-state/empty-state-component';
21
+ import { useSearchGroupedResults } from '../../../hooks/useSearchGroupedResults';
22
+ import TransitionLatestQueueEntryButton from '../../../procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.component';
23
+ import styles from './grouped-orders-table.scss';
24
+ import { type GroupedOrdersTableProps } from './grouped-procedure-types';
25
+ import ListOrderDetails from './list-order-details.component';
26
+ import { OrdersDateRangePicker } from './orders-date-range-picker';
27
+
28
+ const GroupedOrdersTable: React.FC<GroupedOrdersTableProps> = (props) => {
29
+ const workListEntries = props.orders;
30
+ const { t } = useTranslation();
31
+ const [currentPageSize] = useState<number>(10);
32
+ const [searchString, setSearchString] = useState<string>('');
33
+
34
+ function groupOrdersById(orders) {
35
+ if (orders && orders.length > 0) {
36
+ const groupedOrders = orders.reduce((acc, item) => {
37
+ if (!acc[item.patient.uuid]) {
38
+ acc[item.patient.uuid] = [];
39
+ }
40
+ acc[item.patient.uuid].push(item);
41
+ return acc;
42
+ }, {});
43
+
44
+ // Convert the result to an array of objects with patientId and orders
45
+ return Object.keys(groupedOrders).map((patientId) => ({
46
+ patientId: patientId,
47
+ orders: groupedOrders[patientId],
48
+ }));
49
+ } else {
50
+ return [];
51
+ }
52
+ }
53
+ const groupedOrdersByPatient = groupOrdersById(workListEntries);
54
+ const searchResults = useSearchGroupedResults(groupedOrdersByPatient, searchString);
55
+ const { goTo, results: paginatedResults, currentPage } = usePagination(searchResults, currentPageSize);
56
+
57
+ const rowData = useMemo(() => {
58
+ return paginatedResults.map((patient) => ({
59
+ id: patient.patientId,
60
+ patientName: upperCase(patient.orders[0].patient?.person?.display),
61
+ patientAge: patient?.orders[0]?.patient?.person?.age,
62
+ patientGender:
63
+ patient?.orders[0]?.patient?.person?.gender === 'M'
64
+ ? t('male', 'Male')
65
+ : patient?.orders[0]?.patient?.person?.gender === 'F'
66
+ ? t('female', 'Female')
67
+ : patient?.orders[0]?.patient?.person?.gender,
68
+ orders: patient.orders,
69
+ totalOrders: patient.orders?.length,
70
+ fulfillerStatus: patient.orders[0].fulfillerStatus,
71
+ action:
72
+ patient.orders[0].fulfillerStatus === 'COMPLETED' ? (
73
+ <TransitionLatestQueueEntryButton patientUuid={patient.patientId} />
74
+ ) : null,
75
+ }));
76
+ }, [paginatedResults, t]);
77
+
78
+ const tableColumns = useMemo(() => {
79
+ const baseColumns = [
80
+ { key: 'patientName', header: t('patientName', 'Patient Name') },
81
+ { key: 'patientAge', header: t('age', 'Age') },
82
+ { key: 'patientGender', header: t('sex', 'Sex') },
83
+ { key: 'totalOrders', header: t('totalOrders', 'Total Orders') },
84
+ ];
85
+
86
+ const showActionColumn = workListEntries.some((order) => order.fulfillerStatus === 'COMPLETED');
87
+
88
+ return showActionColumn ? [...baseColumns, { key: 'action', header: t('action', 'Action') }] : baseColumns;
89
+ }, [workListEntries, t]);
90
+
91
+ return (
92
+ <>
93
+ <div className={styles.widgetCard}>
94
+ <CardHeader title={props?.title}>
95
+ <div className={styles.elementContainer}>
96
+ <OrdersDateRangePicker />
97
+ <Search
98
+ expanded
99
+ persistent={true}
100
+ onChange={(event) => setSearchString(event.target.value)}
101
+ placeholder={t('searchByPatientName', 'Search by patient name')}
102
+ size="md"
103
+ />
104
+ </div>
105
+ </CardHeader>
106
+ </div>
107
+
108
+ <DataTable size="md" useZebraStyle rows={rowData} headers={tableColumns}>
109
+ {({
110
+ rows,
111
+ headers,
112
+ getHeaderProps,
113
+ getRowProps,
114
+ getExpandedRowProps,
115
+ getTableProps,
116
+ getTableContainerProps,
117
+ }) => (
118
+ <TableContainer className={styles.dataTable} {...getTableContainerProps()}>
119
+ {rows.length <= 0 && (
120
+ <EmptyState subTitle={t('NoOrdersFound', 'There are no orders to display for this patient')} />
121
+ )}
122
+ {rows.length > 0 && (
123
+ <Table {...getTableProps()} aria-label="sample table">
124
+ <TableHead>
125
+ <TableRow>
126
+ <TableExpandHeader aria-label="expand row" />
127
+ {headers.map((header, i) => (
128
+ <TableHeader
129
+ key={i}
130
+ {...getHeaderProps({
131
+ header,
132
+ })}>
133
+ {header.header}
134
+ </TableHeader>
135
+ ))}
136
+ </TableRow>
137
+ </TableHead>
138
+ <TableBody>
139
+ {rows.map((row) => (
140
+ <React.Fragment key={row.id}>
141
+ <TableExpandRow
142
+ {...getRowProps({
143
+ row,
144
+ })}>
145
+ {row.cells.map((cell) => (
146
+ <TableCell key={cell.id}>{cell.value}</TableCell>
147
+ ))}
148
+ </TableExpandRow>
149
+ <TableExpandedRow
150
+ colSpan={headers.length + 1}
151
+ className="demo-expanded-td"
152
+ {...getExpandedRowProps({
153
+ row,
154
+ })}>
155
+ <ListOrderDetails
156
+ actions={props.actions}
157
+ groupedOrders={groupedOrdersByPatient.find((item) => item.patientId === row.id)}
158
+ showActions={props.showActions}
159
+ showOrderType={props.showOrderType}
160
+ showStartButton={props.showStartButton}
161
+ showStatus={props.showStatus}
162
+ />
163
+ </TableExpandedRow>
164
+ </React.Fragment>
165
+ ))}
166
+ </TableBody>
167
+ </Table>
168
+ )}
169
+ </TableContainer>
170
+ )}
171
+ </DataTable>
172
+ </>
173
+ );
174
+ };
175
+
176
+ export default GroupedOrdersTable;
@@ -0,0 +1,30 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/colors';
3
+
4
+ .dataTable {
5
+ display: flex;
6
+ flex-direction: column;
7
+ border: 1px solid colors.$gray-20;
8
+ margin-right: layout.$spacing-05;
9
+ margin-left: layout.$spacing-05;
10
+
11
+ & > section {
12
+ position: relative;
13
+ margin-bottom: layout.$spacing-01;
14
+ }
15
+ }
16
+
17
+ .widgetCard {
18
+ background-color: colors.$white;
19
+ border: 1px solid colors.$gray-20;
20
+ margin-right: layout.$spacing-05;
21
+ margin-left: layout.$spacing-05;
22
+ margin-top: layout.$spacing-05;
23
+ }
24
+
25
+ .elementContainer {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 1rem;
29
+ margin-right: layout.$spacing-03;
30
+ }
@@ -0,0 +1,47 @@
1
+ import { type Result } from '../../../types';
2
+
3
+ export type FulfillerStatus = '' | 'IN_PROGRESS' | 'DECLINED' | 'COMPLETED' | 'EXCEPTION';
4
+
5
+ export type WorkListProps = {
6
+ fulfillerStatus: FulfillerStatus;
7
+ };
8
+
9
+ export interface ResultsOrderProps {
10
+ order: Result;
11
+ patientUuid: string;
12
+ }
13
+
14
+ export interface RejectOrderProps {
15
+ order: Result;
16
+ }
17
+
18
+ export interface InstructionsProps {
19
+ order: Result;
20
+ }
21
+
22
+ export interface GroupedOrders {
23
+ patientId: string;
24
+ orders: Array<Result>;
25
+ }
26
+ export interface GroupedOrdersTableProps {
27
+ orders: Array<Result>;
28
+ showStatus: boolean;
29
+ showStartButton: boolean;
30
+ showActions: boolean;
31
+ showOrderType: boolean;
32
+ actions: Array<OrderAction>;
33
+ title: string;
34
+ }
35
+
36
+ export interface ListOrdersDetailsProps {
37
+ groupedOrders: GroupedOrders;
38
+ showStatus: boolean;
39
+ showStartButton: boolean;
40
+ showActions: boolean;
41
+ showOrderType: boolean;
42
+ actions: Array<OrderAction>;
43
+ }
44
+
45
+ export interface OrderAction {
46
+ actionName: string;
47
+ }
@@ -0,0 +1,171 @@
1
+ import {
2
+ Accordion,
3
+ AccordionItem,
4
+ Button,
5
+ InlineLoading,
6
+ StructuredListBody,
7
+ StructuredListCell,
8
+ StructuredListRow,
9
+ StructuredListWrapper,
10
+ Tag,
11
+ TextArea,
12
+ } from '@carbon/react';
13
+ import { Printer } from '@carbon/react/icons';
14
+ import { formatDate, parseDate, showModal } from '@openmrs/esm-framework';
15
+ import capitalize from 'lodash-es/capitalize';
16
+ import React, { useMemo } from 'react';
17
+ import { useTranslation } from 'react-i18next';
18
+ import ActionButton from './action-button/action-button.component';
19
+ import { type ListOrdersDetailsProps } from './grouped-procedure-types';
20
+ import usePatientDiagnosis from './list-order-details.resource';
21
+ import styles from './list-order-details.scss';
22
+
23
+ const ListOrderDetails: React.FC<ListOrdersDetailsProps> = ({ groupedOrders, showActions, actions }) => {
24
+ const orders = groupedOrders?.orders;
25
+ const { t } = useTranslation();
26
+ const orderRows = useMemo(() => {
27
+ return orders
28
+ ?.filter((item) => item.action === 'NEW')
29
+ .map((entry) => ({
30
+ ...entry,
31
+ id: entry.uuid,
32
+ orderNumber: entry.orderNumber,
33
+ procedure: entry.display,
34
+ status: entry.fulfillerStatus ? entry.fulfillerStatus : '--',
35
+ urgency: entry.urgency,
36
+ orderer: entry.orderer?.display,
37
+ instructions: entry.instructions ? entry.instructions : '--',
38
+ date: <span className={styles['single-line-display']}>{formatDate(parseDate(entry?.dateActivated))}</span>,
39
+ }));
40
+ }, [orders]);
41
+
42
+ const { diagnoses, isLoading } = usePatientDiagnosis(orders[0]?.patient?.uuid);
43
+ if (isLoading) {
44
+ return <InlineLoading status="active" description={t('loading', 'Loading...')} />;
45
+ }
46
+
47
+ return (
48
+ <div>
49
+ {orderRows.map((row) => (
50
+ <div key={row.uuid} className={styles.orderDetailsContainer}>
51
+ <div className={styles.orderHeader}>
52
+ <span className={styles.orderNumber}>
53
+ {t('orderNumbers', 'Order number:')} {row?.orderNumber}
54
+ </span>
55
+ <span className={styles.orderDate}>
56
+ {t('orderDate', 'Order Date:')} {row?.dateActivated ? formatDate(parseDate(row?.dateActivated)) : '--'}
57
+ </span>
58
+ </div>
59
+
60
+ <div className={styles.orderStatus}>
61
+ <span className={styles.urgencyStatus}>
62
+ {t('orderStatus', 'Status:')}
63
+ <Tag size="lg" type="warm-gray">
64
+ {row.fulfillerStatus || t('orderNotPicked', 'Order not picked')}
65
+ </Tag>
66
+ </span>
67
+ </div>
68
+
69
+ <div className={styles.orderHeader}>
70
+ <span className={styles.urgencyStatus}>
71
+ {t('diagnosis', 'Diagnosis: ')}
72
+ {diagnoses.length > 0 ? (
73
+ diagnoses.map((diagnosis) => (
74
+ <Tag size="lg" type="warm-gray" key={diagnosis.id}>
75
+ {diagnosis.text ? capitalize(diagnosis.text) : t('noDiagnosis', 'No available diagnosis')}
76
+ </Tag>
77
+ ))
78
+ ) : (
79
+ <Tag size="lg" type="warm-gray">
80
+ {t('noDiagnosis', 'No available diagnosis')}
81
+ </Tag>
82
+ )}
83
+ </span>
84
+ <span className={styles.urgencyStatus}>
85
+ {t('urgencyStatus', 'Urgency: ')}
86
+ <Tag size="lg" type="warm-gray">
87
+ {capitalize(row.urgency || '--')}
88
+ </Tag>
89
+ </span>
90
+ </div>
91
+
92
+ <StructuredListWrapper>
93
+ <StructuredListBody>
94
+ <StructuredListRow>
95
+ <StructuredListCell>{t('testOrdered', 'Test ordered: ')}</StructuredListCell>
96
+ <StructuredListCell className={styles.orderName}>{capitalize(row?.display || '--')}</StructuredListCell>
97
+ </StructuredListRow>
98
+ <StructuredListRow>
99
+ <StructuredListCell>
100
+ <span className={styles.instructionLabel}>{t('orderInStruction', 'Instructions: ')}</span>
101
+ </StructuredListCell>
102
+ <StructuredListCell>
103
+ {row?.instructions || (
104
+ <Tag size="lg" type="red">
105
+ {t('NoInstructionLeft', 'No instructions are provided.')}
106
+ </Tag>
107
+ )}
108
+ </StructuredListCell>
109
+ </StructuredListRow>
110
+ </StructuredListBody>
111
+ </StructuredListWrapper>
112
+ {row.status === 'COMPLETED' && row.procedures?.[0]?.procedureReport && (
113
+ <Accordion>
114
+ <AccordionItem title={<span className={styles.accordionTitle}>{t('viewReport', 'View Report')}</span>}>
115
+ <TextArea
116
+ className={styles.textAreaInput}
117
+ labelText={t('complication', 'Complication')}
118
+ id={`report-${row.uuid}`}
119
+ name={`report-${row.uuid}`}
120
+ value={row.procedures[0]?.encounters[0]?.obs[0]?.display}
121
+ readOnly
122
+ />
123
+ <TextArea
124
+ className={styles.textAreaInput}
125
+ labelText={t('procedureReport', 'Procedure report')}
126
+ id={`report-${row.uuid}`}
127
+ name={`report-${row.uuid}`}
128
+ value={row.procedures[0]?.procedureReport}
129
+ readOnly
130
+ />
131
+ <Button
132
+ kind="tertiary"
133
+ className={styles.printBtn}
134
+ onClick={() => {
135
+ const dispose = showModal('print-procedure-report-modal', {
136
+ onClose: () => dispose(),
137
+ completedOrder: row,
138
+ });
139
+ }}
140
+ size="sm"
141
+ renderIcon={() => <Printer size={18} />}>
142
+ {t('printReport', 'Print report')}
143
+ </Button>
144
+ </AccordionItem>
145
+ </Accordion>
146
+ )}
147
+
148
+ <div className={styles.nameOrder}>
149
+ {t('ordererName', 'Orderer Name: ')} {capitalize(row?.orderer || '--')}
150
+ </div>
151
+ {showActions && (
152
+ <div className={styles.buttonSection}>
153
+ <div className={styles.actionBtns}>
154
+ {actions.map((action) => (
155
+ <ActionButton
156
+ key={action.actionName}
157
+ order={orders.find((order) => order.uuid === row.id)}
158
+ patientUuid={row.patient.uuid}
159
+ action={action}
160
+ />
161
+ ))}
162
+ </div>
163
+ </div>
164
+ )}
165
+ </div>
166
+ ))}
167
+ </div>
168
+ );
169
+ };
170
+
171
+ export default ListOrderDetails;
@@ -0,0 +1,41 @@
1
+ import { openmrsFetch, restBaseUrl, type Visit } from '@openmrs/esm-framework';
2
+ import { useMemo } from 'react';
3
+ import useSWR from 'swr';
4
+
5
+ export function useVisit(patientUuid: string) {
6
+ const customRepresentation =
7
+ 'custom:(uuid,patient,encounters:(uuid,diagnoses:(uuid,display,certainty,diagnosis:(coded:(uuid,display))),encounterDatetime,encounterType:(uuid,display),encounterProviders:(uuid,display,provider:(uuid,person:(uuid,display)))),location:(uuid,name,display),visitType:(uuid,name,display),startDatetime,stopDatetime)&limit=1';
8
+
9
+ const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<Visit> } }, Error>(
10
+ `${restBaseUrl}/visit?patient=${patientUuid}&v=${customRepresentation}`,
11
+ openmrsFetch,
12
+ );
13
+
14
+ return {
15
+ visits: data ? data?.data?.results[0] : null,
16
+ error,
17
+ isLoading,
18
+ isValidating,
19
+ mutateVisits: mutate,
20
+ };
21
+ }
22
+
23
+ const usePatientDiagnosis = (patientUUid: string) => {
24
+ const { visits: recentVisit, error, isLoading } = useVisit(patientUUid);
25
+
26
+ const diagnoses = useMemo(() => {
27
+ return (
28
+ recentVisit?.encounters?.flatMap(
29
+ (encounter) =>
30
+ encounter.diagnoses.map((diagnosis) => ({
31
+ id: diagnosis.diagnosis.coded.uuid,
32
+ text: diagnosis.display,
33
+ })) || [],
34
+ ) || []
35
+ );
36
+ }, [recentVisit]);
37
+
38
+ return { error, isLoading, diagnoses: diagnoses as Array<{ id: string; text: string }> };
39
+ };
40
+
41
+ export default usePatientDiagnosis;
@@ -0,0 +1,118 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .buttonSection {
6
+ display: flex;
7
+ justify-content: flex-end;
8
+ align-items: center;
9
+ flex-direction: row;
10
+ padding-top: layout.$spacing-03;
11
+ width: 100%;
12
+ }
13
+ .actionBtns {
14
+ display: flex;
15
+ justify-content: flex-end;
16
+ align-items: center;
17
+ text-align: center;
18
+ flex-direction: row;
19
+ padding-top: layout.$spacing-03;
20
+ width: 100%;
21
+ }
22
+
23
+ .nameOrder {
24
+ margin-top: layout.$spacing-03;
25
+ font-size: type.type-scale(2);
26
+ margin-left: layout.$spacing-04;
27
+ }
28
+
29
+ .testOrder {
30
+ font-size: type.type-scale(2);
31
+ font-weight: bold;
32
+ }
33
+
34
+ .actionButtons {
35
+ display: flex;
36
+ justify-content: flex-end;
37
+ align-items: center;
38
+ width: 100%;
39
+ }
40
+
41
+ .orderDetailsContainer {
42
+ width: 80%;
43
+ display: flex;
44
+ flex-direction: column;
45
+ padding: layout.$spacing-05;
46
+ margin-left: layout.$spacing-13;
47
+ background-color: colors.$white-0;
48
+ margin: layout.$spacing-05 0;
49
+ }
50
+
51
+ .orderUrgency {
52
+ display: flex;
53
+ justify-content: flex-end;
54
+ font-size: type.type-scale(2);
55
+ margin-bottom: layout.$spacing-03;
56
+ }
57
+ .printBtn {
58
+ --cds-layout-size-height-context: var(--cds-layout-size-height-sm, 2rem);
59
+ --cds-layout-size-height: var(--cds-layout-size-height-context);
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ text-align: justify;
64
+ padding: 0 layout.$spacing-04;
65
+ gap: layout.$spacing-05;
66
+ margin-right: layout.$spacing-05;
67
+ }
68
+ .orderHeader {
69
+ display: flex;
70
+ justify-content: space-between;
71
+ width: 100%;
72
+ padding: 0;
73
+ margin-bottom: layout.$spacing-03;
74
+ }
75
+
76
+ .orderNumber {
77
+ font-size: type.type-scale(2);
78
+ }
79
+
80
+ .orderStatus {
81
+ font-size: type.type-scale(2);
82
+ }
83
+
84
+ .orderDate {
85
+ text-align: right;
86
+ font-size: type.type-scale(2);
87
+ }
88
+
89
+ .instructions {
90
+ text-align: justify;
91
+ font-size: type.type-scale(1);
92
+ }
93
+
94
+ .urgencyStatus {
95
+ font-size: type.type-scale(2);
96
+ margin-top: layout.$spacing-03;
97
+ }
98
+
99
+ .instructionLabel {
100
+ font-weight: bold;
101
+ font-size: type.type-scale(2);
102
+ }
103
+ .orderName {
104
+ font-weight: bold;
105
+ font-size: type.type-scale(2);
106
+ }
107
+
108
+ .labResultSlot {
109
+ text-align: center;
110
+ margin-bottom: layout.$spacing-03;
111
+ }
112
+
113
+ .accordionTitle {
114
+ font-weight: bold;
115
+ }
116
+ .printBtn {
117
+ margin-top: layout.$spacing-03;
118
+ }