@ampath/esm-dha-workflow-app 4.0.0-next.4 → 4.0.0-next.41

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 (167) hide show
  1. package/dist/104.js +2 -0
  2. package/dist/104.js.LICENSE.txt +9 -0
  3. package/dist/104.js.map +1 -0
  4. package/dist/15.js +1 -0
  5. package/dist/15.js.map +1 -0
  6. package/dist/246.js +2 -0
  7. package/dist/246.js.LICENSE.txt +54 -0
  8. package/dist/246.js.map +1 -0
  9. package/dist/327.js +1 -0
  10. package/dist/327.js.map +1 -0
  11. package/dist/339.js +1 -0
  12. package/dist/339.js.map +1 -0
  13. package/dist/709.js +1 -0
  14. package/dist/709.js.map +1 -0
  15. package/dist/710.js +2 -0
  16. package/dist/710.js.map +1 -0
  17. package/dist/729.js +1 -0
  18. package/dist/729.js.map +1 -0
  19. package/dist/752.js +1 -0
  20. package/dist/752.js.map +1 -0
  21. package/dist/833.js +1 -0
  22. package/dist/833.js.map +1 -0
  23. package/dist/91.js +1 -1
  24. package/dist/91.js.map +1 -1
  25. package/dist/93.js +1 -0
  26. package/dist/93.js.map +1 -0
  27. package/dist/938.js +1 -0
  28. package/dist/938.js.map +1 -0
  29. package/dist/esm-dha-workflow-app.js +1 -0
  30. package/dist/{openmrs-esm-home-app.js.buildmanifest.json → esm-dha-workflow-app.js.buildmanifest.json} +294 -55
  31. package/dist/{openmrs-esm-home-app.js.map → esm-dha-workflow-app.js.map} +1 -1
  32. package/dist/main.js +1 -1
  33. package/dist/main.js.map +1 -1
  34. package/dist/routes.json +1 -1
  35. package/package.json +3 -3
  36. package/src/accounting/accounting.component.tsx +13 -0
  37. package/src/appointments/appointments.component.tsx +13 -0
  38. package/src/bookings/daily/daily-bookings.component.scss +38 -0
  39. package/src/bookings/daily/daily-bookings.component.tsx +138 -0
  40. package/src/bookings/daily/daily-bookings.resource.ts +27 -0
  41. package/src/bookings/daily/filters/daily-bookings-filter.component.scss +15 -0
  42. package/src/bookings/daily/filters/daily-bookings-filter.component.tsx +80 -0
  43. package/src/bookings/daily/patient-list/daily-bookings-patient-list.component.tsx +97 -0
  44. package/src/bookings/types/index.ts +68 -0
  45. package/src/config-schema.ts +132 -32
  46. package/src/dashboard/dashboard.component.scss +7 -0
  47. package/src/dashboard/dashboard.component.tsx +63 -0
  48. package/src/dashboard/overview/overview.component.scss +70 -0
  49. package/src/dashboard/overview/overview.component.tsx +107 -0
  50. package/src/dashboard/patient-list/patient-list.component.tsx +41 -0
  51. package/src/hooks/useActions.ts +165 -0
  52. package/src/index.ts +23 -2
  53. package/src/laboratory/laboratory.component.tsx +13 -0
  54. package/src/left-panel/left-panel.component.tsx +20 -0
  55. package/src/left-panel/left-panel.scss +42 -0
  56. package/src/mch/queues/consultation/mch-consultation.tsx +18 -0
  57. package/src/mch/queues/triage/mch-triage.tsx +15 -0
  58. package/src/modals/sign-off-modal.scss +7 -0
  59. package/src/modals/sign-off-modal.tsx +52 -0
  60. package/src/mortuary/mortuary.component.tsx +13 -0
  61. package/src/pharmacy/pharmacy.component.tsx +13 -0
  62. package/src/registry/client-details/client-details.tsx +40 -0
  63. package/src/registry/modal/client-details-modal/client-details-modal.scss +28 -0
  64. package/src/registry/modal/client-details-modal/client-details-modal.tsx +81 -0
  65. package/src/registry/modal/otp-verification-modal/otp-verification-modal.scss +31 -0
  66. package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +186 -0
  67. package/src/registry/modal/send-to-triage/send-to-triage.modal.scss +34 -0
  68. package/src/registry/modal/send-to-triage/send-to-triage.modal.tsx +302 -0
  69. package/src/registry/payment-details/payment-options/payment-options.tsx +21 -0
  70. package/src/registry/registry.component.scss +83 -0
  71. package/src/registry/registry.component.tsx +397 -2
  72. package/src/registry/registry.resource.ts +60 -0
  73. package/src/registry/types/index.ts +309 -0
  74. package/src/registry/utils/error-handler.ts +37 -0
  75. package/src/registry/utils/format-dependant-display-data.ts +8 -0
  76. package/src/registry/utils/hie-adapter.ts +56 -0
  77. package/src/registry/utils/hie-client-adapter.ts +309 -0
  78. package/src/registry/utils/mask-data.ts +21 -0
  79. package/src/resources/hie-amrs-automatic-registration.service.ts +16 -0
  80. package/src/resources/identifier-types.ts +27 -0
  81. package/src/resources/patient-resource.ts +62 -0
  82. package/src/resources/patient-search.resource.ts +22 -0
  83. package/src/resources/queue.resource.ts +60 -0
  84. package/src/resources/visit.resource.ts +38 -0
  85. package/src/root.component.tsx +42 -30
  86. package/src/root.scss +5 -9
  87. package/src/routes.json +43 -4
  88. package/src/service-queues/action-button.component.tsx +34 -0
  89. package/src/service-queues/action-overflow-menu-item.component.tsx +34 -0
  90. package/src/service-queues/consultation/consultation.component.scss +7 -0
  91. package/src/service-queues/consultation/consultation.component.tsx +15 -0
  92. package/src/service-queues/metrics/metrics-cards/attended-patients.extension.tsx +38 -0
  93. package/src/service-queues/metrics/metrics-cards/metrics-card.component.tsx +86 -0
  94. package/src/service-queues/metrics/metrics-cards/metrics-card.scss +106 -0
  95. package/src/service-queues/metrics/metrics-cards/waiting-patients.extension.tsx +34 -0
  96. package/src/service-queues/metrics/metrics-container.component.tsx +23 -0
  97. package/src/service-queues/metrics/metrics-container.scss +36 -0
  98. package/src/service-queues/metrics/metrics.resource.ts +65 -0
  99. package/src/service-queues/modals/move/move-patient.component.scss +35 -0
  100. package/src/service-queues/modals/move/move-patient.component.tsx +138 -0
  101. package/src/service-queues/modals/serve/serve-patient.comppnent.scss +0 -0
  102. package/src/service-queues/modals/serve/serve-patient.comppnent.tsx +80 -0
  103. package/src/service-queues/modals/sign-off/sign-off.modal.scss +0 -0
  104. package/src/service-queues/modals/sign-off/sign-off.modal.tsx +79 -0
  105. package/src/service-queues/modals/transition/transition-patient.component.scss +0 -0
  106. package/src/service-queues/modals/transition/transition-patient.component.tsx +122 -0
  107. package/src/service-queues/queue-list/queue-list.component.scss +19 -0
  108. package/src/service-queues/queue-list/queue-list.component.tsx +169 -0
  109. package/src/service-queues/queue-room.component.tsx +39 -0
  110. package/src/service-queues/room/room.component.tsx +58 -0
  111. package/src/service-queues/service-queue/service-queue.component.scss +14 -0
  112. package/src/service-queues/service-queue/service-queue.component.tsx +245 -0
  113. package/src/service-queues/service-queue/stats/stat-card/stat-card.component.scss +10 -0
  114. package/src/service-queues/service-queue/stats/stat-card/stat-card.component.tsx +23 -0
  115. package/src/service-queues/service-queue/stats/stat-details/stat-details.component.scss +7 -0
  116. package/src/service-queues/service-queue/stats/stat-details/stat-details.component.tsx +34 -0
  117. package/src/service-queues/service-queue.scss +27 -0
  118. package/src/service-queues/service-queue.tsx +31 -0
  119. package/src/service-queues/service-queues.resource.ts +177 -0
  120. package/src/service-queues/service.resource.ts +28 -0
  121. package/src/shared/constants/civil-status.ts +29 -0
  122. package/src/shared/constants/concepts.ts +30 -0
  123. package/src/shared/constants/index.ts +1 -0
  124. package/src/shared/constants/person-attributes.ts +33 -0
  125. package/src/shared/services/location.resource.ts +9 -0
  126. package/src/shared/ui/otp-input/otp-input.component.scss +14 -0
  127. package/src/shared/ui/otp-input/otp-input.component.tsx +90 -0
  128. package/src/shared/ui/timer/timer.component.scss +5 -0
  129. package/src/shared/ui/timer/timer.component.tsx +40 -0
  130. package/src/shared/utils/get-base-url.ts +17 -0
  131. package/src/side-nav-menu/nav-link-config.ts +82 -0
  132. package/src/side-nav-menu/nav-links.tsx +31 -11
  133. package/src/triage/metrics/attended-patients.extension.tsx +42 -0
  134. package/src/triage/metrics/metrics.scss +36 -0
  135. package/src/triage/metrics/triage-metrics.component.tsx +21 -0
  136. package/src/triage/metrics/waiting-patients.extension.tsx +39 -0
  137. package/src/triage/room/room.scss +29 -0
  138. package/src/triage/triage.component.tsx +15 -0
  139. package/src/triage/triage.resource.ts +19 -0
  140. package/src/triage/types.ts +16 -0
  141. package/src/types/types.ts +128 -0
  142. package/dist/561.js +0 -2
  143. package/dist/561.js.map +0 -1
  144. package/dist/70.js +0 -1
  145. package/dist/70.js.map +0 -1
  146. package/dist/731.js +0 -2
  147. package/dist/731.js.LICENSE.txt +0 -39
  148. package/dist/731.js.map +0 -1
  149. package/dist/819.js +0 -1
  150. package/dist/819.js.map +0 -1
  151. package/dist/openmrs-esm-home-app.js +0 -1
  152. package/src/boxes/extensions/blue-box.component.tsx +0 -15
  153. package/src/boxes/extensions/box.scss +0 -23
  154. package/src/boxes/extensions/brand-box.component.tsx +0 -15
  155. package/src/boxes/extensions/red-box.component.tsx +0 -15
  156. package/src/boxes/slot/boxes.component.tsx +0 -25
  157. package/src/boxes/slot/boxes.scss +0 -29
  158. package/src/greeter/greeter.component.tsx +0 -42
  159. package/src/greeter/greeter.scss +0 -20
  160. package/src/greeter/greeter.test.tsx +0 -28
  161. package/src/patient-getter/patient-getter.component.tsx +0 -40
  162. package/src/patient-getter/patient-getter.resource.ts +0 -39
  163. package/src/patient-getter/patient-getter.scss +0 -16
  164. package/src/patient-getter/patient-getter.test.tsx +0 -40
  165. package/src/resources/resources.component.tsx +0 -56
  166. package/src/resources/resources.scss +0 -68
  167. /package/dist/{561.js.LICENSE.txt → 710.js.LICENSE.txt} +0 -0
