@openmrs/esm-service-queues-app 5.0.1-pre.2087

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 (254) hide show
  1. package/.turbo/turbo-build.log +40 -0
  2. package/LICENSE.md +401 -0
  3. package/README.md +23 -0
  4. package/__mocks__/active-visits.mock.ts +97 -0
  5. package/__mocks__/appointments-data.mock.ts +283 -0
  6. package/__mocks__/search.mock.ts +35 -0
  7. package/__mocks__/visits.mock.ts +42 -0
  8. package/dist/121.js +1 -0
  9. package/dist/121.js.map +1 -0
  10. package/dist/130.js +2 -0
  11. package/dist/130.js.LICENSE.txt +5 -0
  12. package/dist/130.js.map +1 -0
  13. package/dist/176.js +1 -0
  14. package/dist/176.js.map +1 -0
  15. package/dist/227.js +1 -0
  16. package/dist/227.js.map +1 -0
  17. package/dist/242.js +1 -0
  18. package/dist/242.js.map +1 -0
  19. package/dist/252.js +1 -0
  20. package/dist/252.js.map +1 -0
  21. package/dist/286.js +1 -0
  22. package/dist/286.js.map +1 -0
  23. package/dist/308.js +1 -0
  24. package/dist/308.js.map +1 -0
  25. package/dist/319.js +1 -0
  26. package/dist/328.js +1 -0
  27. package/dist/328.js.map +1 -0
  28. package/dist/330.js +1 -0
  29. package/dist/330.js.map +1 -0
  30. package/dist/364.js +1 -0
  31. package/dist/364.js.map +1 -0
  32. package/dist/425.js +1 -0
  33. package/dist/425.js.map +1 -0
  34. package/dist/457.js +1 -0
  35. package/dist/457.js.map +1 -0
  36. package/dist/469.js +1 -0
  37. package/dist/469.js.map +1 -0
  38. package/dist/521.js +1 -0
  39. package/dist/521.js.map +1 -0
  40. package/dist/53.js +1 -0
  41. package/dist/53.js.map +1 -0
  42. package/dist/574.js +1 -0
  43. package/dist/591.js +2 -0
  44. package/dist/591.js.LICENSE.txt +32 -0
  45. package/dist/591.js.map +1 -0
  46. package/dist/610.js +1 -0
  47. package/dist/610.js.map +1 -0
  48. package/dist/611.js +1 -0
  49. package/dist/611.js.map +1 -0
  50. package/dist/616.js +1 -0
  51. package/dist/616.js.map +1 -0
  52. package/dist/644.js +1 -0
  53. package/dist/644.js.map +1 -0
  54. package/dist/670.js +2 -0
  55. package/dist/670.js.LICENSE.txt +57 -0
  56. package/dist/670.js.map +1 -0
  57. package/dist/733.js +1 -0
  58. package/dist/733.js.map +1 -0
  59. package/dist/757.js +1 -0
  60. package/dist/764.js +1 -0
  61. package/dist/764.js.map +1 -0
  62. package/dist/766.js +1 -0
  63. package/dist/766.js.map +1 -0
  64. package/dist/784.js +2 -0
  65. package/dist/784.js.LICENSE.txt +9 -0
  66. package/dist/784.js.map +1 -0
  67. package/dist/788.js +1 -0
  68. package/dist/807.js +1 -0
  69. package/dist/833.js +1 -0
  70. package/dist/86.js +1 -0
  71. package/dist/86.js.map +1 -0
  72. package/dist/981.js +1 -0
  73. package/dist/981.js.map +1 -0
  74. package/dist/main.js +1 -0
  75. package/dist/main.js.map +1 -0
  76. package/dist/openmrs-esm-service-queues-app.js +1 -0
  77. package/dist/openmrs-esm-service-queues-app.js.buildmanifest.json +930 -0
  78. package/dist/openmrs-esm-service-queues-app.js.map +1 -0
  79. package/dist/routes.json +1 -0
  80. package/jest.config.js +3 -0
  81. package/package.json +53 -0
  82. package/src/active-visits/active-visits-tab.component.tsx +71 -0
  83. package/src/active-visits/active-visits-tab.test.tsx +19 -0
  84. package/src/active-visits/active-visits-table.component.tsx +543 -0
  85. package/src/active-visits/active-visits-table.resource.ts +443 -0
  86. package/src/active-visits/active-visits-table.scss +271 -0
  87. package/src/active-visits/active-visits-table.test.tsx +131 -0
  88. package/src/active-visits/change-status-dialog.component.tsx +233 -0
  89. package/src/active-visits/change-status-dialog.scss +32 -0
  90. package/src/active-visits/change-status-dialog.test.tsx +107 -0
  91. package/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx +218 -0
  92. package/src/add-patient-toqueue/add-patient-toqueue-dialog.scss +32 -0
  93. package/src/add-provider-queue-room/add-provider-queue-room.component.tsx +236 -0
  94. package/src/add-provider-queue-room/add-provider-queue-room.resource.tsx +76 -0
  95. package/src/add-provider-queue-room/add-provider-queue-room.scss +17 -0
  96. package/src/add-provider-queue-room/add-provider-queue-room.test.tsx +98 -0
  97. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx +74 -0
  98. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.tsx +46 -0
  99. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss +15 -0
  100. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx +42 -0
  101. package/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx +36 -0
  102. package/src/config-schema.ts +177 -0
  103. package/src/constants.ts +7 -0
  104. package/src/createDashboardLink.tsx +35 -0
  105. package/src/current-visit/current-visit-summary.component.tsx +38 -0
  106. package/src/current-visit/current-visit-summary.test.tsx +47 -0
  107. package/src/current-visit/current-visit.resource.ts +84 -0
  108. package/src/current-visit/current-visit.scss +34 -0
  109. package/src/current-visit/hooks/useVitalsConceptMetadata.tsx +101 -0
  110. package/src/current-visit/visit-details/biometrics-config-schema.ts +14 -0
  111. package/src/current-visit/visit-details/current-visit-details.component.tsx +96 -0
  112. package/src/current-visit/visit-details/triage-note.component.tsx +53 -0
  113. package/src/current-visit/visit-details/triage-note.scss +89 -0
  114. package/src/current-visit/visit-details/vitals-config-schema.ts +17 -0
  115. package/src/current-visit/visit-details/vitals.component.tsx +164 -0
  116. package/src/dashboard.meta.ts +5 -0
  117. package/src/declarations.d.ts +2 -0
  118. package/src/helpers/functions.ts +67 -0
  119. package/src/helpers/helpers.ts +170 -0
  120. package/src/helpers/time-helpers.ts +15 -0
  121. package/src/home.component.tsx +29 -0
  122. package/src/home.test.tsx +58 -0
  123. package/src/index.ts +100 -0
  124. package/src/overlay.component.tsx +46 -0
  125. package/src/overlay.scss +96 -0
  126. package/src/overlay.test.tsx +49 -0
  127. package/src/past-visit/past-visit-details/encounter-list.component.tsx +51 -0
  128. package/src/past-visit/past-visit-details/medications-list.component.tsx +95 -0
  129. package/src/past-visit/past-visit-details/notes-list.component.tsx +40 -0
  130. package/src/past-visit/past-visit-details/past-visit-summary.component.tsx +172 -0
  131. package/src/past-visit/past-visit-details/past-visit-summary.scss +58 -0
  132. package/src/past-visit/past-visit.component.tsx +37 -0
  133. package/src/past-visit/past-visit.resource.ts +83 -0
  134. package/src/past-visit/past-visit.scss +126 -0
  135. package/src/past-visit/past-visit.test.tsx +41 -0
  136. package/src/patient-info/appointment-details.component.tsx +98 -0
  137. package/src/patient-info/appointment-details.scss +34 -0
  138. package/src/patient-info/appointment-details.test.tsx +36 -0
  139. package/src/patient-info/appointments.resource.tsx +43 -0
  140. package/src/patient-info/contact-details.component.tsx +71 -0
  141. package/src/patient-info/contact-details.scss +31 -0
  142. package/src/patient-info/contact-details.test.tsx +94 -0
  143. package/src/patient-info/hooks/usePatientAttributes.tsx +42 -0
  144. package/src/patient-info/patient-info.component.tsx +78 -0
  145. package/src/patient-info/patient-info.scss +60 -0
  146. package/src/patient-info/patient-info.test.tsx +45 -0
  147. package/src/patient-queue-header/patient-queue-header.component.tsx +64 -0
  148. package/src/patient-queue-header/patient-queue-header.scss +88 -0
  149. package/src/patient-queue-header/patient-queue-illustration.component.tsx +22 -0
  150. package/src/patient-queue-metrics/clinic-metrics.component.tsx +93 -0
  151. package/src/patient-queue-metrics/clinic-metrics.resource.tsx +51 -0
  152. package/src/patient-queue-metrics/clinic-metrics.scss +11 -0
  153. package/src/patient-queue-metrics/clinic-metrics.test.tsx +87 -0
  154. package/src/patient-queue-metrics/metrics-card.component.tsx +64 -0
  155. package/src/patient-queue-metrics/metrics-card.scss +80 -0
  156. package/src/patient-queue-metrics/metrics-header.component.tsx +74 -0
  157. package/src/patient-queue-metrics/metrics-header.scss +22 -0
  158. package/src/patient-queue-metrics/queue-metrics.resource.ts +54 -0
  159. package/src/patient-search/advanced-search.component.tsx +190 -0
  160. package/src/patient-search/advanced-search.scss +154 -0
  161. package/src/patient-search/advanced-search.test.tsx +29 -0
  162. package/src/patient-search/basic-search.component.tsx +112 -0
  163. package/src/patient-search/basic-search.scss +139 -0
  164. package/src/patient-search/basic-search.test.tsx +23 -0
  165. package/src/patient-search/empty-data-illustration.component.tsx +41 -0
  166. package/src/patient-search/hooks/useActivePatientEnrollment.tsx +29 -0
  167. package/src/patient-search/hooks/useDefaultLocation.tsx +14 -0
  168. package/src/patient-search/hooks/usePatients.tsx +25 -0
  169. package/src/patient-search/hooks/useQueueLocations.tsx +20 -0
  170. package/src/patient-search/hooks/useRecommendedVisitTypes.tsx +36 -0
  171. package/src/patient-search/hooks/useScheduledVisits.tsx +50 -0
  172. package/src/patient-search/patient-scheduled-visits.component.tsx +350 -0
  173. package/src/patient-search/patient-scheduled-visits.scss +131 -0
  174. package/src/patient-search/patient-scheduled-visits.test.tsx +55 -0
  175. package/src/patient-search/patient-search.component.tsx +64 -0
  176. package/src/patient-search/search-illustration.component.tsx +27 -0
  177. package/src/patient-search/search-results.component.tsx +75 -0
  178. package/src/patient-search/search-results.scss +80 -0
  179. package/src/patient-search/search-results.test.tsx +77 -0
  180. package/src/patient-search/search.resource.ts +10 -0
  181. package/src/patient-search/visit-form/base-visit-type.component.tsx +82 -0
  182. package/src/patient-search/visit-form/base-visit-type.scss +100 -0
  183. package/src/patient-search/visit-form/base-visit-type.test.tsx +41 -0
  184. package/src/patient-search/visit-form/queue.resource.ts +73 -0
  185. package/src/patient-search/visit-form/recommended-visit-type.component.tsx +38 -0
  186. package/src/patient-search/visit-form/visit-form.component.tsx +366 -0
  187. package/src/patient-search/visit-form/visit-form.scss +96 -0
  188. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx +160 -0
  189. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.scss +21 -0
  190. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx +67 -0
  191. package/src/queue-entry-table-components/actions-menu.component.tsx +51 -0
  192. package/src/queue-entry-table-components/actions-menu.scss +3 -0
  193. package/src/queue-entry-table-components/edit-entry.component.tsx +28 -0
  194. package/src/queue-entry-table-components/edit-entry.scss +14 -0
  195. package/src/queue-entry-table-components/open-chart.component.tsx +33 -0
  196. package/src/queue-entry-table-components/open-chart.resource.tsx +14 -0
  197. package/src/queue-entry-table-components/open-chart.scss +14 -0
  198. package/src/queue-entry-table-components/status-icon.component.tsx +17 -0
  199. package/src/queue-entry-table-components/transition-entry.component.tsx +53 -0
  200. package/src/queue-entry-table-components/transition-entry.scss +22 -0
  201. package/src/queue-patient-linelists/queue-linelist-base-table.component.tsx +230 -0
  202. package/src/queue-patient-linelists/queue-linelist-base-table.scss +110 -0
  203. package/src/queue-patient-linelists/queue-linelist-base-table.test.tsx +118 -0
  204. package/src/queue-patient-linelists/queue-linelist-filter.component.tsx +172 -0
  205. package/src/queue-patient-linelists/queue-linelist-filter.scss +63 -0
  206. package/src/queue-patient-linelists/queue-linelist-filter.test.tsx +76 -0
  207. package/src/queue-patient-linelists/queue-linelist.component.tsx +29 -0
  208. package/src/queue-patient-linelists/queue-linelist.resource.tsx +84 -0
  209. package/src/queue-patient-linelists/queue-linelist.test.tsx +22 -0
  210. package/src/queue-patient-linelists/queue-services-table.component.tsx +63 -0
  211. package/src/queue-patient-linelists/scheduled-appointments-table.component.tsx +300 -0
  212. package/src/queue-patient-linelists/scheduled-appointments-table.test.tsx +42 -0
  213. package/src/queue-rooms/queue-room-form.component.tsx +168 -0
  214. package/src/queue-rooms/queue-room-form.test.tsx +65 -0
  215. package/src/queue-rooms/queue-room.resource.tsx +20 -0
  216. package/src/queue-rooms/queue-room.scss +58 -0
  217. package/src/queue-screen/queue-screen.component.tsx +47 -0
  218. package/src/queue-screen/queue-screen.scss +39 -0
  219. package/src/queue-screen/queue-screen.test.tsx +47 -0
  220. package/src/queue-screen/useActiveTickets.tsx +13 -0
  221. package/src/queue-services/queue-service-form.component.tsx +183 -0
  222. package/src/queue-services/queue-service-form.test.tsx +56 -0
  223. package/src/queue-services/queue-service.resource.tsx +38 -0
  224. package/src/queue-services/queue-service.scss +54 -0
  225. package/src/remove-queue-entry-dialog/remove-queue-entry.component.tsx +95 -0
  226. package/src/remove-queue-entry-dialog/remove-queue-entry.resource.tsx +93 -0
  227. package/src/remove-queue-entry-dialog/remove-queue-entry.scss +8 -0
  228. package/src/remove-queue-entry-dialog/remove-queue-entry.test.tsx +44 -0
  229. package/src/root.component.tsx +28 -0
  230. package/src/root.scss +15 -0
  231. package/src/root.test.tsx +28 -0
  232. package/src/routes.json +91 -0
  233. package/src/side-menu/nav-group/createNavGroup.tsx +17 -0
  234. package/src/side-menu/nav-group/nav-group.component.tsx +24 -0
  235. package/src/side-menu/nav-group/nav-group.test.tsx +32 -0
  236. package/src/side-menu/nav-group/nav-group.ts +10 -0
  237. package/src/side-menu/side-menu.component.tsx +6 -0
  238. package/src/side-menu/side-menu.test.tsx +17 -0
  239. package/src/transition-queue-entry/transition-queue-entry-dialog.component.tsx +174 -0
  240. package/src/transition-queue-entry/transition-queue-entry-dialog.scss +12 -0
  241. package/src/transition-queue-entry/transition-queue-entry-dialog.test.tsx +95 -0
  242. package/src/transition-queue-entry/transition-queue-entry.resource.ts +16 -0
  243. package/src/types/index.ts +434 -0
  244. package/src/visits-missing-inqueue/visits-missing-inqueue.component.tsx +298 -0
  245. package/src/visits-missing-inqueue/visits-missing-inqueue.resource.ts +93 -0
  246. package/src/visits-missing-inqueue/visits-missing-inqueue.scss +108 -0
  247. package/translations/am.json +258 -0
  248. package/translations/en.json +258 -0
  249. package/translations/es.json +258 -0
  250. package/translations/fr.json +258 -0
  251. package/translations/he.json +262 -0
  252. package/translations/km.json +258 -0
  253. package/tsconfig.json +5 -0
  254. package/webpack.config.js +1 -0
