@kenyaemr/esm-express-workflow-app 5.4.3 → 5.4.4-pre.100

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 (388) hide show
  1. package/.turbo/turbo-build.log +7 -12
  2. package/dist/1074.js +1 -0
  3. package/dist/1074.js.map +1 -0
  4. package/dist/12.js +17 -0
  5. package/dist/12.js.map +1 -0
  6. package/dist/1311.js +1 -0
  7. package/dist/1311.js.map +1 -0
  8. package/dist/1323.js +1 -0
  9. package/dist/1323.js.map +1 -0
  10. package/dist/1469.js +1 -0
  11. package/dist/1469.js.map +1 -0
  12. package/dist/1506.js +13 -0
  13. package/dist/1506.js.map +1 -0
  14. package/dist/1562.js +1 -0
  15. package/dist/1562.js.map +1 -0
  16. package/dist/1760.js +1 -0
  17. package/dist/1760.js.map +1 -0
  18. package/dist/1780.js +1 -0
  19. package/dist/1780.js.map +1 -0
  20. package/dist/1804.js +1 -0
  21. package/dist/1804.js.map +1 -0
  22. package/dist/1884.js +1 -0
  23. package/dist/1884.js.map +1 -0
  24. package/dist/1972.js +1 -0
  25. package/dist/1972.js.map +1 -0
  26. package/dist/1990.js +1 -0
  27. package/dist/1990.js.map +1 -0
  28. package/dist/2016.js +1 -0
  29. package/dist/2016.js.map +1 -0
  30. package/dist/2024.js +1 -0
  31. package/dist/2024.js.map +1 -0
  32. package/dist/2153.js +1 -0
  33. package/dist/2153.js.map +1 -0
  34. package/dist/216.js +1 -0
  35. package/dist/216.js.map +1 -0
  36. package/dist/2225.js +1 -0
  37. package/dist/2225.js.map +1 -0
  38. package/dist/2294.js +1 -0
  39. package/dist/2294.js.map +1 -0
  40. package/dist/2345.js +1 -0
  41. package/dist/2345.js.map +1 -0
  42. package/dist/2499.js +1 -0
  43. package/dist/2499.js.map +1 -0
  44. package/dist/2500.js +1 -0
  45. package/dist/2500.js.map +1 -0
  46. package/dist/2586.js +1 -0
  47. package/dist/2586.js.map +1 -0
  48. package/dist/2625.js +1 -0
  49. package/dist/2625.js.map +1 -0
  50. package/dist/2685.js +1 -0
  51. package/dist/2685.js.map +1 -0
  52. package/dist/2809.js +1 -0
  53. package/dist/2809.js.map +1 -0
  54. package/dist/2851.js +1 -0
  55. package/dist/2851.js.map +1 -0
  56. package/dist/2881.js +1 -0
  57. package/dist/2881.js.map +1 -0
  58. package/dist/2948.js +1 -0
  59. package/dist/2948.js.map +1 -0
  60. package/dist/2968.js +1 -0
  61. package/dist/2968.js.map +1 -0
  62. package/dist/2978.js +1 -0
  63. package/dist/2978.js.map +1 -0
  64. package/dist/2998.js +1 -0
  65. package/dist/2998.js.map +1 -0
  66. package/dist/3089.js +1 -0
  67. package/dist/3089.js.map +1 -0
  68. package/dist/3548.js +1 -0
  69. package/dist/3548.js.map +1 -0
  70. package/dist/3567.js +1 -0
  71. package/dist/3567.js.map +1 -0
  72. package/dist/3569.js +1 -0
  73. package/dist/3569.js.map +1 -0
  74. package/dist/3571.js +1 -0
  75. package/dist/3571.js.map +1 -0
  76. package/dist/3691.js +1 -0
  77. package/dist/3691.js.map +1 -0
  78. package/dist/3730.js +1 -0
  79. package/dist/3730.js.map +1 -0
  80. package/dist/3923.js +1 -0
  81. package/dist/3923.js.map +1 -0
  82. package/dist/3963.js +1 -0
  83. package/dist/3963.js.map +1 -0
  84. package/dist/4024.js +1 -0
  85. package/dist/4024.js.map +1 -0
  86. package/dist/405.js +1 -0
  87. package/dist/405.js.map +1 -0
  88. package/dist/4071.js +1 -0
  89. package/dist/4071.js.map +1 -0
  90. package/dist/4271.js +1 -0
  91. package/dist/4271.js.map +1 -0
  92. package/dist/4296.js +1 -0
  93. package/dist/4296.js.map +1 -0
  94. package/dist/4337.js +1 -0
  95. package/dist/4337.js.map +1 -0
  96. package/dist/4432.js +1 -0
  97. package/dist/4432.js.map +1 -0
  98. package/dist/4581.js +1 -0
  99. package/dist/4581.js.map +1 -0
  100. package/dist/4637.js +11 -0
  101. package/dist/4637.js.map +1 -0
  102. package/dist/4666.js +1 -0
  103. package/dist/4666.js.map +1 -0
  104. package/dist/4680.js +1 -0
  105. package/dist/4680.js.map +1 -0
  106. package/dist/4735.js +1 -0
  107. package/dist/4735.js.map +1 -0
  108. package/dist/4737.js +1 -0
  109. package/dist/4737.js.map +1 -0
  110. package/dist/4744.js +1 -0
  111. package/dist/4744.js.map +1 -0
  112. package/dist/4795.js +1 -0
  113. package/dist/4795.js.map +1 -0
  114. package/dist/4813.js +2 -0
  115. package/dist/4813.js.map +1 -0
  116. package/dist/4818.js +1 -0
  117. package/dist/4818.js.map +1 -0
  118. package/dist/4858.js +1 -0
  119. package/dist/4858.js.map +1 -0
  120. package/dist/487.js +1 -0
  121. package/dist/487.js.map +1 -0
  122. package/dist/4970.js +1 -0
  123. package/dist/4970.js.map +1 -0
  124. package/dist/5038.js +1 -0
  125. package/dist/5038.js.map +1 -0
  126. package/dist/5202.js +1 -0
  127. package/dist/5202.js.map +1 -0
  128. package/dist/5491.js +1 -0
  129. package/dist/5491.js.map +1 -0
  130. package/dist/5592.js +1 -0
  131. package/dist/5592.js.map +1 -0
  132. package/dist/5669.js +1 -0
  133. package/dist/5669.js.map +1 -0
  134. package/dist/586.js +1 -0
  135. package/dist/586.js.map +1 -0
  136. package/dist/5932.js +1 -0
  137. package/dist/5932.js.map +1 -0
  138. package/dist/5995.js +1 -0
  139. package/dist/5995.js.map +1 -0
  140. package/dist/6258.js +1 -0
  141. package/dist/6258.js.map +1 -0
  142. package/dist/629.js +1 -0
  143. package/dist/629.js.map +1 -0
  144. package/dist/6328.js +1 -0
  145. package/dist/6328.js.map +1 -0
  146. package/dist/6355.js +1 -0
  147. package/dist/6355.js.map +1 -0
  148. package/dist/6419.js +1 -0
  149. package/dist/6419.js.map +1 -0
  150. package/dist/644.js +1 -0
  151. package/dist/644.js.map +1 -0
  152. package/dist/6456.js +1 -0
  153. package/dist/6466.js +3 -0
  154. package/dist/6466.js.map +1 -0
  155. package/dist/655.js +1 -0
  156. package/dist/655.js.map +1 -0
  157. package/dist/6798.js +66 -0
  158. package/dist/6798.js.map +1 -0
  159. package/dist/6910.js +1 -0
  160. package/dist/6910.js.map +1 -0
  161. package/dist/6925.js +1 -0
  162. package/dist/6925.js.map +1 -0
  163. package/dist/70.js +1 -0
  164. package/dist/70.js.map +1 -0
  165. package/dist/7201.js +1 -0
  166. package/dist/7201.js.map +1 -0
  167. package/dist/7234.js +1 -0
  168. package/dist/7234.js.map +1 -0
  169. package/dist/7261.js +1 -0
  170. package/dist/7261.js.map +1 -0
  171. package/dist/7326.js +1 -0
  172. package/dist/7359.js +1 -0
  173. package/dist/7487.js +1 -0
  174. package/dist/7487.js.map +1 -0
  175. package/dist/7591.js +1 -0
  176. package/dist/7591.js.map +1 -0
  177. package/dist/7607.js +1 -0
  178. package/dist/7701.js +1 -0
  179. package/dist/7701.js.map +1 -0
  180. package/dist/7717.js +1 -0
  181. package/dist/7717.js.map +1 -0
  182. package/dist/7739.js +1 -0
  183. package/dist/7739.js.map +1 -0
  184. package/dist/7788.js +1 -0
  185. package/dist/7788.js.map +1 -0
  186. package/dist/7819.js +1 -0
  187. package/dist/7819.js.map +1 -0
  188. package/dist/7971.js +1 -0
  189. package/dist/7971.js.map +1 -0
  190. package/dist/7983.js +1 -0
  191. package/dist/7983.js.map +1 -0
  192. package/dist/807.js +1 -0
  193. package/dist/807.js.map +1 -0
  194. package/dist/8159.js +7 -0
  195. package/dist/8159.js.map +1 -0
  196. package/dist/8338.js +1 -0
  197. package/dist/8338.js.map +1 -0
  198. package/dist/845.js +1 -0
  199. package/dist/845.js.map +1 -0
  200. package/dist/8570.js +1 -0
  201. package/dist/8570.js.map +1 -0
  202. package/dist/8661.js +1 -0
  203. package/dist/8661.js.map +1 -0
  204. package/dist/87.js +1 -0
  205. package/dist/87.js.map +1 -0
  206. package/dist/8727.js +1 -0
  207. package/dist/8766.js +1 -0
  208. package/dist/8766.js.map +1 -0
  209. package/dist/8828.js +1 -0
  210. package/dist/8828.js.map +1 -0
  211. package/dist/8860.js +1 -0
  212. package/dist/8860.js.map +1 -0
  213. package/dist/8911.js +1 -0
  214. package/dist/8911.js.map +1 -0
  215. package/dist/8930.js +1 -0
  216. package/dist/8930.js.map +1 -0
  217. package/dist/8971.js +1 -0
  218. package/dist/8971.js.map +1 -0
  219. package/dist/9124.js +1 -0
  220. package/dist/9124.js.map +1 -0
  221. package/dist/9157.js +1 -0
  222. package/dist/9157.js.map +1 -0
  223. package/dist/9182.js +1 -0
  224. package/dist/921.js +1 -0
  225. package/dist/921.js.map +1 -0
  226. package/dist/9212.js +1 -0
  227. package/dist/9212.js.map +1 -0
  228. package/dist/9255.js +1 -0
  229. package/dist/9255.js.map +1 -0
  230. package/dist/9257.js +1 -0
  231. package/dist/9257.js.map +1 -0
  232. package/dist/9316.js +1 -0
  233. package/dist/9316.js.map +1 -0
  234. package/dist/9333.js +1 -0
  235. package/dist/9333.js.map +1 -0
  236. package/dist/9404.js +1 -0
  237. package/dist/9404.js.map +1 -0
  238. package/dist/9446.js +1 -0
  239. package/dist/9446.js.map +1 -0
  240. package/dist/9447.js +1 -0
  241. package/dist/9447.js.map +1 -0
  242. package/dist/9449.js +1 -0
  243. package/dist/9449.js.map +1 -0
  244. package/dist/9535.js +1 -0
  245. package/dist/9535.js.map +1 -0
  246. package/dist/9606.js +1 -0
  247. package/dist/9606.js.map +1 -0
  248. package/dist/973.js +1 -0
  249. package/dist/973.js.map +1 -0
  250. package/dist/9845.js +1 -0
  251. package/dist/9845.js.map +1 -0
  252. package/dist/kenyaemr-esm-express-workflow-app.js +5 -5
  253. package/dist/kenyaemr-esm-express-workflow-app.js.buildmanifest.json +3004 -175
  254. package/dist/kenyaemr-esm-express-workflow-app.js.map +1 -1
  255. package/dist/main.js +5 -114
  256. package/dist/main.js.map +1 -1
  257. package/dist/routes.json +1 -1
  258. package/package.json +6 -7
  259. package/rspack.config.js +1 -1
  260. package/src/components/accounting/index.tsx +11 -10
  261. package/src/components/admissions/index.tsx +11 -10
  262. package/src/components/appointments/index.ts +10 -8
  263. package/src/components/consultation/clinical-encounter/encounter-details.component.tsx +9 -9
  264. package/src/components/consultation/clinical-encounter/encounter-details.test.tsx +19 -18
  265. package/src/components/consultation/consultation-context.tsx +19 -0
  266. package/src/components/consultation/consultation-summary-cards.component.tsx +124 -0
  267. package/src/components/consultation/consultation.component.tsx +41 -268
  268. package/src/components/consultation/consultation.resource.ts +67 -24
  269. package/src/components/consultation/consultation.scss +5 -0
  270. package/src/components/consultation/consultation.utils.tsx +222 -0
  271. package/src/components/consultation/dashboard.component.tsx +0 -2
  272. package/src/components/consultation/index.ts +27 -20
  273. package/src/components/facility-dashboard/index.tsx +10 -9
  274. package/src/components/laboratory/index.ts +11 -10
  275. package/src/components/laboratory/lab-table.component.tsx +22 -8
  276. package/src/components/laboratory/laboratory-tabs.component.tsx +2 -2
  277. package/src/components/mch/dashboard.component.tsx +0 -2
  278. package/src/components/mch/index.tsx +10 -9
  279. package/src/components/mch/mch.consultation.component.tsx +27 -15
  280. package/src/components/mch/mch.triage.component.tsx +42 -33
  281. package/src/components/pharmacy/index.ts +20 -18
  282. package/src/components/pharmacy/orders/pharmacy-orders.component.tsx +35 -13
  283. package/src/components/pharmacy/orders/pharmacy-orders.test.tsx +8 -7
  284. package/src/components/procedures/index.ts +11 -10
  285. package/src/components/procedures/procedures-table.component.tsx +44 -13
  286. package/src/components/procedures/procedures-tabs.component.tsx +2 -2
  287. package/src/components/radiology-and-imaging/index.ts +14 -10
  288. package/src/components/radiology-and-imaging/radiology-and-imaging-table.component.tsx +35 -15
  289. package/src/components/radiology-and-imaging/radiology-and-imaging.component.tsx +2 -2
  290. package/src/components/registration/card/HIE-card/hie-card.component.tsx +32 -44
  291. package/src/components/registration/card/Local-card/local-card.component.tsx +22 -24
  292. package/src/components/registration/dependants/dependants.component.tsx +32 -60
  293. package/src/components/registration/dependants/dependants.resource.ts +79 -50
  294. package/src/components/registration/helper/index.ts +4 -0
  295. package/src/components/registration/index.ts +10 -9
  296. package/src/components/registration/search-bar/search-bar.resource.ts +32 -93
  297. package/src/components/registration/start-visit-form/hooks/useDefaultFacilityLocation.tsx +15 -0
  298. package/src/components/registration/start-visit-form/hooks/useDefaultVisitLocation.tsx +24 -0
  299. package/src/components/registration/start-visit-form/hooks/useOfflineVisitType.tsx +18 -0
  300. package/src/components/registration/start-visit-form/hooks/useRecommendedVisitTypes.tsx +36 -0
  301. package/src/components/registration/start-visit-form/hooks/useVisitAttributeType.tsx +102 -0
  302. package/src/components/registration/start-visit-form/overflow-menu-extension/overflow-menu-item.extension.tsx +55 -0
  303. package/src/components/registration/start-visit-form/overflow-menu-extension/overflow-menu-item.scss +3 -0
  304. package/src/components/registration/start-visit-form/start-visit-workspace/base-visit-type.component.tsx +126 -0
  305. package/src/components/registration/start-visit-form/start-visit-workspace/base-visit-type.scss +75 -0
  306. package/src/components/registration/start-visit-form/start-visit-workspace/exported-visit-form.workspace.tsx +743 -0
  307. package/src/components/registration/start-visit-form/start-visit-workspace/location-selector.component.tsx +86 -0
  308. package/src/components/registration/start-visit-form/start-visit-workspace/recommended-visit-type.component.tsx +32 -0
  309. package/src/components/registration/start-visit-form/start-visit-workspace/visit-attribute-type.component.tsx +257 -0
  310. package/src/components/registration/start-visit-form/start-visit-workspace/visit-attribute-type.scss +5 -0
  311. package/src/components/registration/start-visit-form/start-visit-workspace/visit-date-time.component.tsx +193 -0
  312. package/src/components/registration/start-visit-form/start-visit-workspace/visit-form.resource.ts +383 -0
  313. package/src/components/registration/start-visit-form/start-visit-workspace/visit-form.scss +166 -0
  314. package/src/components/registration/start-visit-form/visit-form-workspace/visit-form.workspace.tsx +55 -0
  315. package/src/components/reports/index.ts +10 -9
  316. package/src/components/triage/dashboard.component.tsx +0 -2
  317. package/src/components/triage/index.ts +10 -9
  318. package/src/components/triage/triage.component.tsx +92 -42
  319. package/src/components/triage/triage.resource.ts +56 -50
  320. package/src/components/triage/triage.scss +6 -0
  321. package/src/config-schema.ts +91 -0
  322. package/src/hooks/useServiceQueues.tsx +95 -25
  323. package/src/index.ts +32 -13
  324. package/src/routes.json +53 -254
  325. package/src/shared/orders/OrdersTabs.tsx +8 -3
  326. package/src/shared/patient-chart/patient-chart.resources.ts +8 -12
  327. package/src/shared/patient-chart/patient-summary-dashboard/patient-summary-dashboard.component.tsx +1 -2
  328. package/src/shared/pin-put/pinput.component.test.tsx +7 -6
  329. package/src/shared/queue/queue-entry/queue-entry-table.component.tsx +99 -88
  330. package/src/shared/queue/queue-entry/queue-entry-table.scss +4 -0
  331. package/src/shared/queue/queue-summary-cards.component.tsx +32 -0
  332. package/src/shared/queue/queue-tab.component.tsx +182 -70
  333. package/src/shared/queue/queue-tab.scss +1 -0
  334. package/src/shared/queue/queue-workflow-context.tsx +90 -0
  335. package/src/shared/tabs/extension-tabs.component.tsx +9 -6
  336. package/src/shared/utils/index.ts +1 -2
  337. package/src/types/index.ts +17 -2
  338. package/translations/am.json +64 -13
  339. package/translations/en.json +59 -8
  340. package/translations/sw.json +58 -7
  341. package/dist/127.js +0 -1
  342. package/dist/200.js +0 -1
  343. package/dist/200.js.map +0 -1
  344. package/dist/24.js +0 -1
  345. package/dist/24.js.map +0 -1
  346. package/dist/267.js +0 -1
  347. package/dist/267.js.map +0 -1
  348. package/dist/285.js +0 -1
  349. package/dist/285.js.map +0 -1
  350. package/dist/329.js +0 -1
  351. package/dist/329.js.map +0 -1
  352. package/dist/40.js +0 -1
  353. package/dist/466.js +0 -1
  354. package/dist/466.js.map +0 -1
  355. package/dist/472.js +0 -66
  356. package/dist/472.js.map +0 -1
  357. package/dist/490.js +0 -1
  358. package/dist/490.js.map +0 -1
  359. package/dist/54.js +0 -1
  360. package/dist/54.js.map +0 -1
  361. package/dist/689.js +0 -1
  362. package/dist/689.js.map +0 -1
  363. package/dist/697.js +0 -1
  364. package/dist/697.js.map +0 -1
  365. package/dist/704.js +0 -1
  366. package/dist/704.js.map +0 -1
  367. package/dist/710.js +0 -1
  368. package/dist/710.js.map +0 -1
  369. package/dist/729.js +0 -17
  370. package/dist/729.js.map +0 -1
  371. package/dist/805.js +0 -37
  372. package/dist/805.js.map +0 -1
  373. package/dist/847.js +0 -1
  374. package/dist/847.js.map +0 -1
  375. package/dist/85.js +0 -1
  376. package/dist/85.js.map +0 -1
  377. package/dist/882.js +0 -1
  378. package/dist/91.js +0 -1
  379. package/dist/91.js.map +0 -1
  380. package/dist/916.js +0 -1
  381. package/dist/994.js +0 -1
  382. package/dist/994.js.map +0 -1
  383. package/dist/998.js +0 -1
  384. package/dist/998.js.map +0 -1
  385. package/jest.config.js +0 -3
  386. package/src/shared/patient-chart/patient-chart.component.tsx +0 -61
  387. package/src/shared/patient-chart/patient-chart.scss +0 -15
  388. package/src/shared/patient-chart/useInitialize.ts +0 -24
