@kenyaemr/esm-service-queues-app 7.0.2-pre.65

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 (289) hide show
  1. package/.turbo/turbo-build.log +40 -0
  2. package/README.md +20 -0
  3. package/dist/130.js +2 -0
  4. package/dist/130.js.LICENSE.txt +3 -0
  5. package/dist/130.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/169.js +1 -0
  9. package/dist/169.js.map +1 -0
  10. package/dist/185.js +1 -0
  11. package/dist/185.js.map +1 -0
  12. package/dist/233.js +1 -0
  13. package/dist/233.js.map +1 -0
  14. package/dist/237.js +1 -0
  15. package/dist/237.js.map +1 -0
  16. package/dist/255.js +2 -0
  17. package/dist/255.js.LICENSE.txt +9 -0
  18. package/dist/255.js.map +1 -0
  19. package/dist/271.js +1 -0
  20. package/dist/276.js +1 -0
  21. package/dist/276.js.map +1 -0
  22. package/dist/303.js +1 -0
  23. package/dist/303.js.map +1 -0
  24. package/dist/319.js +1 -0
  25. package/dist/401.js +1 -0
  26. package/dist/401.js.map +1 -0
  27. package/dist/430.js +2 -0
  28. package/dist/430.js.LICENSE.txt +50 -0
  29. package/dist/430.js.map +1 -0
  30. package/dist/460.js +1 -0
  31. package/dist/501.js +1 -0
  32. package/dist/501.js.map +1 -0
  33. package/dist/574.js +1 -0
  34. package/dist/591.js +2 -0
  35. package/dist/591.js.LICENSE.txt +32 -0
  36. package/dist/591.js.map +1 -0
  37. package/dist/644.js +1 -0
  38. package/dist/647.js +1 -0
  39. package/dist/647.js.map +1 -0
  40. package/dist/650.js +1 -0
  41. package/dist/650.js.map +1 -0
  42. package/dist/669.js +1 -0
  43. package/dist/669.js.map +1 -0
  44. package/dist/696.js +1 -0
  45. package/dist/696.js.map +1 -0
  46. package/dist/703.js +1 -0
  47. package/dist/703.js.map +1 -0
  48. package/dist/729.js +1 -0
  49. package/dist/729.js.map +1 -0
  50. package/dist/738.js +1 -0
  51. package/dist/738.js.map +1 -0
  52. package/dist/757.js +1 -0
  53. package/dist/764.js +1 -0
  54. package/dist/764.js.map +1 -0
  55. package/dist/784.js +2 -0
  56. package/dist/784.js.LICENSE.txt +9 -0
  57. package/dist/784.js.map +1 -0
  58. package/dist/788.js +1 -0
  59. package/dist/806.js +1 -0
  60. package/dist/806.js.map +1 -0
  61. package/dist/807.js +1 -0
  62. package/dist/833.js +1 -0
  63. package/dist/877.js +1 -0
  64. package/dist/877.js.map +1 -0
  65. package/dist/917.js +1 -0
  66. package/dist/917.js.map +1 -0
  67. package/dist/940.js +1 -0
  68. package/dist/940.js.map +1 -0
  69. package/dist/981.js +1 -0
  70. package/dist/981.js.map +1 -0
  71. package/dist/kenyaemr-esm-service-queues-app.js +1 -0
  72. package/dist/kenyaemr-esm-service-queues-app.js.buildmanifest.json +965 -0
  73. package/dist/kenyaemr-esm-service-queues-app.js.map +1 -0
  74. package/dist/main.js +2 -0
  75. package/dist/main.js.LICENSE.txt +60 -0
  76. package/dist/main.js.map +1 -0
  77. package/dist/routes.json +1 -0
  78. package/jest.config.js +3 -0
  79. package/package.json +54 -0
  80. package/src/active-visits/active-visits-row-actions.component.tsx +25 -0
  81. package/src/active-visits/active-visits-row-actions.scss +4 -0
  82. package/src/active-visits/active-visits-table.resource.ts +273 -0
  83. package/src/active-visits/change-status-dialog.component.tsx +272 -0
  84. package/src/active-visits/change-status-dialog.scss +47 -0
  85. package/src/active-visits/change-status-dialog.test.tsx +154 -0
  86. package/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx +228 -0
  87. package/src/add-patient-toqueue/add-patient-toqueue-dialog.scss +32 -0
  88. package/src/add-provider-queue-room/add-provider-queue-room.component.tsx +238 -0
  89. package/src/add-provider-queue-room/add-provider-queue-room.resource.ts +76 -0
  90. package/src/add-provider-queue-room/add-provider-queue-room.scss +17 -0
  91. package/src/add-provider-queue-room/add-provider-queue-room.test.tsx +105 -0
  92. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx +76 -0
  93. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.ts +7 -0
  94. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss +8 -0
  95. package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx +43 -0
  96. package/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx +43 -0
  97. package/src/config-schema.ts +465 -0
  98. package/src/constants.ts +10 -0
  99. package/src/createDashboardLink.component.tsx +39 -0
  100. package/src/current-visit/current-visit-summary.component.tsx +38 -0
  101. package/src/current-visit/current-visit-summary.test.tsx +43 -0
  102. package/src/current-visit/current-visit.resource.ts +84 -0
  103. package/src/current-visit/current-visit.scss +34 -0
  104. package/src/current-visit/hooks/useVitalsConceptMetadata.tsx +101 -0
  105. package/src/current-visit/visit-details/biometrics-config-schema.ts +14 -0
  106. package/src/current-visit/visit-details/current-visit-details.component.tsx +96 -0
  107. package/src/current-visit/visit-details/triage-note.component.tsx +53 -0
  108. package/src/current-visit/visit-details/triage-note.scss +89 -0
  109. package/src/current-visit/visit-details/vitals-config-schema.ts +17 -0
  110. package/src/current-visit/visit-details/vitals.component.tsx +165 -0
  111. package/src/dashboard.meta.ts +5 -0
  112. package/src/declarations.d.ts +4 -0
  113. package/src/helpers/functions.ts +28 -0
  114. package/src/helpers/helpers.ts +177 -0
  115. package/src/helpers/time-helpers.ts +15 -0
  116. package/src/home.component.tsx +16 -0
  117. package/src/home.test.tsx +48 -0
  118. package/src/hooks/useConcept.ts +13 -0
  119. package/src/hooks/useQueue.ts +10 -0
  120. package/src/hooks/useQueueEntries.ts +187 -0
  121. package/src/hooks/useQueues.ts +22 -0
  122. package/src/hooks/useSystemSetting.ts +18 -0
  123. package/src/index.ts +172 -0
  124. package/src/past-visit/past-visit-details/encounter-list.component.tsx +54 -0
  125. package/src/past-visit/past-visit-details/medications-list.component.tsx +98 -0
  126. package/src/past-visit/past-visit-details/notes-list.component.tsx +41 -0
  127. package/src/past-visit/past-visit-details/past-visit-summary.component.tsx +181 -0
  128. package/src/past-visit/past-visit-details/past-visit-summary.scss +58 -0
  129. package/src/past-visit/past-visit.component.tsx +37 -0
  130. package/src/past-visit/past-visit.resource.ts +83 -0
  131. package/src/past-visit/past-visit.scss +126 -0
  132. package/src/past-visit/past-visit.test.tsx +41 -0
  133. package/src/patient-info/appointment-details.component.tsx +98 -0
  134. package/src/patient-info/appointment-details.scss +34 -0
  135. package/src/patient-info/appointment-details.test.tsx +36 -0
  136. package/src/patient-info/appointments.resource.ts +43 -0
  137. package/src/patient-info/hooks/usePatientAttributes.tsx +42 -0
  138. package/src/patient-info/patient-info.component.tsx +82 -0
  139. package/src/patient-info/patient-info.scss +60 -0
  140. package/src/patient-info/patient-info.test.tsx +43 -0
  141. package/src/patient-queue-header/patient-queue-header.component.tsx +99 -0
  142. package/src/patient-queue-header/patient-queue-header.scss +90 -0
  143. package/src/patient-queue-header/patient-queue-illustration.component.tsx +22 -0
  144. package/src/patient-queue-metrics/clinic-metrics.component.tsx +98 -0
  145. package/src/patient-queue-metrics/clinic-metrics.resource.ts +58 -0
  146. package/src/patient-queue-metrics/clinic-metrics.scss +11 -0
  147. package/src/patient-queue-metrics/clinic-metrics.test.tsx +76 -0
  148. package/src/patient-queue-metrics/metrics-card.component.tsx +68 -0
  149. package/src/patient-queue-metrics/metrics-card.scss +80 -0
  150. package/src/patient-queue-metrics/metrics-header.component.tsx +61 -0
  151. package/src/patient-queue-metrics/metrics-header.scss +26 -0
  152. package/src/patient-queue-metrics/queue-metrics.resource.ts +42 -0
  153. package/src/patient-search/advanced-search.component.tsx +191 -0
  154. package/src/patient-search/advanced-search.scss +154 -0
  155. package/src/patient-search/advanced-search.test.tsx +29 -0
  156. package/src/patient-search/basic-search.component.tsx +112 -0
  157. package/src/patient-search/basic-search.scss +139 -0
  158. package/src/patient-search/basic-search.test.tsx +23 -0
  159. package/src/patient-search/empty-data-illustration.component.tsx +41 -0
  160. package/src/patient-search/hooks/useActivePatientEnrollment.tsx +29 -0
  161. package/src/patient-search/hooks/useDefaultLocation.ts +14 -0
  162. package/src/patient-search/hooks/usePatients.tsx +25 -0
  163. package/src/patient-search/hooks/useQueueLocations.tsx +23 -0
  164. package/src/patient-search/hooks/useRecommendedVisitTypes.tsx +35 -0
  165. package/src/patient-search/hooks/useScheduledVisits.ts +52 -0
  166. package/src/patient-search/patient-scheduled-visits.component.tsx +324 -0
  167. package/src/patient-search/patient-scheduled-visits.scss +131 -0
  168. package/src/patient-search/patient-scheduled-visits.test.tsx +44 -0
  169. package/src/patient-search/patient-search.scss +43 -0
  170. package/src/patient-search/patient-search.workspace.tsx +135 -0
  171. package/src/patient-search/search-illustration.component.tsx +27 -0
  172. package/src/patient-search/search-results.component.tsx +75 -0
  173. package/src/patient-search/search-results.scss +80 -0
  174. package/src/patient-search/search-results.test.tsx +77 -0
  175. package/src/patient-search/search.resource.ts +10 -0
  176. package/src/patient-search/visit-form/existing-visit-form.component.tsx +112 -0
  177. package/src/patient-search/visit-form/queue.resource.ts +64 -0
  178. package/src/patient-search/visit-form/visit-form.component.tsx +344 -0
  179. package/src/patient-search/visit-form/visit-form.scss +73 -0
  180. package/src/patient-search/visit-form/visit-type-selector.component.tsx +155 -0
  181. package/src/patient-search/visit-form/visit-type-selector.scss +100 -0
  182. package/src/patient-search/visit-form/visit-type-selector.test.tsx +83 -0
  183. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx +178 -0
  184. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.scss +19 -0
  185. package/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx +63 -0
  186. package/src/queue-entry-table-components/edit-entry.scss +14 -0
  187. package/src/queue-entry-table-components/queue-duration.component.tsx +41 -0
  188. package/src/queue-entry-table-components/queue-priority.component.tsx +38 -0
  189. package/src/queue-entry-table-components/queue-priority.scss +12 -0
  190. package/src/queue-entry-table-components/queue-status.component.tsx +39 -0
  191. package/src/queue-entry-table-components/transition-entry.component.tsx +55 -0
  192. package/src/queue-entry-table-components/transition-entry.scss +22 -0
  193. package/src/queue-patient-linelists/queue-linelist-base-table.component.tsx +241 -0
  194. package/src/queue-patient-linelists/queue-linelist-base-table.scss +110 -0
  195. package/src/queue-patient-linelists/queue-linelist-base-table.test.tsx +93 -0
  196. package/src/queue-patient-linelists/queue-linelist-filter.scss +63 -0
  197. package/src/queue-patient-linelists/queue-linelist-filter.test.tsx +94 -0
  198. package/src/queue-patient-linelists/queue-linelist-filter.workspace.tsx +185 -0
  199. package/src/queue-patient-linelists/queue-linelist.resource.ts +84 -0
  200. package/src/queue-patient-linelists/queue-services-table.component.tsx +63 -0
  201. package/src/queue-patient-linelists/scheduled-appointments-table.component.tsx +305 -0
  202. package/src/queue-patient-linelists/scheduled-appointments-table.test.tsx +41 -0
  203. package/src/queue-rooms/queue-room-form.scss +56 -0
  204. package/src/queue-rooms/queue-room-form.test.tsx +80 -0
  205. package/src/queue-rooms/queue-room-form.workspace.tsx +169 -0
  206. package/src/queue-rooms/queue-room.resource.ts +20 -0
  207. package/src/queue-screen/queue-screen.component.tsx +47 -0
  208. package/src/queue-screen/queue-screen.scss +39 -0
  209. package/src/queue-screen/queue-screen.test.tsx +51 -0
  210. package/src/queue-screen/useActiveTickets.tsx +13 -0
  211. package/src/queue-services/queue-service-form.scss +61 -0
  212. package/src/queue-services/queue-service-form.test.tsx +60 -0
  213. package/src/queue-services/queue-service-form.workspace.tsx +179 -0
  214. package/src/queue-services/queue-service.resource.ts +33 -0
  215. package/src/queue-table/cells/columns.resource.ts +135 -0
  216. package/src/queue-table/cells/queue-table-action-cell.component.tsx +88 -0
  217. package/src/queue-table/cells/queue-table-action-cell.scss +7 -0
  218. package/src/queue-table/cells/queue-table-coming-from-cell.component.tsx +13 -0
  219. package/src/queue-table/cells/queue-table-extension-cell.component.tsx +16 -0
  220. package/src/queue-table/cells/queue-table-name-cell.component.tsx +20 -0
  221. package/src/queue-table/cells/queue-table-patient-age-cell.component.tsx +18 -0
  222. package/src/queue-table/cells/queue-table-patient-identifier-cell.component.tsx +25 -0
  223. package/src/queue-table/cells/queue-table-priority-cell.component.tsx +23 -0
  224. package/src/queue-table/cells/queue-table-queue-name-cell.component.tsx +14 -0
  225. package/src/queue-table/cells/queue-table-status-cell.component.tsx +18 -0
  226. package/src/queue-table/cells/queue-table-visit-attribute-queue-number-cell.component.tsx +37 -0
  227. package/src/queue-table/cells/queue-table-visit-start-time-cell.component.tsx +20 -0
  228. package/src/queue-table/cells/queue-table-wait-time-cell.component.tsx +17 -0
  229. package/src/queue-table/default-queue-table.component.tsx +174 -0
  230. package/src/queue-table/default-queue-table.test.tsx +131 -0
  231. package/src/queue-table/queue-entry-actions/edit-queue-entry-modal.component.tsx +52 -0
  232. package/src/queue-table/queue-entry-actions/end-queue-entry-modal.component.tsx +39 -0
  233. package/src/queue-table/queue-entry-actions/queue-entry-actions-modal.component.tsx +362 -0
  234. package/src/queue-table/queue-entry-actions/queue-entry-actions-modal.test.tsx +152 -0
  235. package/src/queue-table/queue-entry-actions/queue-entry-actions.resource.ts +83 -0
  236. package/src/queue-table/queue-entry-actions/queue-entry-actons-modal.scss +36 -0
  237. package/src/queue-table/queue-entry-actions/queue-entry-confirm-action-modal.component.tsx +97 -0
  238. package/src/queue-table/queue-entry-actions/queue-entry-confirm-action-modal.test.tsx +106 -0
  239. package/src/queue-table/queue-entry-actions/queue-entry-undo-actions-modal.test.tsx +76 -0
  240. package/src/queue-table/queue-entry-actions/transition-queue-entry-modal.component.tsx +51 -0
  241. package/src/queue-table/queue-entry-actions/undo-transition-queue-entry-modal.component.tsx +58 -0
  242. package/src/queue-table/queue-entry-actions/void-queue-entry-modal.component.tsx +34 -0
  243. package/src/queue-table/queue-table-by-status-menu.component.tsx +42 -0
  244. package/src/queue-table/queue-table-by-status-menu.scss +11 -0
  245. package/src/queue-table/queue-table-by-status-skeleton.component.tsx +32 -0
  246. package/src/queue-table/queue-table-by-status.component.tsx +96 -0
  247. package/src/queue-table/queue-table-expanded-row.component.tsx +29 -0
  248. package/src/queue-table/queue-table-metrics-card.component.tsx +50 -0
  249. package/src/queue-table/queue-table-metrics-card.scss +48 -0
  250. package/src/queue-table/queue-table-metrics.component.tsx +30 -0
  251. package/src/queue-table/queue-table-metrics.scss +11 -0
  252. package/src/queue-table/queue-table.component.tsx +179 -0
  253. package/src/queue-table/queue-table.scss +192 -0
  254. package/src/queue-table/queue-table.test.tsx +210 -0
  255. package/src/remove-queue-entry-dialog/remove-queue-entry.component.tsx +87 -0
  256. package/src/remove-queue-entry-dialog/remove-queue-entry.resource.ts +93 -0
  257. package/src/remove-queue-entry-dialog/remove-queue-entry.scss +7 -0
  258. package/src/remove-queue-entry-dialog/remove-queue-entry.test.tsx +45 -0
  259. package/src/root.component.tsx +28 -0
  260. package/src/root.scss +15 -0
  261. package/src/root.test.tsx +24 -0
  262. package/src/routes.json +133 -0
  263. package/src/side-menu/nav-group/createNavGroup.tsx +17 -0
  264. package/src/side-menu/nav-group/nav-group.component.tsx +24 -0
  265. package/src/side-menu/nav-group/nav-group.test.tsx +32 -0
  266. package/src/side-menu/nav-group/nav-group.ts +10 -0
  267. package/src/side-menu/side-menu.component.tsx +6 -0
  268. package/src/side-menu/side-menu.test.tsx +17 -0
  269. package/src/transition-queue-entry/transition-queue-entry-dialog.component.tsx +134 -0
  270. package/src/transition-queue-entry/transition-queue-entry-dialog.scss +12 -0
  271. package/src/transition-queue-entry/transition-queue-entry-dialog.test.tsx +102 -0
  272. package/src/transition-queue-entry/transition-queue-entry.resource.ts +16 -0
  273. package/src/types/index.ts +494 -0
  274. package/src/views/queue-table-by-status-view.component.tsx +25 -0
  275. package/src/views/queue-tables-for-all-statuses.component.tsx +150 -0
  276. package/src/visits-missing-inqueue/visits-missing-inqueue.component.tsx +277 -0
  277. package/src/visits-missing-inqueue/visits-missing-inqueue.resource.ts +93 -0
  278. package/src/visits-missing-inqueue/visits-missing-inqueue.scss +108 -0
  279. package/translations/am.json +295 -0
  280. package/translations/ar.json +295 -0
  281. package/translations/en.json +305 -0
  282. package/translations/es.json +295 -0
  283. package/translations/fr.json +295 -0
  284. package/translations/he.json +295 -0
  285. package/translations/km.json +295 -0
  286. package/translations/zh.json +295 -0
  287. package/translations/zh_CN.json +295 -0
  288. package/tsconfig.json +5 -0
  289. package/webpack.config.js +1 -0
