@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
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .container {
6
+ background-color: $ui-background;
7
+ display: flex;
8
+ flex-direction: column;
9
+ justify-content: space-between;
10
+ height: calc(100vh - 6rem);
11
+ }
12
+
13
+ .grid {
14
+ padding: 0;
15
+ }
16
+
17
+ .visitTile {
18
+ margin: spacing.$spacing-05 0;
19
+
20
+ &:global(.cds--tile--is-selected:focus) {
21
+ background-color: $color-blue-10;
22
+ color: $color-blue-10;
23
+ border: 1px solid $color-blue-60-2;
24
+ outline: none;
25
+ }
26
+
27
+ &:global(.cds--tile--is-selected:hover) {
28
+ background-color: $color-blue-10;
29
+ color: $color-blue-10;
30
+ border: 1px solid $color-blue-60-2;
31
+ outline: none;
32
+ }
33
+
34
+ &:global(.cds--tile--is-selected:hover .cds--tile__checkmark svg) {
35
+ fill: $color-blue-60-2;
36
+ }
37
+ }
38
+
39
+ .recentlyScheduledVisitsContainer {
40
+ margin-top: spacing.$spacing-06;
41
+ margin-bottom: spacing.$spacing-10;
42
+ }
43
+
44
+ .row {
45
+ margin: 0 spacing.$spacing-05 spacing.$spacing-05 spacing.$spacing-05;
46
+ }
47
+
48
+ .backButton {
49
+ align-items: center;
50
+ display: flex;
51
+ justify-content: flex-start;
52
+ margin: spacing.$spacing-03 0;
53
+ padding: 0;
54
+ @include type.type-style('body-compact-01');
55
+
56
+ button {
57
+ display: flex;
58
+
59
+ svg {
60
+ order: 1;
61
+ margin-right: 0.5rem;
62
+ margin-left: 0rem !important;
63
+ }
64
+
65
+ span {
66
+ order: 2;
67
+ }
68
+ }
69
+ }
70
+
71
+ .heading {
72
+ @include type.type-style('body-02');
73
+ color: $text-02;
74
+ margin-left: 1.2rem;
75
+ }
76
+
77
+ .primaryText {
78
+ @include type.type-style('heading-compact-02');
79
+ }
80
+
81
+ .secondaryText {
82
+ @include type.type-style('body-01');
83
+ color: $text-02;
84
+ }
85
+
86
+ .button {
87
+ height: 4rem;
88
+ display: flex;
89
+ align-content: flex-start;
90
+ align-items: baseline;
91
+ min-width: 50%;
92
+ }
93
+
94
+ .text-divider {
95
+ display: flex;
96
+ align-items: center;
97
+ margin: spacing.$spacing-08 25%;
98
+ @include type.type-style('heading-03');
99
+ color: $text-02;
100
+ }
101
+
102
+ .text-divider::before,
103
+ .text-divider::after {
104
+ content: '';
105
+ height: 1px;
106
+ background-color: $text-03;
107
+ flex-grow: 1;
108
+ }
109
+
110
+ .text-divider::before {
111
+ margin-right: spacing.$spacing-05;
112
+ }
113
+
114
+ .text-divider::after {
115
+ margin-left: spacing.$spacing-05;
116
+ }
117
+
118
+ .buttonContainer {
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ margin-bottom: spacing.$spacing-03;
123
+ }
124
+
125
+ .prioritySwitcher {
126
+ margin: spacing.$spacing-03 0 spacing.$spacing-03;
127
+ }
128
+
129
+ .emptyAppointment {
130
+ margin: spacing.$spacing-05;
131
+ }
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import { screen } from '@testing-library/react';
3
+ import PatientScheduledVisits from './patient-scheduled-visits.component';
4
+ import { renderWithSwr, waitForLoadingToFinish } from '../../../../tools/test-helpers';
5
+ import { mockPatientsVisits } from '../../../../__mocks__/patient-visits.mock';
6
+ import { mockLocations } from '../../../../__mocks__/locations.mock';
7
+ import { mockSession } from '../../../../__mocks__/session.mock';
8
+ import { ConfigObject, openmrsFetch, useConfig } from '@openmrs/esm-framework';
9
+ import { mockPatient } from '../../../../__mocks__/appointments.mock';
10
+
11
+ const mockedUseConfig = useConfig as jest.Mock;
12
+ const mockToggleSearchType = jest.fn();
13
+ const mockedOpenmrsFetch = openmrsFetch as jest.Mock;
14
+
15
+ const testProps = {
16
+ toggleSearchType: mockToggleSearchType,
17
+ patientUuid: mockPatient.uuid,
18
+ closePanel: () => false,
19
+ };
20
+
21
+ jest.mock('@openmrs/esm-framework', () => {
22
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
23
+ return {
24
+ ...originalModule,
25
+ openmrsFetch: jest.fn(),
26
+ useLocations: jest.fn().mockImplementation(() => mockLocations.data),
27
+ useSession: jest.fn().mockImplementation(() => mockSession.data),
28
+ };
29
+ });
30
+ describe('ScheduledVisits', () => {
31
+ beforeEach(() =>
32
+ mockedUseConfig.mockReturnValue({
33
+ concepts: {
34
+ priorityConceptSetUuid: '96105db1-abbf-48d2-8a52-a1d561fd8c90',
35
+ serviceConceptSetUuid: '330c0ec6-0ac7-4b86-9c70-29d76f0ae20a',
36
+ statusConceptSetUuid: 'd60ffa60-fca6-4c60-aea9-a79469ae65c7',
37
+ },
38
+ } as ConfigObject),
39
+ );
40
+ it('should display recent and future scheduled visits', async () => {
41
+ mockedOpenmrsFetch.mockReturnValueOnce({ data: mockPatientsVisits.recentVisits });
42
+
43
+ renderPatientScheduledVisits();
44
+
45
+ await waitForLoadingToFinish();
46
+
47
+ expect(screen.getByText(/Cardiology Consultation 1/i)).toBeInTheDocument();
48
+ expect(screen.getByText(/08-Aug-2022, 02:56 PM · 10 Engineer VCT/i)).toBeInTheDocument();
49
+ expect(screen.getByText(/No appointments found/i)).toBeInTheDocument();
50
+ });
51
+ });
52
+
53
+ function renderPatientScheduledVisits() {
54
+ renderWithSwr(<PatientScheduledVisits {...testProps} />);
55
+ }
@@ -0,0 +1,64 @@
1
+ import React, { useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { SearchTypes } from '../types';
4
+ import Overlay from '../overlay.component';
5
+ import PatientScheduledVisits from './patient-scheduled-visits.component';
6
+ import QueueServiceForm from '../queue-services/queue-service-form.component';
7
+ import QueueRoomForm from '../queue-rooms/queue-room-form.component';
8
+ import VisitForm from './visit-form/visit-form.component';
9
+
10
+ interface PatientSearchProps {
11
+ closePanel: () => void;
12
+ view?: string;
13
+ viewState: {
14
+ selectedPatientUuid?: string;
15
+ };
16
+ headerTitle?: string;
17
+ }
18
+
19
+ const PatientSearch: React.FC<PatientSearchProps> = ({ closePanel, view, viewState, headerTitle }) => {
20
+ const { t } = useTranslation();
21
+ const { selectedPatientUuid } = viewState;
22
+ const [searchType, setSearchType] = useState<SearchTypes>(
23
+ view === 'queue_service_form'
24
+ ? SearchTypes.QUEUE_SERVICE_FORM
25
+ : view === 'queue_room_form'
26
+ ? SearchTypes.QUEUE_ROOM_FORM
27
+ : SearchTypes.SCHEDULED_VISITS,
28
+ );
29
+ const [newVisitMode, setNewVisitMode] = useState<boolean>(false);
30
+
31
+ const toggleSearchType = (searchType: SearchTypes, mode: boolean = false) => {
32
+ setSearchType(searchType);
33
+ setNewVisitMode(mode);
34
+ };
35
+
36
+ return (
37
+ <>
38
+ <Overlay header={headerTitle} closePanel={closePanel}>
39
+ <div className="omrs-main-content">
40
+ {searchType === SearchTypes.SCHEDULED_VISITS ? (
41
+ <PatientScheduledVisits
42
+ patientUuid={selectedPatientUuid}
43
+ toggleSearchType={toggleSearchType}
44
+ closePanel={closePanel}
45
+ />
46
+ ) : searchType === SearchTypes.VISIT_FORM ? (
47
+ <VisitForm
48
+ patientUuid={selectedPatientUuid}
49
+ toggleSearchType={toggleSearchType}
50
+ closePanel={closePanel}
51
+ mode={newVisitMode}
52
+ />
53
+ ) : searchType === SearchTypes.QUEUE_SERVICE_FORM ? (
54
+ <QueueServiceForm toggleSearchType={toggleSearchType} closePanel={closePanel} />
55
+ ) : searchType === SearchTypes.QUEUE_ROOM_FORM ? (
56
+ <QueueRoomForm toggleSearchType={toggleSearchType} closePanel={closePanel} />
57
+ ) : null}
58
+ </div>
59
+ </Overlay>
60
+ </>
61
+ );
62
+ };
63
+
64
+ export default PatientSearch;
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ export default function SearchIllustration({ width = '62', height = '48' }) {
4
+ return (
5
+ <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
6
+ <defs>
7
+ <path
8
+ d="M36.001 3.218C47.482 9.838 51.403 24.52 44.782 36 38.162 47.482 23.48 51.403 12 44.782.518 38.162-3.403 23.48 3.218 12 9.838.518 24.52-3.403 36 3.218z"
9
+ id="a"
10
+ />
11
+ </defs>
12
+ <g fill="none" fillRule="evenodd">
13
+ <path d="M36.001 3.218C47.482 9.838 51.403 24.52 44.782 36 38.162 47.482 23.48 51.403 12 44.782.518 38.162-3.403 23.48 3.218 12 9.838.518 24.52-3.403 36 3.218z" />
14
+ <mask id="b" fill="#fff">
15
+ <use xlinkHref="#a" />
16
+ </mask>
17
+ <use fill="#CEE6E5" xlinkHref="#a" />
18
+ <path
19
+ d="M38 48v-2.618c0-6.696-4.58-12.296-10.72-13.798a8.159 8.159 0 0 0 4.76-7.427C32.04 19.653 28.44 16 24 16c-4.44 0-8.04 3.653-8.04 8.157a8.159 8.159 0 0 0 4.76 7.427C14.58 33.086 10 38.686 10 45.382v2.598"
20
+ fill="#9ACBCA"
21
+ mask="url(#b)"
22
+ />
23
+ <path fill="#9ACBCA" d="m45.34 35 17.124 10.33-1.55 2.57L43.79 37.568z" />
24
+ </g>
25
+ </svg>
26
+ );
27
+ }
@@ -0,0 +1,75 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import { Dropdown } from '@carbon/react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { SearchTypes } from '../types';
5
+ import PatientInfo from '../patient-info/patient-info.component';
6
+ import styles from './search-results.scss';
7
+
8
+ interface SearchResultsProps {
9
+ patients: Array<fhir.Patient>;
10
+ hidePanel?: any;
11
+ toggleSearchType: (searchMode: SearchTypes, patientUuid: string) => void;
12
+ }
13
+
14
+ type SortingCriteria = 'firstNameFirst' | 'lastNameFirst' | 'oldest' | 'youngest';
15
+
16
+ const SearchResults: React.FC<SearchResultsProps> = ({ patients, toggleSearchType }) => {
17
+ const { t } = useTranslation();
18
+ const [selectedSortingCriteria, setSelectedSortingCriteria] = useState<SortingCriteria>('firstNameFirst');
19
+
20
+ const sortedPatient = useMemo(() => {
21
+ return patients.sort((patientA, patientB) => {
22
+ if (selectedSortingCriteria === 'oldest') {
23
+ return new Date(patientA.birthDate).getTime() - new Date(patientB.birthDate).getTime();
24
+ }
25
+ if (selectedSortingCriteria === 'youngest') {
26
+ return new Date(patientB.birthDate).getTime() - new Date(patientA.birthDate).getTime();
27
+ }
28
+ if (selectedSortingCriteria === 'firstNameFirst') {
29
+ return patientA.name?.[0].given?.join('') < patientB.name?.[0].given?.join('') ? -1 : 0;
30
+ }
31
+ if (selectedSortingCriteria === 'lastNameFirst') {
32
+ return patientA?.name?.[0].family < patientB?.name?.[0].family ? -1 : 0;
33
+ }
34
+ });
35
+ }, [patients, selectedSortingCriteria]);
36
+
37
+ const sortingCriteria = [
38
+ { id: 'firstNameFirst', label: t('firstNameSort', 'First name (a-z)') },
39
+ { id: 'lastNameFirst', label: t('lastNameSort', 'Last name (a-z)') },
40
+ { id: 'oldest', label: t('oldest', 'Oldest first') },
41
+ { id: 'youngest', label: t('youngest', 'Youngest first') },
42
+ ];
43
+
44
+ return (
45
+ <>
46
+ <div className={styles.sortingCriteriaSelect}>
47
+ <Dropdown
48
+ ariaLabel="sortBy"
49
+ id="sortingCriteriaDropdown"
50
+ items={sortingCriteria}
51
+ initialSelectedItem={sortingCriteria[0]}
52
+ label={`${t('sortBy', 'Sort by')}:`}
53
+ titleText={`${t('sortBy', 'Sort by')}:`}
54
+ type="inline"
55
+ size="sm"
56
+ onChange={({ selectedItem }) => setSelectedSortingCriteria(selectedItem.id as SortingCriteria)}
57
+ />
58
+ </div>
59
+ {sortedPatient.map((patient, index) => (
60
+ <div key={`search-result-${index}`} className={styles.patientChart}>
61
+ <div className={styles.container}>
62
+ <PatientInfo
63
+ patient={patient}
64
+ handlePatientInfoClick={() => {
65
+ toggleSearchType(SearchTypes.SCHEDULED_VISITS, patient.id);
66
+ }}
67
+ />
68
+ </div>
69
+ </div>
70
+ ))}
71
+ </>
72
+ );
73
+ };
74
+
75
+ export default SearchResults;
@@ -0,0 +1,80 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .patientChart {
6
+ text-decoration: none;
7
+ }
8
+
9
+ :global(.omrs-breakpoint-gt-tablet) .patientChart {
10
+ background-color: $ui-02;
11
+ }
12
+
13
+ .container {
14
+ & .patientBanner {
15
+ background-color: red !important;
16
+ }
17
+ }
18
+
19
+ .patientBanner {
20
+ display: flex;
21
+ }
22
+
23
+ .patientName {
24
+ @include type.type-style('heading-03');
25
+ }
26
+
27
+ .patientAvatar {
28
+ width: spacing.$spacing-11;
29
+ height: spacing.$spacing-11;
30
+ margin: spacing.$spacing-05 spacing.$spacing-05 0;
31
+ border-radius: 1px;
32
+ }
33
+
34
+ .patientInfo {
35
+ width: 100%;
36
+ display: flex;
37
+ flex-flow: column wrap;
38
+ margin: spacing.$spacing-05 0;
39
+ cursor: pointer;
40
+ }
41
+
42
+ .demographics {
43
+ @include type.type-style('body-compact-02');
44
+ color: $text-02;
45
+ margin: 0.3rem 0;
46
+ }
47
+
48
+ .identifiers {
49
+ @include type.type-style('body-compact-02');
50
+ color: $ui-04;
51
+ }
52
+
53
+ .actionsContainer {
54
+ padding-top: spacing.$spacing-03;
55
+ margin-top: spacing.$spacing-05;
56
+ }
57
+
58
+ .sortingCriteriaSelect {
59
+ display: flex;
60
+ width: 100%;
61
+ justify-content: flex-end;
62
+ @include type.type-style('body-compact-02');
63
+ width: 100%;
64
+
65
+ :global(.cds--dropdown__wrapper--inline) {
66
+ gap: 0;
67
+ }
68
+
69
+ :global(.cds--list-box__menu-icon) {
70
+ height: spacing.$spacing-05;
71
+ }
72
+
73
+ :global(.cds--list-box__menu) {
74
+ left: -7.5rem;
75
+ }
76
+
77
+ :global(.cds--label) {
78
+ margin-right: spacing.$spacing-02;
79
+ }
80
+ }
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import { screen, render } from '@testing-library/react';
3
+ import { age } from '@openmrs/esm-framework';
4
+ import SearchResults from './search-results.component';
5
+
6
+ const mockAge = age as jest.Mock;
7
+
8
+ jest.mock('@openmrs/esm-framework', () => {
9
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
10
+ return {
11
+ ...originalModule,
12
+ age: jest.fn(),
13
+ };
14
+ });
15
+
16
+ const patients = [
17
+ {
18
+ id: 'b73bee73-7bf0-4872-9657-ba9de0d93478',
19
+ name: [{ given: ['John'], family: 'Smith' }],
20
+ person: {
21
+ gender: 'Male',
22
+ age: 2,
23
+ birthdate: '2020-02-01T00:00:00.000+0000',
24
+ birthdateEstimated: true,
25
+ personName: {
26
+ display: 'John Smith',
27
+ uuid: '27698bff-4056-430c-824c-cb18cf9329d8',
28
+ givenName: 'John',
29
+ middleName: null,
30
+ familyName: 'Smith',
31
+ familyName2: null,
32
+ voided: false,
33
+ },
34
+ addresses: [
35
+ {
36
+ display: 'Bom Jesus Street',
37
+ uuid: '7eab94da-4b33-4da9-b352-da21a571221a',
38
+ preferred: true,
39
+ address1: 'Bom Jesus Street',
40
+ address2: null,
41
+ cityVillage: 'Recife',
42
+ stateProvince: 'Pernambuco',
43
+ country: 'Brazil',
44
+ postalCode: '50030-310',
45
+ countyDistrict: null,
46
+ },
47
+ ],
48
+ display: 'John Smith',
49
+ dead: false,
50
+ deathDate: null,
51
+ },
52
+ gender: 'Male',
53
+ birthDate: '2020-02-01T00:00:00.000+0000',
54
+ deceasedDateTime: null,
55
+ identifier: [{ value: '10001F0' }],
56
+ address: [{ city: 'Recife', country: 'Brazil', postalCode: '50030-310', state: 'Pernambuco', use: 'home' }],
57
+ telecom: [],
58
+ patientIdentifier: { identifier: [{ value: '10001F0' }] },
59
+ },
60
+ ];
61
+
62
+ const mockToggleSearchType = jest.fn();
63
+
64
+ describe('Search Results', () => {
65
+ it('should search results page', () => {
66
+ mockAge.mockReturnValue(35);
67
+ renderSearchResults();
68
+ expect(screen.getByText(/John Smith/)).toBeInTheDocument();
69
+ expect(screen.getByText(/35/)).toBeInTheDocument();
70
+ expect(screen.getByText(/Male/)).toBeInTheDocument();
71
+ expect(screen.getByText(/01 — Feb — 2020/i)).toBeInTheDocument();
72
+ });
73
+ });
74
+
75
+ const renderSearchResults = () => {
76
+ render(<SearchResults patients={patients} toggleSearchType={mockToggleSearchType} />);
77
+ };
@@ -0,0 +1,10 @@
1
+ import { openmrsFetch } from '@openmrs/esm-framework';
2
+
3
+ export function findPatients(query: string, objectVersion: string, controller: AbortController, includeDead: boolean) {
4
+ const url = `/ws/rest/v1/patient?q=${query}&v=${objectVersion}&includeDead=${includeDead}`;
5
+
6
+ return openmrsFetch(url, {
7
+ method: 'GET',
8
+ signal: controller.signal,
9
+ });
10
+ }
@@ -0,0 +1,82 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import debounce from 'lodash-es/debounce';
4
+ import isEmpty from 'lodash-es/isEmpty';
5
+ import { Layer, Search, RadioButtonGroup, RadioButton, Tile } from '@carbon/react';
6
+ import { useLayoutType, usePagination, VisitType } from '@openmrs/esm-framework';
7
+ import EmptyDataIllustration from '../empty-data-illustration.component';
8
+ import styles from './base-visit-type.scss';
9
+
10
+ interface BaseVisitTypeProps {
11
+ onChange: (event) => void;
12
+ patientUuid: string;
13
+ visitTypes: Array<VisitType>;
14
+ }
15
+
16
+ const BaseVisitType: React.FC<BaseVisitTypeProps> = ({ onChange, visitTypes }) => {
17
+ const { t } = useTranslation();
18
+ const isTablet = useLayoutType() === 'tablet';
19
+ const [searchTerm, setSearchTerm] = useState<string>('');
20
+
21
+ const searchResults = useMemo(() => {
22
+ if (!isEmpty(searchTerm)) {
23
+ return visitTypes.filter((visitType) => visitType.display.toLowerCase().search(searchTerm.toLowerCase()) !== -1);
24
+ } else {
25
+ return visitTypes;
26
+ }
27
+ }, [searchTerm, visitTypes]);
28
+
29
+ const handleSearch = React.useMemo(() => debounce((searchTerm) => setSearchTerm(searchTerm), 300), []);
30
+
31
+ const { results, currentPage, goTo } = usePagination(searchResults, 5);
32
+
33
+ return (
34
+ <div className={`${styles.visitTypeOverviewWrapper} ${isTablet ? styles.tablet : styles.desktop}`}>
35
+ {results.length ? (
36
+ <>
37
+ {isTablet ? (
38
+ <Layer>
39
+ <Search
40
+ onChange={(event) => handleSearch(event.target.value)}
41
+ placeholder={t('searchForAVisitType', 'Search for a visit type')}
42
+ labelText=""
43
+ />
44
+ </Layer>
45
+ ) : (
46
+ <Search
47
+ onChange={(event) => handleSearch(event.target.value)}
48
+ placeholder={t('searchForAVisitType', 'Search for a visit type')}
49
+ labelText=""
50
+ />
51
+ )}
52
+
53
+ <RadioButtonGroup
54
+ className={styles.radioButtonGroup}
55
+ defaultSelected={results?.length === 1 && results[0].uuid}
56
+ orientation="vertical"
57
+ onChange={onChange}
58
+ name="radio-button-group"
59
+ valueSelected={results?.length >= 1 && results[0].uuid}>
60
+ {results.map(({ uuid, display, name }) => (
61
+ <RadioButton key={uuid} className={styles.radioButton} id={name} labelText={display} value={uuid} />
62
+ ))}
63
+ </RadioButtonGroup>
64
+ </>
65
+ ) : (
66
+ <Layer>
67
+ <Tile className={styles.tile}>
68
+ <div className={isTablet ? styles.tabletHeading : styles.desktopHeading}>
69
+ <h4>{t('visitType', 'Visit type')}</h4>
70
+ </div>
71
+ <EmptyDataIllustration />
72
+ <p className={styles.content}>
73
+ {t('noVisitTypes', 'There are no visit types to display for this patient')}
74
+ </p>
75
+ </Tile>
76
+ </Layer>
77
+ )}
78
+ </div>
79
+ );
80
+ };
81
+
82
+ export default BaseVisitType;