@@ -0,0 +1,131 @@
1
+ import React from 'react';
2
+ import { screen, waitFor } from '@testing-library/react';
3
+ import { ConfigObject, useConfig, usePagination, useSession } from '@openmrs/esm-framework';
4
+ import { of } from 'rxjs';
5
+ import { renderWithSwr } from '../../../../tools/test-helpers';
6
+ import { mockServices, mockVisitQueueEntries } from '../../__mocks__/active-visits.mock';
7
+ import { mockMappedQueueEntries } from '../../../../__mocks__/queue-entry.mock';
8
+ import { useVisitQueueEntries } from './active-visits-table.resource';
9
+ import { useQueueRooms } from '../add-provider-queue-room/add-provider-queue-room.resource';
10
+ import { mockSession } from '../../../../__mocks__/session.mock';
11
+ import { useQueueLocations } from '../patient-search/hooks/useQueueLocations';
12
+ import ActiveVisitsTable from './active-visits-table.component';
13
+
14
+ const mockedUseConfig = useConfig as jest.Mock;
15
+ const mockUsePagination = usePagination as jest.Mock;
16
+ const mockGoToPage = jest.fn();
17
+ const mockUseVisitQueueEntries = useVisitQueueEntries as jest.Mock;
18
+ const mockQueueLocations = useQueueLocations as jest.Mock;
19
+ const mockUseQueueRooms = useQueueRooms as jest.Mock;
20
+ const mockUseSession = useSession as jest.Mock;
21
+
22
+ jest.mock('@openmrs/esm-framework', () => {
23
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
24
+ return {
25
+ ...originalModule,
26
+ openmrsFetch: jest.fn(),
27
+ };
28
+ });
29
+
30
+ jest.mock('./active-visits-table.resource', () => {
31
+ const originalModule = jest.requireActual('./active-visits-table.resource');
32
+
33
+ return {
34
+ ...originalModule,
35
+ useServices: jest.fn().mockReturnValue({ services: mockServices }),
36
+ useVisitQueueEntries: jest.fn(),
37
+ };
38
+ });
39
+
40
+ jest.mock('../patient-search/hooks/useQueueLocations', () => {
41
+ const originalModule = jest.requireActual('../patient-search/hooks/useQueueLocations');
42
+
43
+ return {
44
+ ...originalModule,
45
+ useQueueLocations: jest.fn(),
46
+ };
47
+ });
48
+
49
+ jest.mock('../add-provider-queue-room/add-provider-queue-room.resource', () => {
50
+ const originalModule = jest.requireActual('../add-provider-queue-room/add-provider-queue-room.resource');
51
+
52
+ return {
53
+ ...originalModule,
54
+ useQueueRooms: jest.fn(),
55
+ };
56
+ });
57
+
58
+ jest.mock('../helpers/helpers', () => {
59
+ const originalModule = jest.requireActual('../helpers/helpers');
60
+
61
+ return {
62
+ ...originalModule,
63
+ getSelectedServiceName: jest.fn().mockReturnValue(of({ serviceName: 'All' })),
64
+ };
65
+ });
66
+
67
+ jest.setTimeout(20000);
68
+
69
+ describe('ActiveVisitsTable: ', () => {
70
+ beforeEach(() => {
71
+ mockUseSession.mockReturnValue(mockSession),
72
+ mockedUseConfig.mockReturnValue({
73
+ concepts: {
74
+ priorityConceptSetUuid: '96105db1-abbf-48d2-8a52-a1d561fd8c90',
75
+ serviceConceptSetUuid: '330c0ec6-0ac7-4b86-9c70-29d76f0ae20a',
76
+ visitQueueNumberAttributeUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5',
77
+ },
78
+ showQueueTableTab: false,
79
+ } as ConfigObject);
80
+ });
81
+
82
+ it('renders an empty state view if data is unavailable', async () => {
83
+ mockQueueLocations.mockReturnValueOnce({ queueLocations: [] });
84
+ mockUseQueueRooms.mockReturnValue({ rooms: [] });
85
+ mockUseVisitQueueEntries.mockReturnValueOnce({ visitQueueEntries: [], isLoading: false });
86
+
87
+ renderActiveVisitsTable();
88
+
89
+ expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
90
+ expect(screen.getByText(/patients currently in queue/i)).toBeInTheDocument();
91
+ expect(screen.getByText(/no patients to display/i)).toBeInTheDocument();
92
+ });
93
+
94
+ it('renders a tabular overview of visit queue entry data when available', async () => {
95
+ mockQueueLocations.mockReturnValueOnce({ queueLocations: mockQueueLocations });
96
+ mockUseQueueRooms.mockReturnValue({ rooms: mockUseQueueRooms });
97
+ mockUseVisitQueueEntries.mockReturnValue({ visitQueueEntries: mockVisitQueueEntries, isLoading: false });
98
+ mockUsePagination.mockReturnValue({
99
+ results: mockMappedQueueEntries.data.slice(0, 2),
100
+ goTo: mockGoToPage,
101
+ currentPage: 1,
102
+ });
103
+
104
+ renderActiveVisitsTable();
105
+
106
+ await waitFor(() => screen.getByRole('table'));
107
+
108
+ expect(screen.getByText(/patients currently in queue/i)).toBeInTheDocument();
109
+ expect(screen.queryByText(/no patients to display/i)).not.toBeInTheDocument();
110
+ expect(screen.getByRole('link', { name: /eric test ric/i })).toBeInTheDocument();
111
+ expect(screen.getByRole('link', { name: /john smith/i })).toBeInTheDocument();
112
+
113
+ const expectedColumnHeaders = [/name/, /priority/, /status/, /wait time/];
114
+ expectedColumnHeaders.forEach((header) => {
115
+ expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument();
116
+ });
117
+
118
+ const expectedTableRows = [
119
+ /Eric Test Ric Not Urgent Waiting For Triage 206 hours and 2 minutes/,
120
+ /John Smith Emergency Attending Clinical Consultation 206 hours and 2 minutes/,
121
+ ];
122
+
123
+ expectedTableRows.forEach((row) => {
124
+ expect(screen.getByRole('row', { name: new RegExp(row, 'i') })).toBeInTheDocument();
125
+ });
126
+ });
127
+ });
128
+
129
+ function renderActiveVisitsTable() {
130
+ renderWithSwr(<ActiveVisitsTable />);
131
+ }
@@ -0,0 +1,233 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import {
3
+ Button,
4
+ ModalBody,
5
+ ModalFooter,
6
+ ModalHeader,
7
+ Form,
8
+ ContentSwitcher,
9
+ Switch,
10
+ Select,
11
+ SelectItem,
12
+ InlineNotification,
13
+ RadioButton,
14
+ RadioButtonGroup,
15
+ } from '@carbon/react';
16
+ import { useTranslation } from 'react-i18next';
17
+ import {
18
+ ConfigObject,
19
+ navigate,
20
+ showNotification,
21
+ showToast,
22
+ toDateObjectStrict,
23
+ toOmrsIsoString,
24
+ useConfig,
25
+ } from '@openmrs/esm-framework';
26
+ import { MappedQueueEntry } from '../types';
27
+ import {
28
+ updateQueueEntry,
29
+ usePriority,
30
+ useServices,
31
+ useStatus,
32
+ useVisitQueueEntries,
33
+ } from './active-visits-table.resource';
34
+ import { useQueueLocations } from '../patient-search/hooks/useQueueLocations';
35
+ import styles from './change-status-dialog.scss';
36
+
37
+ interface ChangeStatusDialogProps {
38
+ queueEntry: MappedQueueEntry;
39
+ closeModal: () => void;
40
+ }
41
+
42
+ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, closeModal }) => {
43
+ const { t } = useTranslation();
44
+
45
+ const [priority, setPriority] = useState(queueEntry?.priorityUuid);
46
+ const [newQueueUuid, setNewQueueUuid] = useState('');
47
+ const { priorities } = usePriority();
48
+ const config = useConfig() as ConfigObject;
49
+ const [selectedQueueLocation, setSelectedQueueLocation] = useState(queueEntry?.queueLocation);
50
+ const { services } = useServices(selectedQueueLocation);
51
+ const { queueLocations } = useQueueLocations();
52
+ const [editLocation, setEditLocation] = useState(false);
53
+ const { mutate } = useVisitQueueEntries('', selectedQueueLocation);
54
+ const { statuses } = useStatus();
55
+ const [queueStatus, setQueueStatus] = useState(queueEntry?.statusUuid);
56
+
57
+ const changeQueueStatus = useCallback(
58
+ (event) => {
59
+ event.preventDefault();
60
+ const defaultPriority = config.concepts.defaultPriorityConceptUuid;
61
+ setEditLocation(false);
62
+ const queuePriority = priority === '' ? defaultPriority : priority;
63
+ const emergencyPriorityConceptUuid = config.concepts.emergencyPriorityConceptUuid;
64
+ const sortWeight = priority === emergencyPriorityConceptUuid ? 1.0 : 0.0;
65
+ const endDate = toDateObjectStrict(toOmrsIsoString(new Date()));
66
+ updateQueueEntry(
67
+ queueEntry?.visitUuid,
68
+ queueEntry?.queueUuid,
69
+ event?.target['service']?.value,
70
+ queueEntry?.queueEntryUuid,
71
+ queueEntry?.patientUuid,
72
+ queuePriority,
73
+ queueStatus,
74
+ endDate,
75
+ sortWeight,
76
+ ).then(
77
+ ({ status }) => {
78
+ if (status === 201) {
79
+ showToast({
80
+ critical: true,
81
+ title: t('updateEntry', 'Update entry'),
82
+ kind: 'success',
83
+ description: t('queueEntryUpdateSuccessfully', 'Queue Entry Updated Successfully'),
84
+ });
85
+ closeModal();
86
+ mutate();
87
+ navigate({ to: `${window.spaBase}/home/service-queues` });
88
+ }
89
+ },
90
+ (error) => {
91
+ showNotification({
92
+ title: t('queueEntryUpdateFailed', 'Error updating queue entry status'),
93
+ kind: 'error',
94
+ critical: true,
95
+ description: error?.message,
96
+ });
97
+ },
98
+ );
99
+ },
100
+ [
101
+ config.concepts.defaultPriorityConceptUuid,
102
+ config.concepts.emergencyPriorityConceptUuid,
103
+ priority,
104
+ queueEntry?.visitUuid,
105
+ queueEntry?.queueUuid,
106
+ queueEntry?.queueEntryUuid,
107
+ queueEntry?.patientUuid,
108
+ queueStatus,
109
+ t,
110
+ closeModal,
111
+ mutate,
112
+ ],
113
+ );
114
+
115
+ if (Object.keys(queueEntry)?.length === 0) {
116
+ return <ModalHeader closeModal={closeModal} title={t('patientNotInQueue', 'The patient is not in the queue')} />;
117
+ }
118
+
119
+ if (Object.keys(queueEntry)?.length > 0) {
120
+ return (
121
+ <div>
122
+ <Form onSubmit={changeQueueStatus}>
123
+ <ModalHeader
124
+ closeModal={closeModal}
125
+ title={t('movePatientToNextService', 'Move patient to the next service?')}
126
+ />
127
+ <ModalBody>
128
+ <div className={styles.modalBody}>
129
+ <h5>
130
+ {queueEntry.name} &nbsp; · &nbsp;{queueEntry.patientSex} &nbsp; · &nbsp;{queueEntry.patientAge}&nbsp;
131
+ {t('years', 'Years')}
132
+ </h5>
133
+ </div>
134
+ <section>
135
+ <Select
136
+ labelText={t('selectQueueLocation', 'Select a queue location')}
137
+ id="location"
138
+ invalidText="Required"
139
+ value={selectedQueueLocation}
140
+ onChange={(event) => {
141
+ setSelectedQueueLocation(event.target.value);
142
+ setEditLocation(true);
143
+ }}>
144
+ {!selectedQueueLocation ? <SelectItem text={t('selectOption', 'Select an option')} value="" /> : null}
145
+ {queueLocations?.length > 0 &&
146
+ queueLocations.map((location) => (
147
+ <SelectItem key={location.id} text={location.name} value={location.id}>
148
+ {location.name}
149
+ </SelectItem>
150
+ ))}
151
+ </Select>
152
+ </section>
153
+
154
+ <section className={styles.section}>
155
+ <div className={styles.sectionTitle}>{t('queueService', 'Queue service')}</div>
156
+ <Select
157
+ labelText={t('selectService', 'Select a service')}
158
+ id="service"
159
+ invalidText="Required"
160
+ value={newQueueUuid}
161
+ onChange={(event) => setNewQueueUuid(event.target.value)}>
162
+ {!newQueueUuid && editLocation === true ? (
163
+ <SelectItem text={t('selectService', 'Select a service')} value="" />
164
+ ) : null}
165
+ {!queueEntry.queueUuid ? <SelectItem text={t('selectService', 'Select a service')} value="" /> : null}
166
+ {services?.length > 0 &&
167
+ services.map((service) => (
168
+ <SelectItem key={service.uuid} text={service.display} value={service.uuid}>
169
+ {service.display}
170
+ </SelectItem>
171
+ ))}
172
+ </Select>
173
+ </section>
174
+
175
+ <section className={styles.section}>
176
+ <div className={styles.sectionTitle}>{t('queueStatus', 'Queue status')}</div>
177
+ {!statuses?.length ? (
178
+ <InlineNotification
179
+ className={styles.inlineNotification}
180
+ kind={'error'}
181
+ lowContrast
182
+ subtitle={t('configureStatus', 'Please configure status to continue.')}
183
+ title={t('noStatusConfigured', 'No status configured')}
184
+ />
185
+ ) : (
186
+ <RadioButtonGroup
187
+ className={styles.radioButtonWrapper}
188
+ name="status"
189
+ defaultSelected={queueStatus}
190
+ onChange={(uuid) => {
191
+ setQueueStatus(uuid);
192
+ }}>
193
+ {statuses?.length > 0 &&
194
+ statuses.map(({ uuid, display }) => <RadioButton key={uuid} labelText={display} value={uuid} />)}
195
+ </RadioButtonGroup>
196
+ )}
197
+ </section>
198
+
199
+ <section className={styles.section}>
200
+ <div className={styles.sectionTitle}>{t('queuePriority', 'Queue priority')}</div>
201
+ <ContentSwitcher
202
+ size="sm"
203
+ selectedIndex={1}
204
+ onChange={(event) => {
205
+ setPriority(event.name as any);
206
+ }}>
207
+ {priorities?.length > 0 ? (
208
+ priorities.map(({ uuid, display }) => {
209
+ return <Switch name={uuid} text={display} key={uuid} value={uuid} />;
210
+ })
211
+ ) : (
212
+ <Switch
213
+ name={t('noPriorityFound', 'No priority found')}
214
+ text={t('noPriorityFound', 'No priority found')}
215
+ value={null}
216
+ />
217
+ )}
218
+ </ContentSwitcher>
219
+ </section>
220
+ </ModalBody>
221
+ <ModalFooter>
222
+ <Button kind="secondary" onClick={closeModal}>
223
+ {t('cancel', 'Cancel')}
224
+ </Button>
225
+ <Button type="submit">{t('moveToNextService', 'Move to next service')}</Button>
226
+ </ModalFooter>
227
+ </Form>
228
+ </div>
229
+ );
230
+ }
231
+ };
232
+
233
+ export default ChangeStatus;
@@ -0,0 +1,32 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .radioButtonGroup {
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: flex-start;
9
+ margin-top: spacing.$spacing-03;
10
+ min-height: spacing.$spacing-10;
11
+ width: 100%;
12
+ @include type.type-style('body-compact-01');
13
+ }
14
+
15
+ .radioButton {
16
+ padding: spacing.$spacing-02 spacing.$spacing-02;
17
+ margin: spacing.$spacing-03 0;
18
+ }
19
+
20
+ section {
21
+ margin: spacing.$spacing-03;
22
+ }
23
+
24
+ .sectionTitle {
25
+ @include type.type-style('heading-compact-02');
26
+ color: $text-02;
27
+ margin-bottom: spacing.$spacing-04;
28
+ }
29
+
30
+ .modalBody {
31
+ padding-bottom: spacing.$spacing-05;
32
+ }
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import { screen, render, waitFor, within } from '@testing-library/react';
3
+ import { mockServices } from '../../__mocks__/active-visits.mock';
4
+ import { mockPriorities, mockStatus } from '../../../../__mocks__/metrics.mock';
5
+ import { mockSession } from '../../../../__mocks__/session.mock';
6
+ import { mockLocations } from '../../../../__mocks__/locations.mock';
7
+ import { ConfigObject, showToast, useConfig, showNotification } from '@openmrs/esm-framework';
8
+ import ChangeStatus from './change-status-dialog.component';
9
+ import { mockQueueEntry } from '../../../../__mocks__/queue-entry.mock';
10
+ import userEvent from '@testing-library/user-event';
11
+ import { updateQueueEntry } from './active-visits-table.resource';
12
+
13
+ const mockedUseConfig = useConfig as jest.Mock;
14
+ const mockShowToast = showToast as jest.Mock;
15
+ const mockUpdateQueueEntry = updateQueueEntry as jest.Mock;
16
+ const mockShowNotification = showNotification as jest.Mock;
17
+
18
+ jest.mock('./active-visits-table.resource.ts', () => {
19
+ const originalModule = jest.requireActual('./active-visits-table.resource.ts');
20
+
21
+ return {
22
+ ...originalModule,
23
+ useServices: jest.fn().mockReturnValue({ services: mockServices }),
24
+ usePriority: jest.fn().mockReturnValue({ priorities: mockPriorities }),
25
+ useStatus: jest.fn().mockReturnValue({ statuses: mockStatus }),
26
+ updateQueueEntry: jest.fn(),
27
+ };
28
+ });
29
+
30
+ jest.mock('@openmrs/esm-framework', () => {
31
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
32
+ return {
33
+ ...originalModule,
34
+ openmrsFetch: jest.fn(),
35
+ toOmrsIsoString: jest.fn(),
36
+ toDateObjectStrict: jest.fn(),
37
+ useLocations: jest.fn().mockImplementation(() => mockLocations.data),
38
+ useSession: jest.fn().mockImplementation(() => mockSession.data),
39
+ };
40
+ });
41
+
42
+ describe('Queue entry details', () => {
43
+ beforeEach(() =>
44
+ mockedUseConfig.mockReturnValue({
45
+ concepts: {
46
+ priorityConceptSetUuid: '96105db1-abbf-48d2-8a52-a1d561fd8c90',
47
+ serviceConceptSetUuid: '330c0ec6-0ac7-4b86-9c70-29d76f0ae20a',
48
+ statusConceptSetUuid: 'd60ffa60-fca6-4c60-aea9-a79469ae65c7',
49
+ },
50
+ } as ConfigObject),
51
+ );
52
+
53
+ it('should update a queue entry and display toast message', async () => {
54
+ const user = userEvent.setup();
55
+
56
+ mockUpdateQueueEntry.mockResolvedValueOnce({ data: mockQueueEntry, status: 201, statusText: 'Updated' });
57
+
58
+ renderUpdateQueueEntryDialog();
59
+ expect(screen.getByText(/queue service/i)).toBeInTheDocument();
60
+ expect(screen.getByText(/queue priority/i)).toBeInTheDocument();
61
+
62
+ await waitFor(() => user.click(screen.getByRole('button', { name: /move to next service/i })));
63
+
64
+ expect(mockShowToast).toHaveBeenCalledTimes(1);
65
+ expect(mockShowToast).toHaveBeenCalledWith({
66
+ critical: true,
67
+ kind: 'success',
68
+ title: 'Update entry',
69
+ description: 'Queue Entry Updated Successfully',
70
+ });
71
+ });
72
+ it('should display error message when rest api call to update queue entry fails', async () => {
73
+ const user = userEvent.setup();
74
+
75
+ mockUpdateQueueEntry.mockRejectedValue({
76
+ message: 'Internal Server Error',
77
+ response: {
78
+ status: 500,
79
+ statusText: 'Internal Server Error',
80
+ },
81
+ });
82
+
83
+ renderUpdateQueueEntryDialog();
84
+
85
+ expect(screen.getByText(/move patient to the next service?/i)).toBeInTheDocument();
86
+ expect(screen.getByText(/queue service/i)).toBeInTheDocument();
87
+ expect(screen.getByText(/queue priority/i)).toBeInTheDocument();
88
+ const queueServiceTypes = screen.getByLabelText('Select a service');
89
+
90
+ expect(within(queueServiceTypes).getAllByRole('option')).toHaveLength(2);
91
+ expect(within(queueServiceTypes).getAllByRole('option')[0]).toHaveValue('176052c7-5fd4-4b33-89cc-7bae6848c65a');
92
+ expect(within(queueServiceTypes).getAllByRole('option')[1]).toHaveValue('d80ff12a-06a7-11ed-b939-0242ac120002');
93
+
94
+ await waitFor(() => user.click(screen.getByRole('button', { name: /move to next service/i })));
95
+
96
+ expect(mockShowNotification).toHaveBeenCalledWith({
97
+ description: 'Internal Server Error',
98
+ kind: 'error',
99
+ title: 'Error updating queue entry status',
100
+ critical: true,
101
+ });
102
+ });
103
+ });
104
+
105
+ const renderUpdateQueueEntryDialog = () => {
106
+ render(<ChangeStatus queueEntry={mockQueueEntry} closeModal={() => false} />);
107
+ };