@@ -0,0 +1,465 @@
1
+ import { Type, validator, validators } from '@openmrs/esm-framework';
2
+ import vitalsConfigSchema, { type VitalsConfigObject } from './current-visit/visit-details/vitals-config-schema';
3
+ import biometricsConfigSchema, {
4
+ type BiometricsConfigObject,
5
+ } from './current-visit/visit-details/biometrics-config-schema';
6
+
7
+ const columnTypes = [
8
+ 'actions',
9
+ 'coming-from',
10
+ 'extension',
11
+ 'patient-age',
12
+ 'patient-identifier',
13
+ 'patient-name',
14
+ 'priority',
15
+ 'queue-number',
16
+ 'queue',
17
+ 'status',
18
+ 'visit-start-time',
19
+ 'wait-time',
20
+ ] as const;
21
+ type ColumnType = (typeof columnTypes)[number];
22
+
23
+ const statusIcons = ['Group', 'InProgress'] as const;
24
+ type StatusIcon = (typeof statusIcons)[number];
25
+
26
+ // Options from https://react.carbondesignsystem.com/?path=/docs/components-tag--overview
27
+ const carbonTagColors = [
28
+ 'red',
29
+ 'magenta',
30
+ 'purple',
31
+ 'blue',
32
+ 'teal',
33
+ 'cyan',
34
+ 'gray',
35
+ 'green',
36
+ 'warm-gray',
37
+ 'cool-gray',
38
+ 'high-contrast',
39
+ 'outline',
40
+ ] as const;
41
+ type CarbonTagColor = (typeof carbonTagColors)[number];
42
+
43
+ const tagStyles = ['bold'] as const;
44
+ type TagStyle = (typeof tagStyles)[number];
45
+
46
+ // equal to columnTypes but without extension
47
+ export const builtInColumns = columnTypes.filter((columnType) => columnType !== 'extension');
48
+ const defaultIdentifierTypeUuid = '05a29f94-c0ed-11e2-94be-8c13b969e334'; // OpenMRS ID
49
+
50
+ export const defaultColumnConfig: ColumnConfig = {
51
+ identifierTypeUuid: defaultIdentifierTypeUuid,
52
+ priorityConfigs: [],
53
+ statusConfigs: [],
54
+ visitQueueNumberAttributeUuid: null,
55
+ };
56
+
57
+ export const defaultQueueTable: TableDefinitions = {
58
+ columns: ['patient-name', 'coming-from', 'priority', 'status', 'queue', 'wait-time', 'actions'],
59
+ appliedTo: [{ queue: null, status: null }],
60
+ };
61
+
62
+ export const configSchema = {
63
+ concepts: {
64
+ defaultPriorityConceptUuid: {
65
+ _type: Type.ConceptUuid,
66
+ _description: 'The UUID of the default priority for the queues eg Not urgent.',
67
+ _default: 'f4620bfa-3625-4883-bd3f-84c2cce14470',
68
+ },
69
+ emergencyPriorityConceptUuid: {
70
+ _type: Type.ConceptUuid,
71
+ _description: 'The UUID of the priority with the highest sort weight for the queues eg Emergency.',
72
+ _default: '04f6f7e0-e3cb-4e13-a133-4479f759574e',
73
+ },
74
+ defaultStatusConceptUuid: {
75
+ _type: Type.ConceptUuid,
76
+ _description: 'The UUID of the default status for the queues eg Waiting.',
77
+ _default: '51ae5e4d-b72b-4912-bf31-a17efb690aeb',
78
+ },
79
+ defaultTransitionStatus: {
80
+ _type: Type.ConceptUuid,
81
+ _description: 'The UUID of the default status for attending a service in the queues eg In Service.',
82
+ _default: 'ca7494ae-437f-4fd0-8aae-b88b9a2ba47d',
83
+ },
84
+ systolicBloodPressureUuid: {
85
+ _type: Type.ConceptUuid,
86
+ _default: '5085AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
87
+ },
88
+ diastolicBloodPressureUuid: {
89
+ _type: Type.ConceptUuid,
90
+ _default: '5086AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
91
+ },
92
+ pulseUuid: {
93
+ _type: Type.ConceptUuid,
94
+ _default: '5087AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
95
+ },
96
+ temperatureUuid: {
97
+ _type: Type.ConceptUuid,
98
+ _default: '5088AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
99
+ },
100
+ oxygenSaturationUuid: {
101
+ _type: Type.ConceptUuid,
102
+ _default: '5092AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
103
+ },
104
+ heightUuid: {
105
+ _type: Type.ConceptUuid,
106
+ _default: '5090AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
107
+ },
108
+ weightUuid: {
109
+ _type: Type.ConceptUuid,
110
+ _default: '5089AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
111
+ },
112
+ respiratoryRateUuid: {
113
+ _type: Type.ConceptUuid,
114
+ _default: '5242AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
115
+ },
116
+ historicalObsConceptUuid: {
117
+ _type: Type.Array,
118
+ _description: 'The Uuids of the obs that are displayed on the previous visit modal',
119
+ _default: ['161643AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
120
+ },
121
+ },
122
+ contactAttributeType: {
123
+ _type: Type.UUID,
124
+ _description:
125
+ 'The UUID of the person attribute type that captures contact information such as `Next of kin contact details`',
126
+ _default: '',
127
+ },
128
+ vitals: vitalsConfigSchema,
129
+ biometrics: biometricsConfigSchema,
130
+ appointmentStatuses: {
131
+ _type: Type.Array,
132
+ _description: 'Configurable appointment status (status of appointments)',
133
+ _default: ['Requested', 'Scheduled', 'CheckedIn', 'Completed', 'Cancelled', 'Missed'],
134
+ },
135
+ defaultIdentifierTypes: {
136
+ _type: Type.Array,
137
+ _element: {
138
+ _type: Type.String,
139
+ },
140
+ _description: 'The identifier types to be display on all patient search result page',
141
+ _default: ['05ee9cf4-7242-4a17-b4d4-00f707265c8a', 'f85081e2-b4be-4e48-b3a4-7994b69bb101'],
142
+ },
143
+ showRecommendedVisitTypeTab: {
144
+ _type: Type.Boolean,
145
+ _description: 'Whether start visit form should display recommended visit type tab. Requires `visitTypeResourceUrl`',
146
+ _default: false,
147
+ },
148
+ visitTypeResourceUrl: {
149
+ _type: Type.String,
150
+ _description: 'The `visitTypeResourceUrl`',
151
+ _default: null,
152
+ },
153
+ customPatientChartUrl: {
154
+ _type: Type.String,
155
+ _default: '${openmrsSpaBase}/patient/${patientUuid}/chart',
156
+ _description: `Template URL that will be used when clicking on the patient name in the queues table.
157
+ Available arguments: patientUuid, openmrsSpaBase, openBase
158
+ (openmrsSpaBase and openBase are available to any <ConfigurableLink>)`,
159
+ _validators: [validators.isUrlWithTemplateParameters(['patientUuid'])],
160
+ },
161
+ defaultFacilityUrl: {
162
+ _type: Type.String,
163
+ _default: '',
164
+ _description: 'Custom URL to load default facility if it is not in the session',
165
+ },
166
+ visitQueueNumberAttributeUuid: {
167
+ _type: Type.String,
168
+ _description: 'The UUID of the visit attribute that contains the visit queue number.',
169
+ _default: null,
170
+ },
171
+ queueTables: {
172
+ columnDefinitions: {
173
+ _type: Type.Array,
174
+ _default: [],
175
+ _description:
176
+ "Custom columns for queue tables can be defined here. These columns will be referenced by their `id` in the `tableDefinitions` columns configuration. If the provided `id` matches a built-in column, the custom configuration will override the built-in column's configuration.",
177
+ _elements: {
178
+ _validators: [
179
+ validator(
180
+ (columnDfn: ColumnDefinition) =>
181
+ Boolean(columnDfn.columnType || columnTypes.some((c) => c == columnDfn.id)),
182
+ (columnDfn) =>
183
+ `No columnType provided for column with ID '${
184
+ columnDfn.id
185
+ }', and the ID is not a valid columnType. Valid column types are: ${columnTypes.join(', ')}.`,
186
+ ),
187
+ validator(
188
+ (columnDfn: ColumnDefinition) => {
189
+ return (
190
+ columnDfn.config.identifierTypeUuid == defaultIdentifierTypeUuid ||
191
+ columnHasType(columnDfn, 'patient-identifier')
192
+ );
193
+ },
194
+ (columnDfn) => {
195
+ return `Identifier type can only be set for 'patient-identifier' column type. Column ${
196
+ columnDfn.id
197
+ } has type '${columnDfn.columnType ?? columnDfn.id}.`;
198
+ },
199
+ ),
200
+ validator(
201
+ (columnDfn: ColumnDefinition) => {
202
+ return (
203
+ !columnDfn.config.priorityConfigs ||
204
+ columnDfn.config.priorityConfigs.length == 0 ||
205
+ columnHasType(columnDfn, 'priority')
206
+ );
207
+ },
208
+ (columnDfn) => {
209
+ return `Priorities can only be configured for 'priority' column type. Column ${columnDfn.id} has type '${
210
+ columnDfn.columnType ?? columnDfn.id
211
+ }.`;
212
+ },
213
+ ),
214
+ validator(
215
+ (columnDfn: ColumnDefinition) => {
216
+ return (
217
+ !columnDfn.config.statusConfigs ||
218
+ columnDfn.config.statusConfigs.length == 0 ||
219
+ columnHasType(columnDfn, 'status')
220
+ );
221
+ },
222
+ (columnDfn) => {
223
+ return `Statuses can only be configured for 'status' column type. Column ${columnDfn.id} has type '${
224
+ columnDfn.columnType ?? columnDfn.id
225
+ }.`;
226
+ },
227
+ ),
228
+ ],
229
+ id: {
230
+ _type: Type.String,
231
+ _description: 'The unique identifier for the column you are defining',
232
+ },
233
+ columnType: {
234
+ _type: Type.String,
235
+ _description: 'The type of column, if different from the ID',
236
+ _validators: [validators.oneOf(columnTypes)],
237
+ _default: null,
238
+ },
239
+ header: {
240
+ _type: Type.String,
241
+ _description:
242
+ 'The header text for the column. Will be translated if it is a valid translation key. If not provided, the header will be based on the columnType.',
243
+ _default: null,
244
+ },
245
+ headerI18nModule: {
246
+ _type: Type.String,
247
+ _description: 'The module to use for translation of the header.',
248
+ _default: '@openmrs/esm-service-queues-app',
249
+ },
250
+ config: {
251
+ identifierTypeUuid: {
252
+ _type: Type.UUID,
253
+ _description: "For columnType 'patient-identifier'. The UUID of the identifier type to display",
254
+ _default: defaultIdentifierTypeUuid,
255
+ },
256
+ priorityConfigs: {
257
+ _type: Type.Array,
258
+ _default: [],
259
+ _description:
260
+ 'For columnType "priority". Add entries here to configure the styling for specific priority tags.',
261
+ _elements: {
262
+ conceptUuid: {
263
+ _type: Type.UUID,
264
+ _description: 'The UUID of the priority concept to configure',
265
+ },
266
+ color: {
267
+ _type: Type.String,
268
+ _description:
269
+ 'The color of the tag. This is based on the "type" field of the Carbon Design System "Tag" component.',
270
+ _validators: [validators.oneOf(carbonTagColors)],
271
+ _default: 'gray',
272
+ },
273
+ style: {
274
+ _type: Type.String,
275
+ _description: 'Style to apply to the tag',
276
+ _validators: [validators.oneOf(tagStyles)],
277
+ _default: null,
278
+ },
279
+ },
280
+ },
281
+ statusConfigs: {
282
+ _type: Type.Array,
283
+ _default: [],
284
+ _description: 'For columnType "status". Configures the icons for each status.',
285
+ _elements: {
286
+ conceptUuid: {
287
+ _type: Type.UUID,
288
+ _description: 'The UUID of the status concept to configure',
289
+ },
290
+ iconComponent: {
291
+ _type: Type.String,
292
+ _description: 'The icon component to display for the status',
293
+ _validators: [validators.oneOf(statusIcons)],
294
+ _default: null,
295
+ },
296
+ },
297
+ visitQueueNumberAttributeUuid: {
298
+ _type: Type.String,
299
+ _description:
300
+ 'The UUID of the visit attribute that contains the visit queue number. This must be set to use the queue-number column if the top-level `visitQueueNumberAttributeUuid` config element is not set.',
301
+ _default: null,
302
+ },
303
+ },
304
+ },
305
+ },
306
+ },
307
+ tableDefinitions: {
308
+ _type: Type.Array,
309
+ _default: [defaultQueueTable],
310
+ _elements: {
311
+ columns: {
312
+ _type: Type.Array,
313
+ _elements: {
314
+ _type: Type.String,
315
+ },
316
+ },
317
+ appliedTo: {
318
+ _type: Type.Array,
319
+ _elements: {
320
+ queue: {
321
+ _type: Type.String,
322
+ _description: 'The UUID of the queue. If not provided, applies to all queues.',
323
+ _default: null,
324
+ },
325
+ status: {
326
+ _type: Type.String,
327
+ _description: 'The UUID of the status. If not provided, applies to all statuses.',
328
+ _default: null,
329
+ },
330
+ },
331
+ },
332
+ },
333
+ },
334
+ _validators: [
335
+ validator(
336
+ (queueConfig: TablesConfig) => {
337
+ const validColumnIds = [...builtInColumns, ...queueConfig.columnDefinitions.map((colDef) => colDef.id)];
338
+ return queueConfig.tableDefinitions.every((t) => t.columns.every((c) => validColumnIds.includes(c)));
339
+ },
340
+ (queueConfig: TablesConfig) => {
341
+ const validColumnIds = new Set([
342
+ ...builtInColumns,
343
+ ...queueConfig.columnDefinitions.map((colDef) => colDef.id),
344
+ ]);
345
+ return `A table definition references a column that is not defined. You can define additional columns in 'columnDefinitions'.\n\nCurrently defined column IDs are ${[
346
+ ...validColumnIds,
347
+ ].join(', ')}.\n\nInvalid columns: ${queueConfig.tableDefinitions
348
+ .map((t) => t.columns.filter((c) => !validColumnIds.has(c)))
349
+ .flat()
350
+ .join(', ')}`;
351
+ },
352
+ ),
353
+ ],
354
+ },
355
+ _validators: [
356
+ validator((config: ConfigObject) => {
357
+ const queueNumberColumnDefs = [
358
+ ...config.queueTables.columnDefinitions.filter((colDef) => columnHasType(colDef, 'queue-number')),
359
+ { id: 'queue-number', config: defaultColumnConfig },
360
+ ];
361
+ const queueNumberColumnsUsed = config.queueTables.tableDefinitions
362
+ .map((tableDef) => tableDef.columns)
363
+ .flat()
364
+ .filter((col) => queueNumberColumnDefs.map((d) => d.id).includes(col));
365
+ return Boolean(
366
+ config.visitQueueNumberAttributeUuid ||
367
+ queueNumberColumnsUsed.every(
368
+ (columnId) => queueNumberColumnDefs.find((d) => d.id == columnId).config.visitQueueNumberAttributeUuid,
369
+ ),
370
+ );
371
+ }, 'If a queue-number column is used in a table definition, the `visitQueueNumberAttributeUuid` must be set either at the top-level config or in the column definition.'),
372
+ ],
373
+ };
374
+
375
+ function columnHasType(columnDef: ColumnDefinition, type: ColumnType): boolean {
376
+ return columnDef.columnType === type || columnDef.id === type;
377
+ }
378
+
379
+ export interface ConfigObject {
380
+ concepts: {
381
+ defaultPriorityConceptUuid: string;
382
+ defaultStatusConceptUuid: string;
383
+ defaultTransitionStatus: string;
384
+ systolicBloodPressureUuid: string;
385
+ diastolicBloodPressureUuid: string;
386
+ pulseUuid: string;
387
+ temperatureUuid: string;
388
+ oxygenSaturationUuid: string;
389
+ heightUuid: string;
390
+ weightUuid: string;
391
+ respiratoryRateUuid: string;
392
+ emergencyPriorityConceptUuid: string;
393
+ historicalObsConceptUuid: Array<string>;
394
+ };
395
+ contactAttributeType: Array<string>;
396
+ vitals: VitalsConfigObject;
397
+ biometrics: BiometricsConfigObject;
398
+ appointmentStatuses: Array<string>;
399
+ defaultIdentifierTypes: Array<string>;
400
+ showRecommendedVisitTypeTab: boolean;
401
+ customPatientChartUrl: string;
402
+ visitTypeResourceUrl: string;
403
+ visitQueueNumberAttributeUuid: string | null;
404
+ queueTables: TablesConfig;
405
+ }
406
+
407
+ interface TablesConfig {
408
+ columnDefinitions: ColumnDefinition[];
409
+
410
+ /*
411
+ A list of table definitions. A queue table (whether it is displaying entries from a
412
+ particular queue+status combination, from a particular queue, or from multiple queues)
413
+ will determine what columns to show based on these definitions. If multiple TableDefinitions
414
+ have matching appliedTo criteria, the first one will be used.
415
+ */
416
+ tableDefinitions: TableDefinitions[];
417
+ }
418
+
419
+ export type ColumnDefinition = {
420
+ id: string;
421
+ columnType?: ColumnType;
422
+ header?: string;
423
+ headerI18nModule?: string;
424
+ config: ColumnConfig;
425
+ };
426
+
427
+ export interface VisitAttributeQueueNumberColumnConfig {
428
+ visitQueueNumberAttributeUuid: string;
429
+ }
430
+
431
+ export interface PatientIdentifierColumnConfig {
432
+ identifierTypeUuid: string; // uuid of the identifier type
433
+ }
434
+ export interface PriorityConfig {
435
+ conceptUuid: string;
436
+ color: CarbonTagColor;
437
+ style: TagStyle;
438
+ }
439
+
440
+ export interface PriorityColumnConfig {
441
+ priorityConfigs: PriorityConfig[];
442
+ }
443
+
444
+ export interface StatusConfig {
445
+ conceptUuid: string;
446
+ iconComponent: StatusIcon;
447
+ }
448
+
449
+ export interface StatusColumnConfig {
450
+ statusConfigs: StatusConfig[];
451
+ }
452
+
453
+ export type ColumnConfig = PatientIdentifierColumnConfig &
454
+ PriorityColumnConfig &
455
+ StatusColumnConfig &
456
+ VisitAttributeQueueNumberColumnConfig;
457
+
458
+ export interface TableDefinitions {
459
+ // Column IDs defined either in columnDefinitions or in builtInColumns
460
+ columns: Array<string>;
461
+
462
+ // apply the columns to tables of any of the specified queue and status
463
+ // (if appliedTo is null, apply to all tables, including the one in the service queue app home page)
464
+ appliedTo?: Array<{ queue?: string; status?: string }>;
465
+ }
@@ -0,0 +1,10 @@
1
+ import dayjs from 'dayjs';
2
+ export const spaRoot = window['getOpenmrsSpaBase'];
3
+ export const spaBasePath = `${window.spaBase}/home`;
4
+ export const omrsDateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZZ';
5
+ export const startOfDay = dayjs(new Date().setUTCHours(0, 0, 0, 0)).format(omrsDateFormat);
6
+ export const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
7
+
8
+ export const datePickerPlaceHolder = 'dd/mm/yyyy';
9
+ export const datePickerFormat = 'd/m/Y';
10
+ export const time12HourFormatRegexPattern = '^(1[0-2]|0?[1-9]):[0-5][0-9]$';
@@ -0,0 +1,39 @@
1
+ import React, { useMemo } from 'react';
2
+ import classNames from 'classnames';
3
+ import { BrowserRouter, useLocation } from 'react-router-dom';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { ConfigurableLink } from '@openmrs/esm-framework';
6
+
7
+ export interface DashboardLinkConfig {
8
+ name: string;
9
+ title: string;
10
+ }
11
+
12
+ function DashboardExtension({ dashboardLinkConfig }: { dashboardLinkConfig: DashboardLinkConfig }) {
13
+ const { t } = useTranslation();
14
+ const { name, title } = dashboardLinkConfig;
15
+ const location = useLocation();
16
+ const spaBasePath = `${window.spaBase}/home`;
17
+
18
+ const navLink = useMemo(() => {
19
+ const pathArray = location.pathname.split('/home');
20
+ const lastElement = pathArray[pathArray.length - 1];
21
+ return decodeURIComponent(lastElement);
22
+ }, [location.pathname]);
23
+
24
+ return (
25
+ <ConfigurableLink
26
+ className={classNames('cds--side-nav__link', {
27
+ 'active-left-nav-link': navLink.match(name),
28
+ })}
29
+ to={`${spaBasePath}/${name}`}>
30
+ {t('serviceQueues', 'Service queues')}
31
+ </ConfigurableLink>
32
+ );
33
+ }
34
+
35
+ export const createDashboardLink = (dashboardLinkConfig: DashboardLinkConfig) => () => (
36
+ <BrowserRouter>
37
+ <DashboardExtension dashboardLinkConfig={dashboardLinkConfig} />
38
+ </BrowserRouter>
39
+ );
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Tag, DataTableSkeleton } from '@carbon/react';
4
+ import CurrentVisitDetails from './visit-details/current-visit-details.component';
5
+ import { useVisit } from './current-visit.resource';
6
+ import styles from './current-visit.scss';
7
+
8
+ interface CurrentVisitProps {
9
+ patientUuid: string;
10
+ visitUuid: string;
11
+ }
12
+
13
+ const CurrentVisit: React.FC<CurrentVisitProps> = ({ patientUuid, visitUuid }) => {
14
+ const { t } = useTranslation();
15
+ const { visit, isError, isLoading } = useVisit(visitUuid);
16
+
17
+ if (isLoading) {
18
+ return <DataTableSkeleton role="progressbar" />;
19
+ }
20
+ if (visit) {
21
+ return (
22
+ <div className={styles.wrapper}>
23
+ <div>
24
+ <p className={styles.heading}>{visit?.visitType?.display}</p>
25
+ <div className={styles.subHeading}>
26
+ {t('scheduledToday', 'Scheduled for today')} <Tag type="blue"> {t('onTime', 'On time')}</Tag>
27
+ </div>
28
+ </div>
29
+
30
+ <div className={styles.visitContainer}>
31
+ <CurrentVisitDetails encounters={visit.encounters} patientUuid={patientUuid} />
32
+ </div>
33
+ </div>
34
+ );
35
+ }
36
+ };
37
+
38
+ export default CurrentVisit;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { useVisit } from './current-visit.resource';
4
+ import { mockPastVisit } from '__mocks__';
5
+ import CurrentVisit from './current-visit-summary.component';
6
+
7
+ const useVisitMock = useVisit as jest.Mock;
8
+
9
+ jest.mock('./current-visit.resource', () => ({
10
+ useVisit: jest.fn(() => ({
11
+ visit: {
12
+ visitType: { display: 'Visit Type' },
13
+ encounters: [],
14
+ },
15
+ isError: false,
16
+ isLoading: false,
17
+ })),
18
+ }));
19
+
20
+ const patientUuid = mockPastVisit.data.results[0].patient.uuid;
21
+ const visitUuid = mockPastVisit.data.results[0].uuid;
22
+
23
+ describe('CurrentVisit', () => {
24
+ it('renders visit details correctly', async () => {
25
+ render(<CurrentVisit patientUuid={patientUuid} visitUuid={visitUuid} />);
26
+
27
+ expect(screen.queryByRole('progressbar')).toBeNull();
28
+ expect(screen.getByText('Visit Type')).toBeInTheDocument();
29
+ expect(screen.getByText('Scheduled for today')).toBeInTheDocument();
30
+ expect(screen.getByText('On time')).toBeInTheDocument();
31
+ });
32
+ it('should render skeleton when loading', async () => {
33
+ useVisitMock.mockImplementationOnce(() => ({
34
+ visit: undefined,
35
+ isError: false,
36
+ isLoading: true,
37
+ }));
38
+
39
+ render(<CurrentVisit patientUuid={patientUuid} visitUuid={visitUuid} />);
40
+
41
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
42
+ });
43
+ });
@@ -0,0 +1,84 @@
1
+ import useSWR from 'swr';
2
+ import { openmrsFetch, restBaseUrl, type Visit } from '@openmrs/esm-framework';
3
+ import { type ConceptMetadata } from './hooks/useVitalsConceptMetadata';
4
+ import { type ObsMetaInfo } from '../types/index';
5
+
6
+ export function useVisit(visitUuid: string) {
7
+ const customRepresentation =
8
+ 'custom:(uuid,encounters:(uuid,encounterDatetime,' +
9
+ 'orders:(uuid,dateActivated,' +
10
+ 'drug:(uuid,name,strength),doseUnits:(uuid,display),' +
11
+ 'dose,route:(uuid,display),frequency:(uuid,display),' +
12
+ 'duration,durationUnits:(uuid,display),numRefills,' +
13
+ 'orderType:(uuid,display),orderer:(uuid,person:(uuid,display))),' +
14
+ 'obs:(uuid,concept:(uuid,display,conceptClass:(uuid,display)),' +
15
+ 'display,groupMembers:(uuid,concept:(uuid,display),' +
16
+ 'value:(uuid,display)),value),encounterType:(uuid,display),' +
17
+ 'encounterProviders:(uuid,display,encounterRole:(uuid,display),' +
18
+ 'provider:(uuid,person:(uuid,display)))),visitType:(uuid,name,display),startDatetime';
19
+
20
+ const apiUrl = `${restBaseUrl}/visit/${visitUuid}?v=${customRepresentation}`;
21
+
22
+ const { data, error, isLoading, isValidating } = useSWR<{ data: Visit }, Error>(
23
+ visitUuid ? apiUrl : null,
24
+ openmrsFetch,
25
+ );
26
+
27
+ return {
28
+ visit: data ? data.data : null,
29
+ isError: error,
30
+ isLoading,
31
+ isValidating,
32
+ };
33
+ }
34
+
35
+ export function calculateBMI(weight: number, height: number): number {
36
+ if (!weight || !height) {
37
+ return;
38
+ }
39
+
40
+ if (weight > 0 && height > 0) {
41
+ return Number((weight / (height / 100) ** 2).toFixed(1));
42
+ }
43
+ }
44
+
45
+ export function assessValue(value: number, range: ObsMetaInfo): ObservationInterpretation {
46
+ if (range?.hiCritical && value >= range.hiCritical) {
47
+ return 'critically_high';
48
+ }
49
+
50
+ if (range?.hiAbsolute && value >= range.hiAbsolute) {
51
+ return 'critically_high';
52
+ }
53
+
54
+ if (range?.hiNormal && value > range.hiNormal) {
55
+ return 'high';
56
+ }
57
+
58
+ if (range?.lowCritical && value <= range.lowCritical) {
59
+ return 'critically_low';
60
+ }
61
+
62
+ if (range?.lowAbsolute && value <= range.lowAbsolute) {
63
+ return 'critically_low';
64
+ }
65
+
66
+ if (range?.lowNormal && value < range.lowNormal) {
67
+ return 'low';
68
+ }
69
+
70
+ return 'normal';
71
+ }
72
+
73
+ export type ObservationInterpretation = 'critically_low' | 'critically_high' | 'high' | 'low' | 'normal';
74
+
75
+ export function getReferenceRangesForConcept(
76
+ conceptUuid: string,
77
+ conceptMetadata: Array<ConceptMetadata>,
78
+ ): ConceptMetadata {
79
+ if (!conceptUuid || !conceptMetadata?.length) {
80
+ return null;
81
+ }
82
+
83
+ return conceptMetadata?.find((metadata) => metadata.uuid === conceptUuid);
84
+ }