@@ -0,0 +1,122 @@
1
+ import React, { useState } from 'react';
2
+ import { Modal, ModalBody, Select, SelectItem, TextArea } from '@carbon/react';
3
+ import styles from './transition-patient.component.scss';
4
+ import { type QueueEntryResult } from '../../../registry/types';
5
+ import { transitionQueueEntry } from '../../service.resource';
6
+ import { type TransitionQueueEntryDto } from '../../../types/types';
7
+ import { QUEUE_PRIORITIES_UUIDS, QUEUE_STATUS_UUIDS } from '../../../shared/constants/concepts';
8
+ import { showSnackbar } from '@openmrs/esm-framework';
9
+ interface TransitionPatientModalProps {
10
+ open: boolean;
11
+ onModalClose: () => void;
12
+ currentQueueEntry: QueueEntryResult;
13
+ }
14
+ const TransitionPatientModal: React.FC<TransitionPatientModalProps> = ({ open, onModalClose, currentQueueEntry }) => {
15
+ const [selectedComment, setSelectedComment] = useState<string>();
16
+ const [selectedPriority, setSelectedPriority] = useState<string>();
17
+ const [selectedStatus, setSelectedStatus] = useState<string>();
18
+
19
+ const statusChangeHandler = (statusUuid: string) => {
20
+ setSelectedStatus(statusUuid);
21
+ };
22
+
23
+ const transtionQueueEntry = async () => {
24
+ const payload = getTransitionQueueEntryPayload();
25
+ try {
26
+ await transitionQueueEntry(payload);
27
+ showAlert('success', 'Client succesfully Transitioned', '');
28
+ onModalClose();
29
+ } catch (e) {
30
+ showAlert('error', e.message, '');
31
+ }
32
+ };
33
+
34
+ const showAlert = (alertType: 'error' | 'success', title: string, subtitle: string) => {
35
+ showSnackbar({
36
+ kind: alertType,
37
+ title: title,
38
+ subtitle: subtitle,
39
+ });
40
+ };
41
+ const getTransitionQueueEntryPayload = (): TransitionQueueEntryDto => {
42
+ const payload: TransitionQueueEntryDto = {
43
+ queueEntryToTransition: currentQueueEntry.queue_entry_uuid,
44
+ newQueue: currentQueueEntry.service_uuid,
45
+ newStatus: selectedStatus,
46
+ newPriority: selectedPriority,
47
+ newPriorityComment: selectedComment,
48
+ };
49
+
50
+ return payload;
51
+ };
52
+ const priorityChangeHandler = (priorityUuid: string) => {
53
+ setSelectedPriority(priorityUuid);
54
+ };
55
+ const handleCommentChange = (comment: string) => {
56
+ setSelectedComment(comment);
57
+ };
58
+ return (
59
+ <>
60
+ <Modal
61
+ open={open}
62
+ size="md"
63
+ onSecondarySubmit={() => onModalClose()}
64
+ onRequestClose={() => onModalClose()}
65
+ onRequestSubmit={transtionQueueEntry}
66
+ primaryButtonText="Transition"
67
+ secondaryButtonText="Cancel"
68
+ >
69
+ <ModalBody>
70
+ <div className={styles.modelLayout}>
71
+ <div className={styles.sectionHeader}>
72
+ <h4>Move Client</h4>
73
+ </div>
74
+ <div className={styles.formSection}>
75
+ <div className={styles.formRow}>
76
+ <div className={styles.formControl}>
77
+ <Select
78
+ id="queue-status"
79
+ labelText="Select Queue Status"
80
+ onChange={(e) => statusChangeHandler(e.target.value)}
81
+ >
82
+ <SelectItem value="" text="" />;
83
+ <SelectItem value={QUEUE_STATUS_UUIDS.WAITING_UUID} text="IN WAITING" />;
84
+ <SelectItem value={QUEUE_STATUS_UUIDS.IN_SERVICE_UUID} text="IN SERVICE" />;
85
+ <SelectItem value={QUEUE_STATUS_UUIDS.COMPLETED_UUID} text="COMPLETED" />;
86
+ </Select>
87
+ </div>
88
+ <div className={styles.formControl}>
89
+ <Select
90
+ id="priority"
91
+ labelText="Select a Priority"
92
+ onChange={(e) => priorityChangeHandler(e.target.value)}
93
+ >
94
+ <SelectItem value="" text="Select" />;
95
+ <SelectItem value={QUEUE_PRIORITIES_UUIDS.NORMAL_PRIORITY_UUID} text="NORMAL" />;
96
+ <SelectItem value={QUEUE_PRIORITIES_UUIDS.EMERGENCY_PRIORITY_UUID} text="EMERGENCY" />;
97
+ </Select>
98
+ </div>
99
+ </div>
100
+ <div className={styles.formRow}>
101
+ <div className={styles.formControl}>
102
+ <TextArea
103
+ enableCounter
104
+ helperText="Comment"
105
+ id="comment"
106
+ labelText="Comment"
107
+ maxCount={500}
108
+ placeholder="Comment"
109
+ onChange={(e) => handleCommentChange(e.target.value)}
110
+ rows={4}
111
+ />
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </ModalBody>
117
+ </Modal>
118
+ </>
119
+ );
120
+ };
121
+
122
+ export default TransitionPatientModal;
@@ -0,0 +1,19 @@
1
+ .queueListLayout{
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ row-gap: 15px;
6
+ padding: 5px 5px;
7
+ }
8
+ .actionHeader{
9
+ display: flex;
10
+ flex-direction: row;
11
+ justify-content: flex-end;
12
+ width: 100%;
13
+ }
14
+ .tableSection{
15
+ display: flex;
16
+ flex-direction: column;
17
+ width: 100%;
18
+ row-gap: 10px;
19
+ }
@@ -0,0 +1,169 @@
1
+ import {
2
+ Button,
3
+ Link,
4
+ OverflowMenu,
5
+ OverflowMenuItem,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableHead,
10
+ TableHeader,
11
+ TableRow,
12
+ Tag,
13
+ } from '@carbon/react';
14
+ import { type QueueEntryResult } from '../../registry/types';
15
+ import React, { useState } from 'react';
16
+ import styles from './queue-list.component.scss';
17
+ import { QueueEntryPriority, QueueEntryStatus, type TagColor } from '../../types/types';
18
+
19
+ interface QueueListProps {
20
+ queueEntries: QueueEntryResult[];
21
+ handleMovePatient: (queueEntryResult: QueueEntryResult) => void;
22
+ handleTransitionPatient: (queueEntryResult: QueueEntryResult) => void;
23
+ handleServePatient: (queueEntryResult: QueueEntryResult) => void;
24
+ handleSignOff: (queueEntryResult: QueueEntryResult) => void;
25
+ handleRemovePatient: (queueEntryResult: QueueEntryResult) => void;
26
+ showComingFromCol: boolean;
27
+ }
28
+
29
+ const QueueList: React.FC<QueueListProps> = ({
30
+ queueEntries,
31
+ handleMovePatient,
32
+ handleTransitionPatient,
33
+ handleServePatient,
34
+ handleSignOff,
35
+ handleRemovePatient,
36
+ showComingFromCol,
37
+ }) => {
38
+ const [checkIn, setCheckIn] = useState<boolean>(false);
39
+ const handleCheckin = () => {
40
+ setCheckIn((prev) => !prev);
41
+ };
42
+ const getTagTypeByStatus = (status: string): TagColor => {
43
+ let type: TagColor;
44
+ switch (status) {
45
+ case QueueEntryStatus.Completed:
46
+ type = 'green';
47
+ break;
48
+ case QueueEntryStatus.Waiting:
49
+ type = 'gray';
50
+ break;
51
+ case QueueEntryStatus.InService:
52
+ type = 'blue';
53
+ break;
54
+ default:
55
+ type = 'gray';
56
+ }
57
+ return type;
58
+ };
59
+ const getTagTypeByPriority = (priority: string): TagColor => {
60
+ let type: TagColor;
61
+ switch (priority) {
62
+ case QueueEntryPriority.Emergency:
63
+ type = 'red';
64
+ break;
65
+ case QueueEntryPriority.Normal:
66
+ type = 'blue';
67
+ break;
68
+ default:
69
+ type = 'gray';
70
+ }
71
+ return type;
72
+ };
73
+ return (
74
+ <>
75
+ <div className={styles.queueListLayout}>
76
+ <div className={styles.actionHeader}>
77
+ <>
78
+ {checkIn ? (
79
+ <>
80
+ <Button kind="danger" onClick={handleCheckin}>
81
+ Check Out
82
+ </Button>
83
+ </>
84
+ ) : (
85
+ <>
86
+ <Button kind="primary" onClick={handleCheckin}>
87
+ Check In
88
+ </Button>
89
+ </>
90
+ )}
91
+ </>
92
+ </div>
93
+ <div className={styles.tableSection}>
94
+ <Table>
95
+ <TableHead>
96
+ <TableRow>
97
+ <TableHeader>No</TableHeader>
98
+ <TableHeader>Name</TableHeader>
99
+ <TableHeader>Coming From</TableHeader>
100
+ <TableHeader>Ticket</TableHeader>
101
+ <TableHeader>Status</TableHeader>
102
+ <TableHeader>Priority</TableHeader>
103
+ <TableHeader>Wait Time</TableHeader>
104
+ <TableHeader>Action</TableHeader>
105
+ </TableRow>
106
+ </TableHead>
107
+ <TableBody>
108
+ {queueEntries.map((val, index) => (
109
+ <TableRow>
110
+ <TableCell>{index + 1}</TableCell>
111
+ <TableCell>
112
+ {checkIn ? (
113
+ <Link href={`${window.spaBase}/patient/${val.patient_uuid}/chart/`}>
114
+ {val.family_name} {val.middle_name} {val.given_name}
115
+ </Link>
116
+ ) : (
117
+ <>
118
+ {val.family_name} {val.middle_name} {val.given_name}
119
+ </>
120
+ )}
121
+ </TableCell>
122
+ <TableCell>{showComingFromCol ? val.queue_coming_from : ''}</TableCell>
123
+ <TableCell>{val.queue_entry_id}</TableCell>
124
+ <TableCell>
125
+ <Tag size="md" type={getTagTypeByStatus(val.status)}>
126
+ {val.status}
127
+ </Tag>
128
+ </TableCell>
129
+ <TableCell>
130
+ <Tag size="md" type={getTagTypeByPriority(val.priority)}>
131
+ {val.priority}
132
+ </Tag>
133
+ </TableCell>
134
+ <TableCell>{`${val.wait_time_in_min} minute(s)`}</TableCell>
135
+ <TableCell>
136
+ {val.status === QueueEntryStatus.Waiting ? (
137
+ <>
138
+ <Button kind="ghost" disabled={!checkIn} onClick={() => handleServePatient(val)}>
139
+ Serve
140
+ </Button>
141
+ </>
142
+ ) : (
143
+ <>
144
+ {checkIn ? (
145
+ <>
146
+ <OverflowMenu aria-label="overflow-menu">
147
+ <OverflowMenuItem itemText="Move" onClick={() => handleMovePatient(val)} />
148
+ <OverflowMenuItem itemText="Transition" onClick={() => handleTransitionPatient(val)} />
149
+ <OverflowMenuItem itemText="Sign Off" onClick={() => handleSignOff(val)} />
150
+ <OverflowMenuItem itemText="Remove Patient" onClick={() => handleRemovePatient(val)} />
151
+ </OverflowMenu>
152
+ </>
153
+ ) : (
154
+ <></>
155
+ )}
156
+ </>
157
+ )}
158
+ </TableCell>
159
+ </TableRow>
160
+ ))}
161
+ </TableBody>
162
+ </Table>
163
+ </div>
164
+ </div>
165
+ </>
166
+ );
167
+ };
168
+
169
+ export default QueueList;
@@ -0,0 +1,39 @@
1
+ import React, { useState } from 'react';
2
+ import { MetricsCard, MetricsCardBody } from './metrics/metrics-cards/metrics-card.component';
3
+ import { Button, InlineLoading, Stack, Tag } from '@carbon/react';
4
+ import styles from './service-queue.scss';
5
+ import { useQueues } from './service-queues.resource';
6
+ import { Room } from './room/room.component';
7
+ import { type QueueEntryAction } from '../config-schema';
8
+
9
+ interface QueueRoomProps {
10
+ serviceUuid: string;
11
+ overflowMenuKeys: QueueEntryAction[];
12
+ defaultMenuKey: QueueEntryAction;
13
+ }
14
+
15
+ const QueueRoom: React.FC<QueueRoomProps> = ({ serviceUuid, defaultMenuKey, overflowMenuKeys }) => {
16
+ const { data: queuesData, isLoading: isLoadingQueues } = useQueues(serviceUuid);
17
+ const [checkedInRoomUuids, setCheckedInRoomUuids] = useState([]);
18
+
19
+ return <Stack orientation='horizontal' gap={1} className={styles.cardContainer}>
20
+ {
21
+ isLoadingQueues ? <InlineLoading /> : queuesData.data.results.map((queue, index) => (
22
+ <MetricsCard key={index} >
23
+ <Stack as="div" orientation='horizontal' gap={4} className={styles.cardHeaderStack}>
24
+ <p>{queue.display}</p>
25
+ <Tag type="blue" size="sm">
26
+ 0 patient(s)
27
+ </Tag>
28
+ <Button kind={checkedInRoomUuids.includes(queue.uuid) ? 'danger' : 'primary'} size='sm' onClick={() => setCheckedInRoomUuids((prev) => (prev.includes(queue.uuid) ? prev.filter(q => q !== queue.uuid) : [...prev, queue.uuid]))}> {!checkedInRoomUuids.includes(queue.uuid) ? 'Check in' : 'Check out'}</Button>
29
+ </Stack>
30
+ <MetricsCardBody>
31
+ <Room checkedInRoomUuids={checkedInRoomUuids} queueUuid={queue.uuid} overflowMenuKeys={overflowMenuKeys} defaultMenuKey={defaultMenuKey}/>
32
+ </MetricsCardBody>
33
+ </MetricsCard>
34
+ ))
35
+ }
36
+ </Stack>
37
+ }
38
+
39
+ export default QueueRoom;
@@ -0,0 +1,58 @@
1
+ import { Stack, InlineLoading, Tag, OverflowMenu } from "@carbon/react";
2
+ import { isDesktop, useLayoutType } from "@openmrs/esm-framework";
3
+ import dayjs from "dayjs";
4
+ import React, { useEffect, useState } from "react";
5
+ import { MetricsCard, MetricsCardHeader, MetricsCardBody } from "../metrics/metrics-cards/metrics-card.component";
6
+ import { ActionButton } from "../action-button.component";
7
+ import { ActionOverflowMenuItem } from "../action-overflow-menu-item.component";
8
+ import { useTranslation } from "react-i18next";
9
+ import { type QueueEntryAction } from "../../config-schema";
10
+ import { useQueueEntries } from "../service-queues.resource";
11
+
12
+ interface RoomProps {
13
+ checkedInRoomUuids: Array<string>;
14
+ queueUuid: string;
15
+ overflowMenuKeys: QueueEntryAction[];
16
+ defaultMenuKey: QueueEntryAction;
17
+ }
18
+
19
+ export const Room: React.FC<RoomProps> = ({ queueUuid, overflowMenuKeys, checkedInRoomUuids = [], defaultMenuKey = 'move' }) => {
20
+ const { t } = useTranslation();
21
+ const { queueEntries, isLoading } = useQueueEntries(queueUuid);
22
+ const layout = useLayoutType();
23
+ const [checkedIn, setCheckedIn] = useState(false);
24
+
25
+ useEffect(() => {
26
+ if(checkedInRoomUuids.includes(queueUuid)) {
27
+ setCheckedIn(true);
28
+ } else {
29
+ setCheckedIn(false);
30
+ }
31
+ }, [checkedInRoomUuids, queueUuid]);
32
+
33
+ return <Stack as="div">
34
+ {
35
+ isLoading ? (<InlineLoading />) : queueEntries?.data?.results.map((queueEntry, index) => (
36
+ <MetricsCard key={index}>
37
+ <MetricsCardHeader title={queueEntry.display} />
38
+ <MetricsCardBody>
39
+ <p>{`${t('visitTime', 'Visit time')} : ${dayjs(queueEntry.startedAt).format('YYYY-MM-DD HH:mm')}`}</p>
40
+ <Tag type="blue" size="sm">
41
+ {queueEntry.priority?.display}
42
+ </Tag>
43
+ {
44
+ checkedIn ? <>
45
+ <ActionButton key={defaultMenuKey} actionKey={defaultMenuKey} queueEntry={queueEntry} />
46
+ <OverflowMenu aria-label="Actions menu" size={isDesktop(layout) ? 'sm' : 'lg'} align="left" flipped>
47
+ {overflowMenuKeys.map((actionKey) => (
48
+ <ActionOverflowMenuItem key={actionKey} actionKey={actionKey} queueEntry={queueEntry} />
49
+ ))}
50
+ </OverflowMenu>
51
+ </> : <></>
52
+ }
53
+ </MetricsCardBody>
54
+ </MetricsCard>
55
+ ))
56
+ }
57
+ </Stack>
58
+ }
@@ -0,0 +1,14 @@
1
+ .consultationLayout{
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ padding: 15px 15px;
6
+ row-gap: 15px;
7
+ }
8
+ .headerAction{
9
+ display: flex;
10
+ flex-direction: row;
11
+ column-gap: 5px;
12
+ justify-content: flex-end;
13
+ padding: 10px 10px;
14
+ }
@@ -0,0 +1,245 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { type QueueEntryResult } from '../../registry/types';
3
+ import { showSnackbar, useSession } from '@openmrs/esm-framework';
4
+ import { closeQueueEntry, getServiceQueueByLocationUuid } from '../service-queues.resource';
5
+ import { Button, InlineLoading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
6
+ import QueueList from '../queue-list/queue-list.component';
7
+ import styles from './service-queue.component.scss';
8
+ import MovePatientModal from '../modals/move/move-patient.component';
9
+ import TransitionPatientModal from '../modals/transition/transition-patient.component';
10
+ import ServePatientModal from '../modals/serve/serve-patient.comppnent';
11
+ import StatDetails from './stats/stat-details/stat-details.component';
12
+ import SignOffEntryModal from '../modals/sign-off/sign-off.modal';
13
+ import { endVisit } from '../../resources/visit.resource';
14
+ import { QUEUE_SERVICE_UUIDS } from '../../shared/constants/concepts';
15
+
16
+ interface ServiceQueueComponentProps {
17
+ serviceTypeUuid: string;
18
+ title: string;
19
+ }
20
+
21
+ const ServiceQueueComponent: React.FC<ServiceQueueComponentProps> = ({ serviceTypeUuid, title }) => {
22
+ const [queueEntries, setQueueEntries] = useState<QueueEntryResult[]>([]);
23
+ const [selectedQueueEntry, setSelectedQueueEntry] = useState<QueueEntryResult>();
24
+ const [displayMoveModal, setDisplayMoveModal] = useState<boolean>(false);
25
+ const [displayTransitionModal, setDisplayTransitionModal] = useState<boolean>(false);
26
+ const [displayServeModal, setDisplayServeModal] = useState<boolean>(false);
27
+ const [displaySignOffModal, setDisplaySignOffModal] = useState<boolean>(false);
28
+ const [loading, setLoading] = useState<boolean>(false);
29
+ const session = useSession();
30
+ const locationUuid = session.sessionLocation.uuid;
31
+
32
+ const groupEntriesByRooms = () => {
33
+ const roomEntries = {};
34
+ if (!queueEntries || queueEntries.length === 0) return {};
35
+ queueEntries.forEach((qe) => {
36
+ const room = qe.queue_room;
37
+ if (!roomEntries[room]) {
38
+ roomEntries[room] = [qe];
39
+ } else {
40
+ roomEntries[room].push(qe);
41
+ }
42
+ });
43
+ return roomEntries;
44
+ };
45
+
46
+ const groupedByRoom: { [key: string]: QueueEntryResult[] } = useMemo(() => groupEntriesByRooms(), [queueEntries]);
47
+
48
+ useEffect(() => {
49
+ getEntryQueues();
50
+ }, []);
51
+
52
+ const getEntryQueues = async () => {
53
+ setLoading(true);
54
+ const res = await getServiceQueueByLocationUuid(serviceTypeUuid, locationUuid);
55
+ setQueueEntries(res);
56
+ setLoading(false);
57
+ };
58
+
59
+ if (!groupedByRoom) {
60
+ return <>No Data to Display</>;
61
+ }
62
+ const handleMovePatient = (queueEntry: QueueEntryResult) => {
63
+ setDisplayMoveModal(true);
64
+ setSelectedQueueEntry(queueEntry);
65
+ };
66
+ const handleModalCloes = () => {
67
+ setDisplayMoveModal(false);
68
+ setDisplayTransitionModal(false);
69
+ setDisplayServeModal(false);
70
+ handleRefresh();
71
+ };
72
+
73
+ const handleTransitionPatient = (queueEntry: QueueEntryResult) => {
74
+ setDisplayTransitionModal(true);
75
+ setSelectedQueueEntry(queueEntry);
76
+ };
77
+
78
+ const handleServePatient = (queueEntry: QueueEntryResult) => {
79
+ setDisplayServeModal(true);
80
+ setSelectedQueueEntry(queueEntry);
81
+ };
82
+
83
+ const navigateToPatientChart = () => {
84
+ if (selectedQueueEntry && selectedQueueEntry.patient_uuid) {
85
+ window.location.href = `${window.spaBase}/patient/${selectedQueueEntry.patient_uuid}/chart`;
86
+ }
87
+ };
88
+
89
+ const handleSuccessfullServe = () => {
90
+ handleModalCloes();
91
+ navigateToPatientChart();
92
+ };
93
+
94
+ const handleSignOff = (queueEntry: QueueEntryResult) => {
95
+ setDisplaySignOffModal(true);
96
+ setSelectedQueueEntry(queueEntry);
97
+ };
98
+
99
+ const onSuccessfullSignOff = () => {
100
+ setDisplaySignOffModal(false);
101
+ handleRefresh();
102
+ };
103
+
104
+ const handleRefresh = () => {
105
+ getEntryQueues();
106
+ };
107
+
108
+ const handleRemovePatient = async (queueEntryResult: QueueEntryResult) => {
109
+ try {
110
+ await closeQueueEntry(queueEntryResult.queue_entry_uuid);
111
+ showSnackbar({
112
+ kind: 'success',
113
+ title: 'Patient removal from queue successfully!',
114
+ subtitle: '',
115
+ });
116
+ await endVisit(queueEntryResult.visit_uuid, {
117
+ stopDatetime: new Date().toISOString(),
118
+ });
119
+ showSnackbar({
120
+ kind: 'success',
121
+ title: 'Visit Ended successfully!',
122
+ subtitle: '',
123
+ });
124
+ handleRefresh();
125
+ } catch (e) {
126
+ showSnackbar({
127
+ kind: 'error',
128
+ title: 'Patient removal from queue failed!',
129
+ subtitle: e.message ?? '',
130
+ });
131
+ }
132
+ };
133
+
134
+ if (!serviceTypeUuid) {
135
+ return <>No service type defined</>;
136
+ }
137
+
138
+ return (
139
+ <>
140
+ <div className={styles.consultationLayout}>
141
+ <div className={styles.headerSection}>
142
+ <h4>{title}</h4>
143
+ </div>
144
+ <div>
145
+ {queueEntries ? (
146
+ <>
147
+ <StatDetails queueEntries={queueEntries} />
148
+ </>
149
+ ) : (
150
+ <></>
151
+ )}
152
+ </div>
153
+ <div className={styles.headerAction}>
154
+ <Button kind="tertiary" onClick={handleRefresh} disabled={loading}>
155
+ {loading ? <InlineLoading description="Refreshing..." /> : 'Refresh'}
156
+ </Button>
157
+ </div>
158
+
159
+ <div className={styles.contentSection}>
160
+ <Tabs>
161
+ <TabList contained>
162
+ {groupedByRoom &&
163
+ Object.keys(groupedByRoom).map((key) => {
164
+ return <Tab>{key}</Tab>;
165
+ })}
166
+ </TabList>
167
+ <TabPanels>
168
+ {groupedByRoom &&
169
+ Object.keys(groupedByRoom).map((key) => {
170
+ return (
171
+ <TabPanel>
172
+ {
173
+ <QueueList
174
+ queueEntries={groupedByRoom[key]}
175
+ handleMovePatient={handleMovePatient}
176
+ handleTransitionPatient={handleTransitionPatient}
177
+ handleServePatient={handleServePatient}
178
+ handleSignOff={handleSignOff}
179
+ handleRemovePatient={handleRemovePatient}
180
+ showComingFromCol={serviceTypeUuid !== QUEUE_SERVICE_UUIDS.TRIAGE_SERVICE_UUID}
181
+ />
182
+ }
183
+ </TabPanel>
184
+ );
185
+ })}
186
+ </TabPanels>
187
+ </Tabs>
188
+ </div>
189
+ </div>
190
+ {displayMoveModal && selectedQueueEntry ? (
191
+ <>
192
+ <MovePatientModal
193
+ open={displayMoveModal}
194
+ locationUuid={locationUuid}
195
+ serviceUuid={serviceTypeUuid}
196
+ onModalClose={handleModalCloes}
197
+ currentQueueEntryUuid={selectedQueueEntry.queue_entry_uuid}
198
+ />
199
+ </>
200
+ ) : (
201
+ <></>
202
+ )}
203
+
204
+ {displayTransitionModal ? (
205
+ <>
206
+ <TransitionPatientModal
207
+ open={displayTransitionModal}
208
+ onModalClose={handleModalCloes}
209
+ currentQueueEntry={selectedQueueEntry}
210
+ />
211
+ </>
212
+ ) : (
213
+ <></>
214
+ )}
215
+
216
+ {displayServeModal ? (
217
+ <>
218
+ <ServePatientModal
219
+ open={displayServeModal}
220
+ onModalClose={handleModalCloes}
221
+ currentQueueEntry={selectedQueueEntry}
222
+ onSuccessfullServe={handleSuccessfullServe}
223
+ />
224
+ </>
225
+ ) : (
226
+ <></>
227
+ )}
228
+
229
+ {displaySignOffModal ? (
230
+ <>
231
+ <SignOffEntryModal
232
+ open={displaySignOffModal}
233
+ onModalClose={handleModalCloes}
234
+ currentQueueEntry={selectedQueueEntry}
235
+ onSuccessfullSignOff={onSuccessfullSignOff}
236
+ />
237
+ </>
238
+ ) : (
239
+ <></>
240
+ )}
241
+ </>
242
+ );
243
+ };
244
+
245
+ export default ServiceQueueComponent;
@@ -0,0 +1,10 @@
1
+ .statsCard{
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 40%;
5
+ row-gap: 5px;
6
+ border-radius: 12px;
7
+ border: 1px solid #d2d2d2;
8
+ height: 100px;
9
+ padding: 10px 10px;
10
+ }