@@ -736,3 +736,7 @@ export const getEligibilityTags = (patient: fhir.Patient, eligibilityData?: Elig
736
736
 
737
737
  return tags;
738
738
  };
739
+
740
+ export const sanitizeName = (name: string): string => {
741
+ return name?.trim().replace(/[^\w\s'\-\.]/g, '') || '';
742
+ };
@@ -1,20 +1,21 @@
1
- import { getSyncLifecycle } from '@openmrs/esm-framework';
1
+ import { getAsyncLifecycle } from '@openmrs/esm-framework';
2
2
 
3
3
  import { moduleName } from '../../constants';
4
- import { createLeftPanelLink } from '../../shared/dashboard-link/dashboard-link.component';
5
- import Registration from './registration.component';
6
4
 
7
5
  const options = {
8
6
  featureName: 'express-workflow',
9
7
  moduleName,
10
8
  };
11
9
 
12
- export const registrationDashboard = getSyncLifecycle(Registration, options);
10
+ export const registrationDashboard = getAsyncLifecycle(() => import('./registration.component'), options);
13
11
  // t('registration', 'Registration')
14
- export const registrationLeftPanelLink = getSyncLifecycle(
15
- createLeftPanelLink({
16
- title: 'registration',
17
- name: 'registration',
18
- }),
12
+ export const registrationLeftPanelLink = getAsyncLifecycle(
13
+ () =>
14
+ import('../../shared/dashboard-link/dashboard-link.component').then((m) => ({
15
+ default: m.createLeftPanelLink({
16
+ title: 'registration',
17
+ name: 'registration',
18
+ }),
19
+ })),
19
20
  options,
20
21
  );
@@ -1,5 +1,13 @@
1
1
  import { useMemo } from 'react';
2
- import { FetchResponse, launchWorkspace, makeUrl, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import {
3
+ FetchResponse,
4
+ launchWorkspace2,
5
+ launchWorkspaceGroup2,
6
+ makeUrl,
7
+ openmrsFetch,
8
+ restBaseUrl,
9
+ type Visit,
10
+ } from '@openmrs/esm-framework';
3
11
  import useSWR from 'swr';
4
12
  import { type EligibilityResponse, type HIEEligibilityResponse, type LocalPatientApiResponse } from '../type';
5
13
  import {
@@ -11,6 +19,7 @@ import {
11
19
  } from '../constant';
12
20
  import { createDependentPatient, createHIEPatient } from '../dependants/dependants.resource';
13
21
  import { transformToDependentPayload } from '../helper';
22
+ import { VisitFormProps } from '../start-visit-form/visit-form-workspace/visit-form.workspace';
14
23
 
15
24
  export const searchPatientFromHIE = async (identifierType: string, searchQuery: string) => {
16
25
  const url = `${restBaseUrl}/kenyaemr/getSHAPatient/${searchQuery}/${identifierType}`;
@@ -64,11 +73,10 @@ export const useSHAEligibility = (nationalId: string) => {
64
73
  };
65
74
 
66
75
  export const extractPatientIdentifiers = (patient: any, isDependent = false) => {
67
- const identifiers = [];
76
+ const identifiers: Array<{ value: string; type: string }> = [];
68
77
 
69
78
  if (isDependent) {
70
79
  const identifierExtensions = patient.extension?.filter((ext: any) => ext.url === 'identifiers') || [];
71
-
72
80
  identifierExtensions.forEach((ext: any) => {
73
81
  if (ext.valueIdentifier?.value && ext.valueIdentifier?.type?.coding?.[0]?.code) {
74
82
  identifiers.push({
@@ -101,7 +109,7 @@ export const findExistingLocalPatient = async (patient: any, isDependent = false
101
109
  }
102
110
 
103
111
  const prioritizedIdentifiers = identifiers.sort((a, b) => {
104
- const priorityOrder = { 'national-id': 1, 'sha-number': 2, 'birth-certificate': 3 };
112
+ const priorityOrder: Record<string, number> = { 'national-id': 1, 'sha-number': 2, 'birth-certificate': 3 };
105
113
  return (priorityOrder[a.type] || 999) - (priorityOrder[b.type] || 999);
106
114
  });
107
115
 
@@ -119,26 +127,30 @@ export const findExistingLocalPatient = async (patient: any, isDependent = false
119
127
  return null;
120
128
  };
121
129
 
122
- /**
123
- * Registers a new patient if they don't exist locally, or launches the start visit workspace if they do.
124
- *
125
- * @param {Object} hiePatient - The patient object returned from the HIE
126
- * @param {Object} t - The i18n translation function
127
- * @returns {Promise<Object>} The patient record for the newly registered or existing patient
128
- */
130
+ async function launchCheckInWorkspace(patient: any, patientUuid: string) {
131
+ await launchWorkspaceGroup2('ewf-patient-chart', {
132
+ patient,
133
+ patientUuid,
134
+ visitContext: null as unknown as Visit,
135
+ mutateVisitContext: () => {},
136
+ });
137
+
138
+ launchWorkspace2<VisitFormProps, {}, {}>(
139
+ 'custom-start-visit-workspace-form',
140
+ {
141
+ openedFrom: 'registration-check-in',
142
+ showPatientHeader: false,
143
+ },
144
+ {},
145
+ null,
146
+ );
147
+ }
148
+
129
149
  export const registerOrLaunchHIEPatient = async (hiePatient: any, t: any) => {
130
150
  try {
131
151
  const existingLocalPatient = await findExistingLocalPatient(hiePatient, false);
132
152
 
133
153
  if (existingLocalPatient) {
134
- const patientName =
135
- existingLocalPatient.person?.personName?.display ||
136
- `${existingLocalPatient.person?.personName?.givenName || ''} ${
137
- existingLocalPatient.person?.personName?.middleName || ''
138
- } ${existingLocalPatient.person?.personName?.familyName || ''}`.trim() ||
139
- hiePatient.name?.[0]?.text ||
140
- 'Unknown Patient';
141
-
142
154
  return existingLocalPatient;
143
155
  } else {
144
156
  return await createHIEPatient(hiePatient, t);
@@ -148,32 +160,12 @@ export const registerOrLaunchHIEPatient = async (hiePatient: any, t: any) => {
148
160
  }
149
161
  };
150
162
 
151
- /**
152
- * Registers a new dependent patient if they don't exist locally, or launches the start visit workspace if they do.
153
- *
154
- * @param {Object} dependent - The dependent object returned from the HIE
155
- * @param {Object} t - The i18n translation function
156
- * @returns {Promise<Object>} The patient record for the newly registered or existing patient
157
- */
158
163
  export const registerOrLaunchDependent = async (dependent: any, t: any) => {
159
164
  try {
160
165
  const existingLocalPatient = await findExistingLocalPatient(dependent.contactData, true);
161
166
 
162
167
  if (existingLocalPatient) {
163
- const patientName =
164
- existingLocalPatient.person?.personName?.display ||
165
- `${existingLocalPatient.person?.personName?.givenName || ''} ${
166
- existingLocalPatient.person?.personName?.middleName || ''
167
- } ${existingLocalPatient.person?.personName?.familyName || ''}`.trim() ||
168
- dependent.name;
169
-
170
- launchWorkspace('start-visit-workspace-form', {
171
- patientUuid: existingLocalPatient.uuid,
172
- workspaceTitle: t('startVisitWorkspaceTitle', 'Start Visit for {{patientName}}', {
173
- patientName: patientName,
174
- }),
175
- });
176
-
168
+ await launchCheckInWorkspace(existingLocalPatient, existingLocalPatient.uuid);
177
169
  return existingLocalPatient;
178
170
  } else {
179
171
  const dependentPayload = transformToDependentPayload(dependent);
@@ -184,12 +176,6 @@ export const registerOrLaunchDependent = async (dependent: any, t: any) => {
184
176
  }
185
177
  };
186
178
 
187
- /**
188
- * Searches for multiple dependents locally.
189
- *
190
- * @param {Object[]} dependents - Array of dependent objects returned from the HIE
191
- * @returns {Promise<Map<string, any>>} A Map of dependent IDs to the corresponding patient records if found, or `null` if the dependent is not found.
192
- */
193
179
  export const searchMultipleDependentsLocally = async (dependents: any[]) => {
194
180
  const results = new Map();
195
181
 
@@ -205,13 +191,6 @@ export const searchMultipleDependentsLocally = async (dependents: any[]) => {
205
191
  return results;
206
192
  };
207
193
 
208
- /**
209
- * Searches for a patient by identifier value in the local OpenMRS database.
210
- *
211
- * @param {string} identifierValue - The value of the identifier to search for
212
- * @param {string} [identifierType] - The type of identifier to search by, if specified
213
- * @returns {Promise<Object | null>} The patient record for the matching patient, or `null` if no match is found
214
- */
215
194
  export const searchLocalPatientByIdentifier = async (identifierValue: string, identifierType?: string) => {
216
195
  if (!identifierValue) {
217
196
  return null;
@@ -253,22 +232,6 @@ export const searchLocalPatientByIdentifier = async (identifierValue: string, id
253
232
  }
254
233
  };
255
234
 
256
- /**
257
- * @param {string | null} identifierValue - The identifier value to search for, or null to not search.
258
- * @returns {object} An object containing the local patient, if found, and booleans indicating whether the search is loading or encountered an error.
259
- *
260
- * @example
261
- * const { localPatient, isLoading, error } = useLocalPatientByIdentifier('12345678');
262
- * if (isLoading) {
263
- * // show a loading indicator
264
- * }
265
- * if (error) {
266
- * // handle the error
267
- * }
268
- * if (localPatient) {
269
- * // do something with the local patient
270
- * }
271
- */
272
235
  export const useLocalPatientByIdentifier = (identifierValue: string | null) => {
273
236
  const { data, error, isLoading } = useSWR(
274
237
  identifierValue ? `local-patient-${identifierValue}` : null,
@@ -287,30 +250,6 @@ export const useLocalPatientByIdentifier = (identifierValue: string | null) => {
287
250
  };
288
251
  };
289
252
 
290
- /**
291
- * Searches for local patients with the given identifiers and returns an array of results.
292
- *
293
- * Each result is an object with an `identifier` property (the identifier that was searched for),
294
- * a `patient` property (the local patient that was found, or null if none was found), and an
295
- * `error` property (an error message if the search failed, or null if the search was successful).
296
- *
297
- * @param {Array<{ value: string; type: string }>} identifiers - The identifiers to search for.
298
- * @returns {Promise<Array<{ identifier: { value: string; type: string }; patient: LocalPatient | null; error: string | null }>>} The results of the search.
299
- *
300
- * @example
301
- * const results = await searchLocalPatientsByIdentifiers([
302
- * { value: '12345678', type: 'national-id' },
303
- * { value: '1234567890', type: 'ccc-number' },
304
- * ]);
305
- * results.forEach((result) => {
306
- * if (result.patient) {
307
- * // do something with the local patient
308
- * }
309
- * if (result.error) {
310
- * // handle the error
311
- * }
312
- * });
313
- */
314
253
  export const searchLocalPatientsByIdentifiers = async (identifiers: Array<{ value: string; type: string }>) => {
315
254
  const results = await Promise.allSettled(
316
255
  identifiers.map((identifier) => searchLocalPatientByIdentifier(identifier.value, identifier.type)),
@@ -0,0 +1,15 @@
1
+ import { type FetchResponse, openmrsFetch, useConfig } from '@openmrs/esm-framework';
2
+ import useSWRImmutable from 'swr/immutable';
3
+ import { ExpressWorkflowConfig } from '../../../../config-schema';
4
+
5
+ export const useDefaultFacilityLocation = () => {
6
+ const config = useConfig() as ExpressWorkflowConfig;
7
+ const apiUrl = config.defaultFacilityUrl;
8
+ const { data, error, isLoading } = useSWRImmutable<FetchResponse>(apiUrl, openmrsFetch);
9
+
10
+ return {
11
+ defaultFacility: data ? data?.data : null,
12
+ isLoading: isLoading,
13
+ error: error,
14
+ };
15
+ };
@@ -0,0 +1,24 @@
1
+ import { type FetchResponse, type Location, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { useEffect } from 'react';
3
+ import useSWR from 'swr';
4
+
5
+ /**
6
+ * Fetches the default visit location based on the location and the restrictByVisitLocationTag
7
+ * If restrictByVisitLocationTag is true, it fetches the nearest ancestor location that supports visits, otherwise it returns the passed-in location
8
+ *
9
+ * @param location
10
+ * @param restrictByVisitLocationTag
11
+ */
12
+ export function useDefaultVisitLocation(location: Location | undefined, restrictByVisitLocationTag: boolean) {
13
+ let url = location?.uuid ? `${restBaseUrl}/emrapi/locationThatSupportsVisits?location=${location.uuid}` : null;
14
+
15
+ const { data, error } = useSWR<FetchResponse>(restrictByVisitLocationTag ? url : null, openmrsFetch);
16
+
17
+ useEffect(() => {
18
+ if (error) {
19
+ console.error(error);
20
+ }
21
+ }, [error]);
22
+
23
+ return !restrictByVisitLocationTag ? location : data?.data;
24
+ }
@@ -0,0 +1,18 @@
1
+ import { type VisitType, useConfig } from '@openmrs/esm-framework';
2
+ import { useEffect, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { ExpressWorkflowConfig } from '../../../../config-schema';
5
+
6
+ export const useOfflineVisitType = () => {
7
+ const config = useConfig() as ExpressWorkflowConfig;
8
+ const { t } = useTranslation();
9
+ const [visitTypes, setVisitTypes] = useState<Array<VisitType>>([]);
10
+
11
+ useEffect(() => {
12
+ setVisitTypes([
13
+ { uuid: config.offlineVisitTypeUuid, name: 'Offline Visit', display: t('offlineVisit', 'Offline Visit') },
14
+ ]);
15
+ }, [t, config.offlineVisitTypeUuid]);
16
+
17
+ return visitTypes;
18
+ };
@@ -0,0 +1,36 @@
1
+ import { useMemo } from 'react';
2
+ import useSWR from 'swr';
3
+ import { openmrsFetch, useConfig } from '@openmrs/esm-framework';
4
+ import { ExpressWorkflowConfig } from '../../../../config-schema';
5
+
6
+ interface EnrollmentVisitType {
7
+ uuid: string;
8
+ dataDependencies: Array<string>;
9
+ enrollmentOptions: object;
10
+ incompatibleWith: Array<string>;
11
+ name: string;
12
+ visitTypes: { allowed: Array<EnrollmentVisitType>; disallowed: Array<EnrollmentVisitType> };
13
+ }
14
+
15
+ export const useRecommendedVisitTypes = (
16
+ patientUuid: string,
17
+ enrollmentUuid: string,
18
+ programUuid: string,
19
+ locationUuid: string,
20
+ ) => {
21
+ const { visitTypeResourceUrl, showRecommendedVisitTypeTab } = useConfig() as ExpressWorkflowConfig;
22
+ const url = `${visitTypeResourceUrl}${patientUuid}/program/${programUuid}/enrollment/${enrollmentUuid}?intendedLocationUuid=${locationUuid}`;
23
+
24
+ const { data, error, isLoading } = useSWR<{ data: EnrollmentVisitType }>(
25
+ showRecommendedVisitTypeTab && patientUuid && enrollmentUuid && programUuid ? url : null,
26
+ openmrsFetch,
27
+ );
28
+
29
+ const recommendedVisitTypes = useMemo(() => data?.data?.visitTypes?.allowed.map(mapToVisitType) ?? [], [data]);
30
+ return { recommendedVisitTypes, error, isLoading };
31
+ };
32
+
33
+ const mapToVisitType = (visitType: EnrollmentVisitType) => ({
34
+ ...visitType,
35
+ display: visitType.name,
36
+ });
@@ -0,0 +1,102 @@
1
+ import { useMemo } from 'react';
2
+ import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
3
+ import useSWRImmutable from 'swr/immutable';
4
+
5
+ interface VisitAttributeType {
6
+ uuid: string;
7
+ display: string;
8
+ name: string;
9
+ description: string | null;
10
+ datatypeClassname:
11
+ | 'org.openmrs.customdatatype.datatype.ConceptDatatype'
12
+ | 'org.openmrs.customdatatype.datatype.FloatDatatype'
13
+ | 'org.openmrs.customdatatype.datatype.BooleanDatatype'
14
+ | 'org.openmrs.customdatatype.datatype.LongFreeTextDatatype'
15
+ | 'org.openmrs.customdatatype.datatype.FreeTextDatatype'
16
+ | 'org.openmrs.customdatatype.datatype.DateDatatype';
17
+ datatypeConfig: string;
18
+ preferredHandlerClassname: any;
19
+ retired: boolean;
20
+ }
21
+
22
+ interface Concept {
23
+ uuid: string;
24
+ name: {
25
+ display: string;
26
+ };
27
+ display: string;
28
+ answers: Array<{
29
+ display: string;
30
+ uuid: string;
31
+ }>;
32
+ }
33
+
34
+ const visitAttributeTypeCustomRepresentation =
35
+ 'custom:(uuid,display,name,description,datatypeClassname,datatypeConfig)';
36
+
37
+ export function useVisitAttributeTypes() {
38
+ const { data, error, isLoading } = useSWRImmutable<FetchResponse<{ results: VisitAttributeType[] }>, Error>(
39
+ `${restBaseUrl}/visitattributetype?v=${visitAttributeTypeCustomRepresentation}`,
40
+ openmrsFetch,
41
+ );
42
+
43
+ if (error) {
44
+ console.error('Failed to fetch visit attribute types: ', error);
45
+ }
46
+
47
+ const results = useMemo(
48
+ () => ({
49
+ isLoading,
50
+ error,
51
+ visitAttributeTypes: data?.data?.results ?? [],
52
+ }),
53
+ [data, error, isLoading],
54
+ );
55
+
56
+ return results;
57
+ }
58
+
59
+ export function useVisitAttributeType(uuid: string) {
60
+ const { data, error, isLoading } = useSWRImmutable<FetchResponse<VisitAttributeType>, Error>(
61
+ `${restBaseUrl}/visitattributetype/${uuid}?v=${visitAttributeTypeCustomRepresentation}`,
62
+ openmrsFetch,
63
+ );
64
+
65
+ if (error) {
66
+ console.error(`Failed to fetch visit attribute type ${uuid}: `, error);
67
+ }
68
+
69
+ const results = useMemo(
70
+ () => ({
71
+ isLoading,
72
+ error: error,
73
+ data: data?.data,
74
+ }),
75
+ [data, error, isLoading],
76
+ );
77
+
78
+ return results;
79
+ }
80
+
81
+ export function useConceptAnswersForVisitAttributeType(conceptUuid: string | null) {
82
+ const { data, error, isLoading } = useSWRImmutable<FetchResponse<Concept>, Error>(
83
+ conceptUuid ? `${restBaseUrl}/concept/${conceptUuid}` : null,
84
+ openmrsFetch,
85
+ );
86
+
87
+ if (error) {
88
+ console.error(`Failed to fetch concept answers for visit attribute type ${conceptUuid}: `, error);
89
+ }
90
+
91
+ const results = useMemo(
92
+ () => ({
93
+ isLoading,
94
+ error: error,
95
+ data: data?.data,
96
+ answers: data?.data?.answers,
97
+ }),
98
+ [data, error, isLoading],
99
+ );
100
+
101
+ return results;
102
+ }
@@ -0,0 +1,55 @@
1
+ import React, { useCallback } from 'react';
2
+ import { OverflowMenuItem } from '@carbon/react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { launchWorkspace2, launchWorkspaceGroup2, type Visit } from '@openmrs/esm-framework';
5
+ import styles from './overflow-menu-item.scss';
6
+ import { type VisitFormProps } from '../visit-form-workspace/visit-form.workspace';
7
+
8
+ interface CustomStartVisitOverflowMenuItemProps {
9
+ patientUuid?: string;
10
+ patient?: fhir.Patient;
11
+ closeMenu?: () => void;
12
+ }
13
+
14
+ const CustomStartVisitOverflowMenuItem: React.FC<CustomStartVisitOverflowMenuItemProps> = ({
15
+ patient,
16
+ patientUuid,
17
+ closeMenu,
18
+ }) => {
19
+ const { t } = useTranslation();
20
+ const isDeceased = Boolean(patient?.deceasedDateTime);
21
+
22
+ const handleLaunchModal = useCallback(async () => {
23
+ const resolvedPatientUuid = patient?.id ?? patientUuid ?? '';
24
+
25
+ await launchWorkspaceGroup2('ewf-patient-chart', {
26
+ patient,
27
+ patientUuid: resolvedPatientUuid,
28
+ visitContext: null as unknown as Visit,
29
+ mutateVisitContext: () => {},
30
+ });
31
+
32
+ launchWorkspace2<VisitFormProps, {}, {}>(
33
+ 'custom-start-visit-workspace-form',
34
+ {
35
+ openedFrom: 'patient-chart-start-visit',
36
+ showPatientHeader: false,
37
+ },
38
+ {},
39
+ null,
40
+ );
41
+ }, [patient, patientUuid]);
42
+
43
+ return (
44
+ !isDeceased && (
45
+ <OverflowMenuItem
46
+ className={styles.menuitem}
47
+ itemText={t('customCheckin', 'Check in')}
48
+ onClick={handleLaunchModal}
49
+ closeMenu={closeMenu}
50
+ />
51
+ )
52
+ );
53
+ };
54
+
55
+ export default CustomStartVisitOverflowMenuItem;
@@ -0,0 +1,126 @@
1
+ import React, { useState, useMemo, useCallback, useEffect, useRef, type ChangeEvent } from 'react';
2
+ import classNames from 'classnames';
3
+ import { Layer, RadioButton, RadioButtonGroup, Search, StructuredListSkeleton, Tile } from '@carbon/react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { useFormContext, Controller } from 'react-hook-form';
6
+ import { PatientChartPagination } from '@openmrs/esm-patient-common-lib';
7
+ import { useConfig, useDebounce, useLayoutType, usePagination, type VisitType } from '@openmrs/esm-framework';
8
+ import { type VisitFormData } from './visit-form.resource';
9
+ import { type ExpressWorkflowConfig } from '../../../../config-schema';
10
+ import styles from './base-visit-type.scss';
11
+
12
+ interface BaseVisitTypeProps {
13
+ visitTypes: Array<VisitType>;
14
+ }
15
+
16
+ const BaseVisitType: React.FC<BaseVisitTypeProps> = ({ visitTypes }) => {
17
+ const { t } = useTranslation();
18
+ const { control, setValue } = useFormContext<VisitFormData>();
19
+ const isTablet = useLayoutType() === 'tablet';
20
+ const [searchTerm, setSearchTerm] = useState<string>('');
21
+ const debouncedSearchTerm = useDebounce(searchTerm, 300);
22
+ const config = useConfig<ExpressWorkflowConfig>();
23
+
24
+ const filteredVisitTypes = useMemo(() => {
25
+ const excluded = config?.excludedVisitTypeUuids;
26
+ return visitTypes.filter((visitType) => !excluded.includes(visitType.uuid));
27
+ }, [visitTypes, config?.excludedVisitTypeUuids]);
28
+
29
+ const searchResults = useMemo(() => {
30
+ if (!debouncedSearchTerm.trim()) {
31
+ return filteredVisitTypes;
32
+ }
33
+ const lowercasedTerm = debouncedSearchTerm.toLowerCase();
34
+ return filteredVisitTypes.filter((visitType) => visitType.display.toLowerCase().includes(lowercasedTerm));
35
+ }, [debouncedSearchTerm, filteredVisitTypes]);
36
+
37
+ const { results, currentPage, goTo } = usePagination(searchResults, 5);
38
+ const hasNoMatchingSearchResults = debouncedSearchTerm.trim() !== '' && searchResults.length === 0;
39
+ const prevSearchTermRef = useRef(debouncedSearchTerm);
40
+
41
+ const handleSearchTermChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
42
+ setSearchTerm(e.target.value);
43
+ }, []);
44
+
45
+ useEffect(() => {
46
+ if (searchResults?.length === 1) {
47
+ setValue('visitType', searchResults[0].uuid);
48
+ }
49
+ }, [searchResults, setValue]);
50
+
51
+ useEffect(() => {
52
+ if (prevSearchTermRef.current !== debouncedSearchTerm && currentPage !== 1) {
53
+ goTo(1);
54
+ }
55
+ prevSearchTermRef.current = debouncedSearchTerm;
56
+ }, [debouncedSearchTerm, currentPage, goTo]);
57
+
58
+ const searchComponent = (
59
+ <Search
60
+ labelText={t('searchForAVisitType', 'Search for a visit type')}
61
+ onChange={handleSearchTermChange}
62
+ placeholder={t('searchForAVisitType', 'Search for a visit type')}
63
+ value={searchTerm}
64
+ />
65
+ );
66
+
67
+ return (
68
+ <div className={classNames(styles.visitTypeOverviewWrapper, isTablet ? styles.tablet : styles.desktop)}>
69
+ {filteredVisitTypes.length ? (
70
+ <>
71
+ {isTablet ? <Layer>{searchComponent}</Layer> : searchComponent}
72
+
73
+ {hasNoMatchingSearchResults ? (
74
+ <div className={styles.tileContainer}>
75
+ <Tile className={styles.tile}>
76
+ <div className={styles.tileContent}>
77
+ <p className={styles.content}>{t('noVisitTypesToDisplay', 'No visit types to display')}</p>
78
+ <p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
79
+ </div>
80
+ </Tile>
81
+ </div>
82
+ ) : (
83
+ <Controller
84
+ name="visitType"
85
+ control={control}
86
+ render={({ field: { onChange, value } }) => (
87
+ <RadioButtonGroup
88
+ className={styles.radioButtonGroup}
89
+ name="visit-types"
90
+ onChange={onChange}
91
+ orientation="vertical"
92
+ valueSelected={value}>
93
+ {results.map(({ display, uuid }) => (
94
+ <RadioButton
95
+ className={styles.radioButton}
96
+ id={`visit-type-${uuid}`}
97
+ key={uuid}
98
+ labelText={display}
99
+ value={uuid}
100
+ />
101
+ ))}
102
+ </RadioButtonGroup>
103
+ )}
104
+ />
105
+ )}
106
+
107
+ {!hasNoMatchingSearchResults && (
108
+ <div className={styles.paginationContainer}>
109
+ <PatientChartPagination
110
+ currentItems={results.length}
111
+ onPageNumberChange={({ page }: { page: number }) => goTo(page)}
112
+ pageNumber={currentPage}
113
+ pageSize={5}
114
+ totalItems={searchResults.length}
115
+ />
116
+ </div>
117
+ )}
118
+ </>
119
+ ) : (
120
+ <StructuredListSkeleton className={styles.skeleton} />
121
+ )}
122
+ </div>
123
+ );
124
+ };
125
+
126
+ export default BaseVisitType;