@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
@@ -0,0 +1,743 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import { Controller, FormProvider, useForm, useWatch } from 'react-hook-form';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { useSWRConfig } from 'swr';
6
+ import {
7
+ Button,
8
+ ButtonSet,
9
+ ContentSwitcher,
10
+ Form,
11
+ FormGroup,
12
+ InlineLoading,
13
+ InlineNotification,
14
+ RadioButton,
15
+ RadioButtonGroup,
16
+ Row,
17
+ Stack,
18
+ Switch,
19
+ } from '@carbon/react';
20
+ import { zodResolver } from '@hookform/resolvers/zod';
21
+ import {
22
+ Extension,
23
+ ExtensionSlot,
24
+ OpenmrsFetchError,
25
+ saveVisit,
26
+ showSnackbar,
27
+ updateVisit,
28
+ useConfig,
29
+ useConnectivity,
30
+ useEmrConfiguration,
31
+ useLayoutType,
32
+ useVisit,
33
+ Workspace2,
34
+ type AssignedExtension,
35
+ type NewVisitPayload,
36
+ type Visit,
37
+ type Workspace2DefinitionProps,
38
+ } from '@openmrs/esm-framework';
39
+ import {
40
+ createOfflineVisitForPatient,
41
+ invalidateCurrentVisit,
42
+ invalidateVisitAndEncounterData,
43
+ useActivePatientEnrollment,
44
+ } from '@openmrs/esm-patient-common-lib';
45
+ import { MemoizedRecommendedVisitType } from './recommended-visit-type.component';
46
+ import {
47
+ convertToDate,
48
+ createVisitAttribute,
49
+ deleteVisitAttribute,
50
+ extractErrorMessagesFromResponse,
51
+ updateVisitAttribute,
52
+ useAllowOverlappingVisits,
53
+ useConditionalVisitTypes,
54
+ useEarliestAllowedVisitStartDate,
55
+ useVisitFormCallbacks,
56
+ useVisitFormSchemaAndDefaultValues,
57
+ visitStatuses,
58
+ type ErrorObject,
59
+ type VisitFormCallbacks,
60
+ type VisitFormData,
61
+ } from './visit-form.resource';
62
+ import { useVisitAttributeTypes } from '../hooks/useVisitAttributeType';
63
+ import BaseVisitType from './base-visit-type.component';
64
+ import LocationSelector from './location-selector.component';
65
+ import VisitAttributeTypeFields from './visit-attribute-type.component';
66
+ import VisitDateTimeSection from './visit-date-time.component';
67
+ import styles from './visit-form.scss';
68
+ import { ExpressWorkflowConfig } from '../../../../config-schema';
69
+
70
+ interface VisitAttribute {
71
+ attributeType: string;
72
+ value: string;
73
+ }
74
+
75
+ export interface ExtraVisitInfo {
76
+ handleCreateExtraVisitInfo?: () => void | Promise<void>;
77
+ attributes?: Array<VisitAttribute>;
78
+ }
79
+
80
+ export interface ExportedVisitFormProps {
81
+ openedFrom: string;
82
+ showPatientHeader?: boolean;
83
+ onVisitStarted?: (visit: Visit) => void;
84
+ patient: fhir.Patient;
85
+ patientUuid: string;
86
+ visitContext: Visit | null;
87
+ }
88
+
89
+ interface VisitAttributeResource {
90
+ attributeType: { uuid: string; display: string };
91
+ uuid: string;
92
+ value: unknown;
93
+ }
94
+
95
+ const ExportedVisitForm: React.FC<Workspace2DefinitionProps<ExportedVisitFormProps, {}, {}>> = ({
96
+ closeWorkspace,
97
+ workspaceProps,
98
+ }) => {
99
+ const {
100
+ openedFrom = '',
101
+ showPatientHeader = false,
102
+ onVisitStarted,
103
+ patient,
104
+ patientUuid = '',
105
+ visitContext: visitToEdit = null,
106
+ } = workspaceProps ?? {};
107
+
108
+ const { t } = useTranslation();
109
+ const isTablet = useLayoutType() === 'tablet';
110
+ const isOnline = useConnectivity();
111
+ const config = useConfig<ExpressWorkflowConfig>();
112
+ const { emrConfiguration } = useEmrConfiguration();
113
+ const [visitTypeContentSwitcherIndex, setVisitTypeContentSwitcherIndex] = useState(
114
+ config?.showRecommendedVisitTypeTab ? 0 : 1,
115
+ );
116
+ const visitHeaderSlotState = useMemo(() => ({ patientUuid, patient }), [patientUuid, patient]);
117
+ const { activePatientEnrollment, isLoading } = useActivePatientEnrollment(patientUuid);
118
+ const { activeVisit, isLoading: isLoadingVisit } = useVisit(patientUuid);
119
+ const { allowOverlappingVisits, isLoading: isLoadingOverlapSetting } = useAllowOverlappingVisits();
120
+ const { mutate: globalMutate } = useSWRConfig();
121
+ const allVisitTypes = useConditionalVisitTypes();
122
+ const { earliestAllowedStartDate, isLoading: isLoadingBirthdateCheck } =
123
+ useEarliestAllowedVisitStartDate(patientUuid);
124
+
125
+ const [errorFetchingResources, setErrorFetchingResources] = useState<{
126
+ blockSavingForm: boolean;
127
+ } | null>(null);
128
+
129
+ const setErrorFetchingResourcesAdapter = useCallback((action: React.SetStateAction<{ blockSavingForm: boolean }>) => {
130
+ setErrorFetchingResources((prev) => {
131
+ const prevNonNull = prev ?? { blockSavingForm: false };
132
+ return typeof action === 'function' ? action(prevNonNull) : action;
133
+ });
134
+ }, []) as React.Dispatch<React.SetStateAction<{ blockSavingForm: boolean }>>;
135
+
136
+ const { visitAttributeTypes } = useVisitAttributeTypes();
137
+ const [visitFormCallbacks, setVisitFormCallbacks] = useVisitFormCallbacks();
138
+ const [extraVisitInfo, setExtraVisitInfo] = useState<ExtraVisitInfo | null>(null);
139
+
140
+ const { visitFormSchema, defaultValues, firstEncounterDateTime, lastEncounterDateTime } =
141
+ useVisitFormSchemaAndDefaultValues(visitToEdit ?? undefined, earliestAllowedStartDate);
142
+
143
+ const methods = useForm<VisitFormData>({
144
+ mode: 'all',
145
+ resolver: zodResolver(visitFormSchema),
146
+ defaultValues,
147
+ });
148
+
149
+ const {
150
+ handleSubmit,
151
+ control,
152
+ getValues,
153
+ formState: { errors, isDirty, isSubmitting },
154
+ reset,
155
+ } = methods;
156
+
157
+ const visitStatus = useWatch({ control, name: 'visitStatus' });
158
+ const visitType = useWatch({ control, name: 'visitType' });
159
+ const hasActiveVisitConflict = !visitToEdit && visitStatus !== 'past' && !!activeVisit && !allowOverlappingVisits;
160
+
161
+ const isSHAVisit = useMemo(
162
+ () => [...visitFormCallbacks.values()].some((cb) => cb.isSHAVisit === true),
163
+ [visitFormCallbacks],
164
+ );
165
+
166
+ useEffect(() => {
167
+ reset(defaultValues, {
168
+ keepDirty: true,
169
+ keepDirtyValues: true,
170
+ keepErrors: true,
171
+ keepTouched: true,
172
+ });
173
+ }, [defaultValues, reset]);
174
+
175
+ const getErrorDescription = useCallback(
176
+ (error: unknown) => {
177
+ if (OpenmrsFetchError && error instanceof OpenmrsFetchError) {
178
+ return typeof error.responseBody === 'string'
179
+ ? error.responseBody
180
+ : extractErrorMessagesFromResponse(error.responseBody as ErrorObject, t);
181
+ }
182
+ return (error as Error)?.message;
183
+ },
184
+ [t],
185
+ );
186
+
187
+ const handleVisitAttributes = useCallback(
188
+ (visitAttributes: { [p: string]: string }, visitUuid: string) => {
189
+ const existingVisitAttributeTypes =
190
+ visitToEdit?.attributes?.map((attribute) => attribute.attributeType.uuid) || [];
191
+ const promises: Array<Promise<unknown>> = [];
192
+
193
+ for (const [attributeType, value] of Object.entries(visitAttributes)) {
194
+ if (attributeType && existingVisitAttributeTypes.includes(attributeType)) {
195
+ const attributeToEdit = (visitToEdit?.attributes as Array<VisitAttributeResource> | undefined)?.find(
196
+ (attr) => attr.attributeType.uuid === attributeType,
197
+ );
198
+ if (attributeToEdit) {
199
+ const isSameValue =
200
+ typeof attributeToEdit.value === 'object' &&
201
+ attributeToEdit.value !== null &&
202
+ 'uuid' in (attributeToEdit.value as object)
203
+ ? (attributeToEdit.value as { uuid: string }).uuid === value
204
+ : attributeToEdit.value === value;
205
+ if (isSameValue) {
206
+ continue;
207
+ }
208
+ if (value) {
209
+ promises.push(
210
+ updateVisitAttribute(visitUuid, attributeToEdit.uuid, value).catch((error) => {
211
+ showSnackbar({
212
+ title: t('errorUpdatingVisitAttribute', 'Error updating the {{attributeName}} visit attribute', {
213
+ attributeName: attributeToEdit.attributeType.display,
214
+ }),
215
+ kind: 'error',
216
+ isLowContrast: false,
217
+ subtitle: getErrorDescription(error),
218
+ });
219
+ return Promise.reject(error);
220
+ }),
221
+ );
222
+ } else {
223
+ promises.push(
224
+ deleteVisitAttribute(visitUuid, attributeToEdit.uuid).catch((error) => {
225
+ showSnackbar({
226
+ title: t('errorDeletingVisitAttribute', 'Error deleting the {{attributeName}} visit attribute', {
227
+ attributeName: attributeToEdit.attributeType.display,
228
+ }),
229
+ kind: 'error',
230
+ isLowContrast: false,
231
+ subtitle: getErrorDescription(error),
232
+ });
233
+ return Promise.reject(error);
234
+ }),
235
+ );
236
+ }
237
+ }
238
+ } else {
239
+ if (value) {
240
+ promises.push(
241
+ createVisitAttribute(visitUuid, attributeType, value).catch((error) => {
242
+ showSnackbar({
243
+ title: t('errorCreatingVisitAttribute', 'Error creating the {{attributeName}} visit attribute', {
244
+ attributeName: visitAttributeTypes?.find((type) => type.uuid === attributeType)?.display,
245
+ }),
246
+ kind: 'error',
247
+ isLowContrast: false,
248
+ subtitle: getErrorDescription(error),
249
+ });
250
+ return Promise.reject(error);
251
+ }),
252
+ );
253
+ }
254
+ }
255
+ }
256
+ return Promise.all(promises);
257
+ },
258
+ [getErrorDescription, visitToEdit, t, visitAttributeTypes],
259
+ );
260
+
261
+ const onSubmit = useCallback(
262
+ async (data: VisitFormData) => {
263
+ const {
264
+ visitStatus,
265
+ visitStartTimeFormat,
266
+ visitStartDate,
267
+ visitLocation,
268
+ visitStartTime,
269
+ visitType,
270
+ visitAttributes,
271
+ visitStopDate,
272
+ visitStopTime,
273
+ visitStopTimeFormat,
274
+ } = data;
275
+
276
+ const { handleCreateExtraVisitInfo, attributes: extraAttributes } = extraVisitInfo ?? {};
277
+ const hasStartTime = ['ongoing', 'past'].includes(visitStatus);
278
+ const hasStopTime = 'past' === visitStatus;
279
+ const startDatetime = convertToDate(visitStartDate, visitStartTime, visitStartTimeFormat);
280
+ const stopDatetime = convertToDate(visitStopDate, visitStopTime, visitStopTimeFormat);
281
+
282
+ const formAttributes = !visitToEdit
283
+ ? Object.entries(visitAttributes)
284
+ .filter(([, value]) => value)
285
+ .map(([attributeType, value]) => ({ attributeType, value }))
286
+ : [];
287
+
288
+ const formAttributeTypes = new Set(formAttributes.map((attr) => attr.attributeType));
289
+ const deduplicatedExtraAttributes = (extraAttributes ?? []).filter(
290
+ (attr: VisitAttribute) => attr?.attributeType && !formAttributeTypes.has(attr.attributeType),
291
+ );
292
+ const inlineAttributes = !visitToEdit
293
+ ? [...formAttributes, ...deduplicatedExtraAttributes].filter(
294
+ (attr: VisitAttribute) => attr?.attributeType?.length > 0 && attr?.value?.length > 0,
295
+ )
296
+ : [];
297
+
298
+ const locationUuid = visitLocation?.uuid ?? '';
299
+ const payload: NewVisitPayload = {
300
+ visitType,
301
+ location: locationUuid,
302
+ startDatetime: (hasStartTime ? startDatetime : null) as Date,
303
+ stopDatetime: (hasStopTime ? stopDatetime : null) as Date,
304
+ ...(!visitToEdit && { patient: patientUuid }),
305
+ ...(inlineAttributes.length > 0 && { attributes: inlineAttributes }),
306
+ };
307
+
308
+ const abortController = new AbortController();
309
+ if (isOnline) {
310
+ for (const [, callbacks] of visitFormCallbacks) {
311
+ if (typeof callbacks.onBeforeVisitSave === 'function') {
312
+ const canProceed = await callbacks.onBeforeVisitSave();
313
+ if (!canProceed) {
314
+ return;
315
+ }
316
+ }
317
+ }
318
+
319
+ const visitRequest = visitToEdit?.uuid
320
+ ? updateVisit(visitToEdit.uuid, payload, abortController)
321
+ : saveVisit(payload, abortController);
322
+
323
+ await visitRequest
324
+ .then((response) => {
325
+ showSnackbar({
326
+ isLowContrast: true,
327
+ kind: 'success',
328
+ subtitle: !visitToEdit
329
+ ? t('visitStartedSuccessfully', '{{visit}} started successfully', {
330
+ visit: response?.data?.visitType?.display ?? t('visit', 'Visit'),
331
+ })
332
+ : t('visitDetailsUpdatedSuccessfully', '{{visit}} updated successfully', {
333
+ visit: response?.data?.visitType?.display ?? t('pastVisit', 'Past visit'),
334
+ }),
335
+ title: !visitToEdit
336
+ ? t('visitStarted', 'Visit started')
337
+ : t('visitDetailsUpdated', 'Visit details updated'),
338
+ });
339
+ return response;
340
+ })
341
+ .catch((error) => {
342
+ showSnackbar({
343
+ title: !visitToEdit
344
+ ? t('startVisitError', 'Error starting visit')
345
+ : t('errorUpdatingVisitDetails', 'Error updating visit details'),
346
+ kind: 'error',
347
+ isLowContrast: false,
348
+ subtitle: getErrorDescription(error),
349
+ });
350
+ return Promise.reject(error);
351
+ })
352
+ .then(async (response) => {
353
+ const visit = response.data;
354
+
355
+ invalidateVisitAndEncounterData(globalMutate as any, patientUuid);
356
+ invalidateCurrentVisit(globalMutate as any, patientUuid);
357
+
358
+ globalMutate(
359
+ (key) =>
360
+ typeof key === 'string' &&
361
+ (key.includes(`/visit?patient=${patientUuid}`) ||
362
+ key.includes(`/patient/${patientUuid}`) ||
363
+ key.includes('/queue') ||
364
+ key.includes('/visit/')),
365
+ undefined,
366
+ { revalidate: true },
367
+ );
368
+
369
+ const visitAttributesRequest = visitToEdit
370
+ ? handleVisitAttributes(visitAttributes, response.data.uuid).then((visitAttributesResponses) => {
371
+ if (visitAttributesResponses.length > 0) {
372
+ showSnackbar({
373
+ isLowContrast: true,
374
+ kind: 'success',
375
+ title: t(
376
+ 'additionalVisitInformationUpdatedSuccessfully',
377
+ 'Additional visit information updated successfully',
378
+ ),
379
+ });
380
+ }
381
+ })
382
+ : Promise.resolve();
383
+
384
+ const onVisitCreatedOrUpdatedRequests = [...visitFormCallbacks.values()].map((callbacks) =>
385
+ callbacks.onVisitCreatedOrUpdated(visit),
386
+ );
387
+
388
+ await Promise.all([visitAttributesRequest, ...onVisitCreatedOrUpdatedRequests]);
389
+ await handleCreateExtraVisitInfo?.();
390
+ await closeWorkspace({ discardUnsavedChanges: true });
391
+ onVisitStarted?.(visit);
392
+ })
393
+ .catch(() => {});
394
+ } else {
395
+ await createOfflineVisitForPatient(
396
+ patientUuid,
397
+ locationUuid,
398
+ config.offlineVisitTypeUuid,
399
+ payload.startDatetime,
400
+ ).then(
401
+ async (visit) => {
402
+ invalidateVisitAndEncounterData(globalMutate as any, patientUuid);
403
+ invalidateCurrentVisit(globalMutate as any, patientUuid);
404
+
405
+ globalMutate(
406
+ (key) =>
407
+ typeof key === 'string' &&
408
+ (key.includes(`/visit?patient=${patientUuid}`) ||
409
+ key.includes(`/patient/${patientUuid}`) ||
410
+ key.includes('/queue')),
411
+ undefined,
412
+ { revalidate: true },
413
+ );
414
+
415
+ showSnackbar({
416
+ isLowContrast: true,
417
+ kind: 'success',
418
+ subtitle: t('visitStartedSuccessfully', '{{visit}} started successfully', {
419
+ visit: t('offlineVisit', 'Offline Visit'),
420
+ }),
421
+ title: t('visitStarted', 'Visit started'),
422
+ });
423
+ await closeWorkspace({ discardUnsavedChanges: true });
424
+ onVisitStarted?.(visit);
425
+ },
426
+ (error: Error) => {
427
+ showSnackbar({
428
+ title: t('startVisitError', 'Error starting visit'),
429
+ kind: 'error',
430
+ isLowContrast: false,
431
+ subtitle: error?.message,
432
+ });
433
+ },
434
+ );
435
+ }
436
+ },
437
+ [
438
+ closeWorkspace,
439
+ config.offlineVisitTypeUuid,
440
+ extraVisitInfo,
441
+ getErrorDescription,
442
+ globalMutate,
443
+ handleVisitAttributes,
444
+ isOnline,
445
+ onVisitStarted,
446
+ patientUuid,
447
+ t,
448
+ visitFormCallbacks,
449
+ visitToEdit,
450
+ ],
451
+ );
452
+
453
+ if (!workspaceProps) {
454
+ return null;
455
+ }
456
+
457
+ return (
458
+ <Workspace2
459
+ title={visitToEdit ? t('editVisit', 'Edit visit') : t('startVisitWorkspaceTitle', 'Start a visit')}
460
+ hasUnsavedChanges={isDirty}>
461
+ <FormProvider {...methods}>
462
+ <Form className={styles.form} onSubmit={handleSubmit(onSubmit)} data-openmrs-role="Start Visit Form">
463
+ {showPatientHeader && patient && (
464
+ <ExtensionSlot name="patient-header-slot" state={{ patient, patientUuid, hideActionsOverflow: true }} />
465
+ )}
466
+ {errorFetchingResources && (
467
+ <InlineNotification
468
+ kind={errorFetchingResources?.blockSavingForm ? 'error' : 'warning'}
469
+ lowContrast
470
+ className={styles.inlineNotification}
471
+ title={t('partOfFormDidntLoad', 'Part of the form did not load')}
472
+ subtitle={t('refreshToTryAgain', 'Please refresh to try again')}
473
+ />
474
+ )}
475
+ <div>
476
+ {isTablet && (
477
+ <Row className={styles.headerGridRow}>
478
+ <ExtensionSlot
479
+ name="visit-form-header-slot"
480
+ className={styles.dataGridRow}
481
+ state={visitHeaderSlotState}
482
+ />
483
+ </Row>
484
+ )}
485
+ <Stack gap={4} className={styles.container}>
486
+ <section>
487
+ <FormGroup legendText={t('theVisitIs', 'The visit is')}>
488
+ <Controller
489
+ name="visitStatus"
490
+ control={control}
491
+ render={({ field: { onChange, value } }) => {
492
+ const validVisitStatuses = visitToEdit ? ['ongoing', 'past'] : visitStatuses;
493
+ const idx = validVisitStatuses.indexOf(value);
494
+ const selectedIndex = idx >= 0 ? idx : 0;
495
+ return visitToEdit ? (
496
+ <ContentSwitcher
497
+ selectedIndex={selectedIndex}
498
+ onChange={({ name }) => onChange(name)}
499
+ size="md">
500
+ <Switch name="ongoing">{t('ongoing', 'Ongoing')}</Switch>
501
+ <Switch name="past">{t('ended', 'Ended')}</Switch>
502
+ </ContentSwitcher>
503
+ ) : (
504
+ <ContentSwitcher
505
+ selectedIndex={selectedIndex}
506
+ onChange={({ name }) => onChange(name)}
507
+ size="md">
508
+ <Switch name="new">{t('new', 'New')}</Switch>
509
+ <Switch name="ongoing">{t('ongoing', 'Ongoing')}</Switch>
510
+ <Switch name="past">{t('inThePast', 'In the past')}</Switch>
511
+ </ContentSwitcher>
512
+ );
513
+ }}
514
+ />
515
+ </FormGroup>
516
+ </section>
517
+ {hasActiveVisitConflict && (
518
+ <InlineNotification
519
+ className={styles.inlineNotification}
520
+ kind="info"
521
+ lowContrast
522
+ title={t('activeVisitExists', 'This patient already has an active visit')}
523
+ subtitle={t('endActiveVisitFirst', 'You must end the current visit before starting a new one.')}
524
+ />
525
+ )}
526
+ {!hasActiveVisitConflict && (
527
+ <>
528
+ <VisitDateTimeSection
529
+ {...{ control, firstEncounterDateTime, lastEncounterDateTime }}
530
+ earliestStartDate={earliestAllowedStartDate?.getTime()}
531
+ />
532
+ {config.showUpcomingAppointments && (
533
+ <section>
534
+ <div className={styles.sectionField}>
535
+ <VisitFormExtensionSlot
536
+ name="visit-form-top-slot"
537
+ visitStatus={visitStatus}
538
+ patientUuid={patientUuid}
539
+ visitFormOpenedFrom={openedFrom}
540
+ setVisitFormCallbacks={setVisitFormCallbacks}
541
+ />
542
+ </div>
543
+ </section>
544
+ )}
545
+ <LocationSelector control={control} />
546
+ {config.showRecommendedVisitTypeTab && (
547
+ <section>
548
+ <h1 className={styles.sectionTitle}>{t('program', 'Program')}</h1>
549
+ <FormGroup
550
+ legendText={t('selectProgramType', 'Select program type')}
551
+ className={styles.sectionField}>
552
+ <Controller
553
+ name="programType"
554
+ control={control}
555
+ render={({ field: { onChange } }) => (
556
+ <RadioButtonGroup
557
+ orientation="vertical"
558
+ onChange={(selection: string | number | undefined) =>
559
+ onChange(
560
+ activePatientEnrollment.find(
561
+ ({ program }) => program.uuid === String(selection ?? ''),
562
+ )?.uuid,
563
+ )
564
+ }
565
+ name="program-type-radio-group">
566
+ {activePatientEnrollment.map(({ uuid, display, program }) => (
567
+ <RadioButton
568
+ key={uuid}
569
+ className={styles.radioButton}
570
+ id={uuid}
571
+ labelText={display}
572
+ value={program.uuid}
573
+ />
574
+ ))}
575
+ </RadioButtonGroup>
576
+ )}
577
+ />
578
+ </FormGroup>
579
+ </section>
580
+ )}
581
+ {!emrConfiguration?.atFacilityVisitType && (
582
+ <section>
583
+ <h1 className={styles.sectionTitle}>{t('visitType_title', 'Visit Type')}</h1>
584
+ <div className={styles.sectionField}>
585
+ {config.showRecommendedVisitTypeTab ? (
586
+ <>
587
+ <ContentSwitcher
588
+ selectedIndex={visitTypeContentSwitcherIndex}
589
+ onChange={({ index }) => setVisitTypeContentSwitcherIndex(index ?? 0)}
590
+ size="md">
591
+ <Switch name="recommended">{t('recommended', 'Recommended')}</Switch>
592
+ <Switch name="all">{t('all', 'All')}</Switch>
593
+ </ContentSwitcher>
594
+ {visitTypeContentSwitcherIndex === 0 &&
595
+ !isLoading &&
596
+ (() => {
597
+ const enrollment = activePatientEnrollment?.find(
598
+ ({ program }) => program.uuid === getValues('programType'),
599
+ );
600
+ const locationUuid = getValues('visitLocation')?.uuid;
601
+ return enrollment && locationUuid ? (
602
+ <MemoizedRecommendedVisitType
603
+ patientUuid={patientUuid}
604
+ patientProgramEnrollment={enrollment}
605
+ locationUuid={locationUuid}
606
+ />
607
+ ) : null;
608
+ })()}
609
+ {visitTypeContentSwitcherIndex === 1 && <BaseVisitType visitTypes={allVisitTypes} />}
610
+ </>
611
+ ) : (
612
+ <BaseVisitType visitTypes={allVisitTypes} />
613
+ )}
614
+ </div>
615
+ {errors?.visitType && (
616
+ <section>
617
+ <div className={styles.sectionField}>
618
+ <InlineNotification
619
+ role="alert"
620
+ style={{ margin: '0', minWidth: '100%' }}
621
+ kind="error"
622
+ lowContrast={true}
623
+ title={t('missingVisitType', 'Missing visit type')}
624
+ subtitle={t('selectVisitType', 'Please select a Visit Type')}
625
+ />
626
+ </div>
627
+ </section>
628
+ )}
629
+ </section>
630
+ )}
631
+ <ExtensionSlot state={{ patientUuid, setExtraVisitInfo }} name="extra-visit-attribute-slot" />
632
+ <section>
633
+ <h1 className={styles.sectionTitle}>{isTablet && t('visitAttributes', 'Visit attributes')}</h1>
634
+ <div className={styles.sectionField}>
635
+ <VisitAttributeTypeFields setErrorFetchingResources={setErrorFetchingResourcesAdapter} />
636
+ </div>
637
+ </section>
638
+ <section>
639
+ <div className={styles.sectionField}>
640
+ <VisitFormExtensionSlot
641
+ name="visit-form-bottom-slot"
642
+ visitStatus={visitStatus}
643
+ patientUuid={patientUuid}
644
+ visitFormOpenedFrom={openedFrom}
645
+ setVisitFormCallbacks={setVisitFormCallbacks}
646
+ visitTypeUuid={visitType}
647
+ />
648
+ </div>
649
+ </section>
650
+ </>
651
+ )}
652
+ </Stack>
653
+ </div>
654
+ <ButtonSet
655
+ className={classNames(styles.buttonSet, {
656
+ [styles.tablet]: isTablet,
657
+ [styles.desktop]: !isTablet,
658
+ })}>
659
+ <Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
660
+ {t('discard', 'Discard')}
661
+ </Button>
662
+ <Button
663
+ className={styles.button}
664
+ disabled={
665
+ isSubmitting ||
666
+ isLoadingVisit ||
667
+ isLoadingBirthdateCheck ||
668
+ isLoadingOverlapSetting ||
669
+ errorFetchingResources?.blockSavingForm ||
670
+ hasActiveVisitConflict
671
+ }
672
+ kind="primary"
673
+ type="submit">
674
+ {isSubmitting ? (
675
+ <InlineLoading
676
+ className={styles.spinner}
677
+ description={
678
+ visitToEdit
679
+ ? t('updatingVisit', 'Updating visit') + '...'
680
+ : isSHAVisit
681
+ ? t('sendingOtp', 'Sending OTP') + '...'
682
+ : t('startingVisit', 'Starting visit') + '...'
683
+ }
684
+ />
685
+ ) : (
686
+ <span>
687
+ {visitToEdit
688
+ ? t('updateVisit', 'Update visit')
689
+ : isSHAVisit
690
+ ? t('sendOtpAndStartVisit', 'Send OTP & Start Visit')
691
+ : t('startVisit', 'Start visit')}
692
+ </span>
693
+ )}
694
+ </Button>
695
+ </ButtonSet>
696
+ </Form>
697
+ </FormProvider>
698
+ </Workspace2>
699
+ );
700
+ };
701
+
702
+ interface VisitFormExtensionSlotProps {
703
+ name: string;
704
+ patientUuid: string;
705
+ visitStatus: string;
706
+ visitFormOpenedFrom: string;
707
+ setVisitFormCallbacks: React.Dispatch<React.SetStateAction<Map<string, VisitFormCallbacks>>>;
708
+ visitTypeUuid?: string;
709
+ }
710
+
711
+ type VisitFormExtensionState = {
712
+ patientUuid: string;
713
+ setVisitFormCallbacks: (callbacks: VisitFormCallbacks) => void;
714
+ visitFormOpenedFrom: string;
715
+ visitStatus: string;
716
+ patientChartConfig: ExpressWorkflowConfig;
717
+ visitTypeUuid?: string;
718
+ };
719
+
720
+ const VisitFormExtensionSlot: React.FC<VisitFormExtensionSlotProps> = React.memo(
721
+ ({ name, patientUuid, visitFormOpenedFrom, setVisitFormCallbacks, visitStatus, visitTypeUuid }) => {
722
+ const config = useConfig<ExpressWorkflowConfig>();
723
+ return (
724
+ <ExtensionSlot name={name}>
725
+ {(extension: AssignedExtension) => {
726
+ const state: VisitFormExtensionState = {
727
+ patientUuid,
728
+ setVisitFormCallbacks: (callbacks: VisitFormCallbacks): void => {
729
+ setVisitFormCallbacks((old) => new Map(old).set(extension.id, callbacks));
730
+ },
731
+ visitFormOpenedFrom,
732
+ visitStatus,
733
+ patientChartConfig: config,
734
+ visitTypeUuid,
735
+ };
736
+ return <Extension state={state} />;
737
+ }}
738
+ </ExtensionSlot>
739
+ );
740
+ },
741
+ );
742
+
743
+ export default ExportedVisitForm;