@bathiran212/esm-patient-chart-app 7.0.0

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 (382) hide show
  1. package/README.md +3 -0
  2. package/dist/108.js +1 -0
  3. package/dist/108.js.map +1 -0
  4. package/dist/1339.js +1 -0
  5. package/dist/1339.js.map +1 -0
  6. package/dist/1480.js +1 -0
  7. package/dist/1480.js.map +1 -0
  8. package/dist/1543.js +1 -0
  9. package/dist/1543.js.map +1 -0
  10. package/dist/1582.js +1 -0
  11. package/dist/1582.js.map +1 -0
  12. package/dist/1646.js +1 -0
  13. package/dist/1646.js.map +1 -0
  14. package/dist/1797.js +1 -0
  15. package/dist/1797.js.map +1 -0
  16. package/dist/1869.js +1 -0
  17. package/dist/1869.js.map +1 -0
  18. package/dist/1877.js +1 -0
  19. package/dist/1877.js.map +1 -0
  20. package/dist/2020.js +1 -0
  21. package/dist/2020.js.map +1 -0
  22. package/dist/2246.js +1 -0
  23. package/dist/2246.js.map +1 -0
  24. package/dist/2317.js +1 -0
  25. package/dist/2317.js.map +1 -0
  26. package/dist/2416.js +1 -0
  27. package/dist/2416.js.map +1 -0
  28. package/dist/2790.js +1 -0
  29. package/dist/2790.js.map +1 -0
  30. package/dist/282.js +1 -0
  31. package/dist/282.js.map +1 -0
  32. package/dist/2881.js +1 -0
  33. package/dist/2881.js.map +1 -0
  34. package/dist/3137.js +1 -0
  35. package/dist/3137.js.map +1 -0
  36. package/dist/3378.js +1 -0
  37. package/dist/3378.js.map +1 -0
  38. package/dist/3390.js +1 -0
  39. package/dist/3390.js.map +1 -0
  40. package/dist/3536.js +1 -0
  41. package/dist/3536.js.map +1 -0
  42. package/dist/3720.js +1 -0
  43. package/dist/3720.js.map +1 -0
  44. package/dist/3857.js +1 -0
  45. package/dist/3857.js.map +1 -0
  46. package/dist/3925.js +1 -0
  47. package/dist/3925.js.map +1 -0
  48. package/dist/3963.js +1 -0
  49. package/dist/3963.js.map +1 -0
  50. package/dist/3989.js +1 -0
  51. package/dist/3989.js.map +1 -0
  52. package/dist/4092.js +1 -0
  53. package/dist/4092.js.map +1 -0
  54. package/dist/4106.js +1 -0
  55. package/dist/4106.js.map +1 -0
  56. package/dist/4111.js +1 -0
  57. package/dist/4111.js.map +1 -0
  58. package/dist/4145.js +1 -0
  59. package/dist/4145.js.map +1 -0
  60. package/dist/434.js +1 -0
  61. package/dist/434.js.map +1 -0
  62. package/dist/4348.js +1 -0
  63. package/dist/4348.js.map +1 -0
  64. package/dist/4383.js +1 -0
  65. package/dist/4383.js.map +1 -0
  66. package/dist/4540.js +1 -0
  67. package/dist/4540.js.map +1 -0
  68. package/dist/4658.js +1 -0
  69. package/dist/4658.js.map +1 -0
  70. package/dist/466.js +1 -0
  71. package/dist/466.js.map +1 -0
  72. package/dist/4913.js +1 -0
  73. package/dist/4913.js.map +1 -0
  74. package/dist/4928.js +1 -0
  75. package/dist/4928.js.map +1 -0
  76. package/dist/5069.js +1 -0
  77. package/dist/5069.js.map +1 -0
  78. package/dist/5117.js +1 -0
  79. package/dist/5117.js.map +1 -0
  80. package/dist/5132.js +1 -0
  81. package/dist/5132.js.map +1 -0
  82. package/dist/5145.js +1 -0
  83. package/dist/5145.js.map +1 -0
  84. package/dist/52.js +1 -0
  85. package/dist/52.js.map +1 -0
  86. package/dist/5422.js +1 -0
  87. package/dist/5422.js.map +1 -0
  88. package/dist/5503.js +1 -0
  89. package/dist/5503.js.map +1 -0
  90. package/dist/5549.js +1 -0
  91. package/dist/5549.js.map +1 -0
  92. package/dist/556.js +1 -0
  93. package/dist/556.js.map +1 -0
  94. package/dist/5644.js +1 -0
  95. package/dist/5644.js.map +1 -0
  96. package/dist/5697.js +1 -0
  97. package/dist/5697.js.map +1 -0
  98. package/dist/5793.js +1 -0
  99. package/dist/5793.js.map +1 -0
  100. package/dist/5940.js +1 -0
  101. package/dist/5940.js.map +1 -0
  102. package/dist/5952.js +1 -0
  103. package/dist/5952.js.map +1 -0
  104. package/dist/6047.js +1 -0
  105. package/dist/6047.js.map +1 -0
  106. package/dist/6371.js +1 -0
  107. package/dist/6371.js.map +1 -0
  108. package/dist/6377.js +1 -0
  109. package/dist/6377.js.map +1 -0
  110. package/dist/6444.js +1 -0
  111. package/dist/6444.js.map +1 -0
  112. package/dist/6479.js +1 -0
  113. package/dist/6479.js.map +1 -0
  114. package/dist/6508.js +1 -0
  115. package/dist/6508.js.map +1 -0
  116. package/dist/6724.js +1 -0
  117. package/dist/6724.js.map +1 -0
  118. package/dist/6759.js +27 -0
  119. package/dist/6759.js.map +1 -0
  120. package/dist/689.js +1 -0
  121. package/dist/689.js.map +1 -0
  122. package/dist/6904.js +1 -0
  123. package/dist/6904.js.map +1 -0
  124. package/dist/7045.js +1 -0
  125. package/dist/7045.js.map +1 -0
  126. package/dist/7175.js +1 -0
  127. package/dist/7175.js.map +1 -0
  128. package/dist/7182.js +1 -0
  129. package/dist/7182.js.map +1 -0
  130. package/dist/7302.js +1 -0
  131. package/dist/7302.js.map +1 -0
  132. package/dist/7646.js +17 -0
  133. package/dist/7646.js.map +1 -0
  134. package/dist/7742.js +1 -0
  135. package/dist/7742.js.map +1 -0
  136. package/dist/7912.js +1 -0
  137. package/dist/7912.js.map +1 -0
  138. package/dist/8105.js +21 -0
  139. package/dist/8105.js.map +1 -0
  140. package/dist/8202.js +1 -0
  141. package/dist/8202.js.map +1 -0
  142. package/dist/8349.js +1 -0
  143. package/dist/8349.js.map +1 -0
  144. package/dist/8358.js +1 -0
  145. package/dist/8358.js.map +1 -0
  146. package/dist/8359.js +1 -0
  147. package/dist/8359.js.map +1 -0
  148. package/dist/8695.js +1 -0
  149. package/dist/8695.js.map +1 -0
  150. package/dist/8702.js +1 -0
  151. package/dist/8702.js.map +1 -0
  152. package/dist/8894.js +1 -0
  153. package/dist/8894.js.map +1 -0
  154. package/dist/8958.js +1 -0
  155. package/dist/8958.js.map +1 -0
  156. package/dist/903.js +1 -0
  157. package/dist/903.js.map +1 -0
  158. package/dist/9061.js +1 -0
  159. package/dist/9061.js.map +1 -0
  160. package/dist/9072.js +1 -0
  161. package/dist/9072.js.map +1 -0
  162. package/dist/9105.js +1 -0
  163. package/dist/9105.js.map +1 -0
  164. package/dist/9107.js +1 -0
  165. package/dist/9107.js.map +1 -0
  166. package/dist/9456.js +1 -0
  167. package/dist/9456.js.map +1 -0
  168. package/dist/9586.js +1 -0
  169. package/dist/9586.js.map +1 -0
  170. package/dist/9712.js +1 -0
  171. package/dist/9712.js.map +1 -0
  172. package/dist/9771.js +1 -0
  173. package/dist/9771.js.map +1 -0
  174. package/dist/9806.js +1 -0
  175. package/dist/9806.js.map +1 -0
  176. package/dist/9873.js +1 -0
  177. package/dist/9873.js.map +1 -0
  178. package/dist/9927.js +1 -0
  179. package/dist/9927.js.map +1 -0
  180. package/dist/main.js +6 -0
  181. package/dist/main.js.map +1 -0
  182. package/dist/openmrs-esm-patient-chart-app.js +6 -0
  183. package/dist/openmrs-esm-patient-chart-app.js.buildmanifest.json +2386 -0
  184. package/dist/openmrs-esm-patient-chart-app.js.map +1 -0
  185. package/dist/routes.json +1 -0
  186. package/package.json +63 -0
  187. package/rspack.config.js +1 -0
  188. package/src/actions-buttons/action-button.scss +3 -0
  189. package/src/actions-buttons/delete-visit.component.tsx +41 -0
  190. package/src/actions-buttons/delete-visit.test.tsx +26 -0
  191. package/src/actions-buttons/mark-patient-alive.component.tsx +42 -0
  192. package/src/actions-buttons/mark-patient-deceased.component.tsx +35 -0
  193. package/src/actions-buttons/start-visit.component.tsx +41 -0
  194. package/src/actions-buttons/start-visit.test.tsx +44 -0
  195. package/src/actions-buttons/stop-visit.component.tsx +39 -0
  196. package/src/actions-buttons/stop-visit.test.tsx +27 -0
  197. package/src/clinical-views/encounter-list/encounter-list-tabs.extension.tsx +78 -0
  198. package/src/clinical-views/encounter-list/encounter-list-tabs.scss +7 -0
  199. package/src/clinical-views/encounter-list/encounter-list.component.tsx +306 -0
  200. package/src/clinical-views/encounter-list/encounter-list.scss +36 -0
  201. package/src/clinical-views/encounter-list/table.component.tsx +63 -0
  202. package/src/clinical-views/encounter-list/table.scss +11 -0
  203. package/src/clinical-views/encounter-list/tag.component.test.tsx +307 -0
  204. package/src/clinical-views/encounter-list/tag.component.tsx +43 -0
  205. package/src/clinical-views/encounter-tile/clinical-views-summary.component.tsx +40 -0
  206. package/src/clinical-views/encounter-tile/encounter-tile.component.tsx +94 -0
  207. package/src/clinical-views/encounter-tile/tile.scss +82 -0
  208. package/src/clinical-views/hooks/index.ts +3 -0
  209. package/src/clinical-views/hooks/useEncounterRows.ts +60 -0
  210. package/src/clinical-views/hooks/useEncountersByVisit.ts +13 -0
  211. package/src/clinical-views/hooks/useFormsJson.ts +15 -0
  212. package/src/clinical-views/hooks/useLastEncounter.ts +29 -0
  213. package/src/clinical-views/types.ts +305 -0
  214. package/src/clinical-views/utils/concept-utils.ts +24 -0
  215. package/src/clinical-views/utils/encounter-list-config-builder.ts +160 -0
  216. package/src/clinical-views/utils/encounter-list.resource.ts +26 -0
  217. package/src/clinical-views/utils/helpers.ts +226 -0
  218. package/src/clinical-views/utils/index.ts +90 -0
  219. package/src/config-schema.ts +235 -0
  220. package/src/constants.ts +11 -0
  221. package/src/dashboard.meta.ts +15 -0
  222. package/src/data.resource.ts +117 -0
  223. package/src/declarations.d.ts +4 -0
  224. package/src/index.ts +204 -0
  225. package/src/loader/loader.component.tsx +11 -0
  226. package/src/loader/loader.scss +9 -0
  227. package/src/mark-patient-alive/mark-patient-alive.modal.tsx +54 -0
  228. package/src/mark-patient-deceased/mark-patient-deceased-form.scss +175 -0
  229. package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +203 -0
  230. package/src/mark-patient-deceased/mark-patient-deceased-form.workspace.tsx +295 -0
  231. package/src/offline.ts +41 -0
  232. package/src/patient-banner-tags/visit-attribute-tags.extension.tsx +62 -0
  233. package/src/patient-banner-tags/visit-attribute-tags.scss +8 -0
  234. package/src/patient-chart/chart-review/chart-review.component.tsx +138 -0
  235. package/src/patient-chart/chart-review/chart-review.test.tsx +77 -0
  236. package/src/patient-chart/chart-review/dashboard-view.component.tsx +85 -0
  237. package/src/patient-chart/chart-review/dashboard-view.scss +84 -0
  238. package/src/patient-chart/patient-chart.component.tsx +71 -0
  239. package/src/patient-chart/patient-chart.resources.test.ts +238 -0
  240. package/src/patient-chart/patient-chart.resources.ts +231 -0
  241. package/src/patient-chart/patient-chart.scss +65 -0
  242. package/src/patient-details-tile/patient-details-tile.component.tsx +25 -0
  243. package/src/patient-details-tile/patient-details-tile.scss +24 -0
  244. package/src/root.component.tsx +35 -0
  245. package/src/root.scss +54 -0
  246. package/src/routes.json +267 -0
  247. package/src/side-nav/side-menu.component.tsx +10 -0
  248. package/src/side-nav/side-menu.scss +38 -0
  249. package/src/side-nav/side-menu.test.tsx +27 -0
  250. package/src/utils.test.ts +17 -0
  251. package/src/utils.ts +5 -0
  252. package/src/visit/hooks/useDefaultFacilityLocation.tsx +15 -0
  253. package/src/visit/hooks/useDefaultVisitLocation.tsx +24 -0
  254. package/src/visit/hooks/useDeleteVisit.test.tsx +267 -0
  255. package/src/visit/hooks/useDeleteVisit.tsx +103 -0
  256. package/src/visit/hooks/useOfflineVisitType.tsx +18 -0
  257. package/src/visit/hooks/useRecommendedVisitTypes.tsx +34 -0
  258. package/src/visit/hooks/useVisitAttributeType.tsx +102 -0
  259. package/src/visit/start-visit-button.component.tsx +47 -0
  260. package/src/visit/start-visit-button.test.tsx +32 -0
  261. package/src/visit/visit-action-items/delete-visit-action-item.component.tsx +60 -0
  262. package/src/visit/visit-action-items/delete-visit-action-item.test.tsx +48 -0
  263. package/src/visit/visit-action-items/edit-visit-details.component.tsx +79 -0
  264. package/src/visit/visit-form/base-visit-type.component.tsx +121 -0
  265. package/src/visit/visit-form/base-visit-type.scss +75 -0
  266. package/src/visit/visit-form/base-visit-type.test.tsx +153 -0
  267. package/src/visit/visit-form/exported-visit-form.workspace.tsx +755 -0
  268. package/src/visit/visit-form/location-selector.component.tsx +86 -0
  269. package/src/visit/visit-form/location-selector.test.tsx +146 -0
  270. package/src/visit/visit-form/recommended-visit-type.component.tsx +32 -0
  271. package/src/visit/visit-form/visit-attribute-type.component.tsx +258 -0
  272. package/src/visit/visit-form/visit-attribute-type.scss +5 -0
  273. package/src/visit/visit-form/visit-date-time.component.tsx +206 -0
  274. package/src/visit/visit-form/visit-form.resource.ts +401 -0
  275. package/src/visit/visit-form/visit-form.scss +167 -0
  276. package/src/visit/visit-form/visit-form.test.tsx +1233 -0
  277. package/src/visit/visit-form/visit-form.workspace.tsx +61 -0
  278. package/src/visit/visit-form/visit-type.test.tsx +88 -0
  279. package/src/visit/visit-history-table/visit-actions-cell.component.tsx +20 -0
  280. package/src/visit/visit-history-table/visit-actions-cell.scss +4 -0
  281. package/src/visit/visit-history-table/visit-date-cell.component.tsx +19 -0
  282. package/src/visit/visit-history-table/visit-diagnoses-cell.component.tsx +18 -0
  283. package/src/visit/visit-history-table/visit-history-table.component.tsx +145 -0
  284. package/src/visit/visit-history-table/visit-history-table.scss +25 -0
  285. package/src/visit/visit-history-table/visit-type-cell.component.tsx +15 -0
  286. package/src/visit/visit-prompt/delete-visit-dialog.modal.tsx +46 -0
  287. package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +79 -0
  288. package/src/visit/visit-prompt/end-visit-dialog.modal.tsx +82 -0
  289. package/src/visit/visit-prompt/end-visit-dialog.scss +7 -0
  290. package/src/visit/visit-prompt/end-visit-dialog.test.tsx +131 -0
  291. package/src/visit/visit-prompt/modify-visit-date.modal.tsx +40 -0
  292. package/src/visit/visit-prompt/start-visit-dialog.modal.tsx +64 -0
  293. package/src/visit/visit-prompt/start-visit-dialog.scss +10 -0
  294. package/src/visit/visit-prompt/start-visit-dialog.test.tsx +40 -0
  295. package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.scss +7 -0
  296. package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.tsx +178 -0
  297. package/src/visit/visits-widget/current-visit-summary.extension.tsx +48 -0
  298. package/src/visit/visits-widget/current-visit-summary.scss +10 -0
  299. package/src/visit/visits-widget/current-visit-summary.test.tsx +85 -0
  300. package/src/visit/visits-widget/encounter-observations/encounter-observations.component.tsx +67 -0
  301. package/src/visit/visits-widget/encounter-observations/index.ts +3 -0
  302. package/src/visit/visits-widget/encounter-observations/styles.scss +22 -0
  303. package/src/visit/visits-widget/past-visits-components/delete-encounter.modal.tsx +47 -0
  304. package/src/visit/visits-widget/past-visits-components/delete-encounter.scss +9 -0
  305. package/src/visit/visits-widget/past-visits-components/encounters-table/all-encounters-table.component.tsx +49 -0
  306. package/src/visit/visits-widget/past-visits-components/encounters-table/completed-forms-table.component.tsx +67 -0
  307. package/src/visit/visits-widget/past-visits-components/encounters-table/completed-forms-table.test.tsx +146 -0
  308. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +452 -0
  309. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.test.ts +156 -0
  310. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.ts +215 -0
  311. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.scss +113 -0
  312. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.test.tsx +432 -0
  313. package/src/visit/visits-widget/past-visits-components/encounters-table/visit-completed-forms-table.component.tsx +61 -0
  314. package/src/visit/visits-widget/past-visits-components/encounters-table/visit-completed-forms-table.test.tsx +125 -0
  315. package/src/visit/visits-widget/past-visits-components/encounters-table/visit-encounters-table.component.tsx +47 -0
  316. package/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx +163 -0
  317. package/src/visit/visits-widget/past-visits-components/notes-summary.component.tsx +66 -0
  318. package/src/visit/visits-widget/past-visits-components/patient-notes-summary.component.tsx +318 -0
  319. package/src/visit/visits-widget/past-visits-components/tests-summary.component.tsx +16 -0
  320. package/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx +192 -0
  321. package/src/visit/visits-widget/past-visits-components/visit-summary.scss +72 -0
  322. package/src/visit/visits-widget/past-visits-components/visit-summary.test.tsx +105 -0
  323. package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.component.tsx +94 -0
  324. package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.scss +60 -0
  325. package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/restrospective-date-time-picker.scss +35 -0
  326. package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +140 -0
  327. package/src/visit/visits-widget/visit-context/visit-context-header.extension.tsx +61 -0
  328. package/src/visit/visits-widget/visit-context/visit-context-header.scss +45 -0
  329. package/src/visit/visits-widget/visit-context/visit-context-header.test.tsx +59 -0
  330. package/src/visit/visits-widget/visit-context/visit-context-info.component.tsx +37 -0
  331. package/src/visit/visits-widget/visit-context/visit-context-info.scss +12 -0
  332. package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +166 -0
  333. package/src/visit/visits-widget/visit-context/visit-context-switcher.scss +83 -0
  334. package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +79 -0
  335. package/src/visit/visits-widget/visit-detail-overview.component.tsx +67 -0
  336. package/src/visit/visits-widget/visit-detail-overview.scss +301 -0
  337. package/src/visit/visits-widget/visit-detail-overview.test.tsx +205 -0
  338. package/src/visit/visits-widget/visit.resource.tsx +146 -0
  339. package/translations/am.json +209 -0
  340. package/translations/ar.json +209 -0
  341. package/translations/ar_SY.json +209 -0
  342. package/translations/bn.json +209 -0
  343. package/translations/cs.json +209 -0
  344. package/translations/de.json +209 -0
  345. package/translations/en.json +209 -0
  346. package/translations/en_US.json +209 -0
  347. package/translations/es.json +209 -0
  348. package/translations/es_MX.json +209 -0
  349. package/translations/fr.json +209 -0
  350. package/translations/he.json +209 -0
  351. package/translations/hi.json +209 -0
  352. package/translations/hi_IN.json +209 -0
  353. package/translations/id.json +209 -0
  354. package/translations/it.json +209 -0
  355. package/translations/ka.json +209 -0
  356. package/translations/km.json +209 -0
  357. package/translations/ku.json +209 -0
  358. package/translations/ky.json +209 -0
  359. package/translations/lg.json +209 -0
  360. package/translations/ne.json +209 -0
  361. package/translations/pl.json +209 -0
  362. package/translations/pt.json +209 -0
  363. package/translations/pt_BR.json +209 -0
  364. package/translations/qu.json +209 -0
  365. package/translations/ro_RO.json +209 -0
  366. package/translations/ru_RU.json +209 -0
  367. package/translations/si.json +209 -0
  368. package/translations/sq.json +209 -0
  369. package/translations/sw.json +209 -0
  370. package/translations/sw_KE.json +209 -0
  371. package/translations/tr.json +209 -0
  372. package/translations/tr_TR.json +209 -0
  373. package/translations/uk.json +209 -0
  374. package/translations/uz.json +209 -0
  375. package/translations/uz@Latn.json +209 -0
  376. package/translations/uz_UZ.json +209 -0
  377. package/translations/vi.json +209 -0
  378. package/translations/zh.json +209 -0
  379. package/translations/zh_CN.json +209 -0
  380. package/translations/zh_TW.json +209 -0
  381. package/tsconfig.json +4 -0
  382. package/vitest.config.ts +4 -0
@@ -0,0 +1,140 @@
1
+ import { SelectItem, TimePickerSelect, TimePicker, Checkbox } from '@carbon/react';
2
+ import { OpenmrsDatePicker, ResponsiveWrapper, useFeatureFlag, type Visit } from '@openmrs/esm-framework';
3
+ import React, { useEffect, useState } from 'react';
4
+ import { type Control, Controller, useForm } from 'react-hook-form';
5
+ import { useTranslation } from 'react-i18next';
6
+ import styles from './restrospective-date-time-picker.scss';
7
+
8
+ type FormValues = {
9
+ retrospectiveDate: Date;
10
+ retrospectiveTime: string;
11
+ retrospectiveTimeFormat: string;
12
+ };
13
+
14
+ type RetrospectiveDateTimePickerProps = {
15
+ patientUuid: string;
16
+ visitContext: Visit;
17
+ control?: Control<FormValues>;
18
+ onChange?: (data: FormValues) => void;
19
+ };
20
+
21
+ const RetrospectiveDateTimePicker = ({
22
+ visitContext,
23
+ control: propControl,
24
+ onChange,
25
+ }: RetrospectiveDateTimePickerProps) => {
26
+ const { t } = useTranslation();
27
+ const isRdeEnabled = useFeatureFlag('rde');
28
+
29
+ const isActiveVisit = !Boolean(visitContext && visitContext.stopDatetime);
30
+ const maxDate = visitContext?.stopDatetime;
31
+ const minDate = visitContext?.startDatetime;
32
+
33
+ const [manuallyEnableDateTimePicker, setManuallyEnableDateTimePicker] = useState<boolean>(false);
34
+
35
+ // disable inputs if user has not manually enabled the picker.
36
+ const disableInputs = isActiveVisit && !manuallyEnableDateTimePicker;
37
+
38
+ // the below form is for when the caller does not pass a control prop
39
+ // when this happens we assume the caller passed an onChange function which we
40
+ // call to supply the data
41
+ const form = useForm<FormValues>();
42
+
43
+ const retrospectiveDate = form.watch('retrospectiveDate');
44
+ const retrospectiveTime = form.watch('retrospectiveTime');
45
+ const retrospectiveTimeFormat = form.watch('retrospectiveTimeFormat');
46
+
47
+ const control = propControl || form.control;
48
+
49
+ useEffect(() => {
50
+ if (onChange) {
51
+ onChange({
52
+ retrospectiveDate: retrospectiveDate,
53
+ retrospectiveTime: retrospectiveTime,
54
+ retrospectiveTimeFormat: retrospectiveTimeFormat,
55
+ });
56
+ }
57
+ }, [onChange, retrospectiveDate, retrospectiveTime, retrospectiveTimeFormat]);
58
+
59
+ if (!isRdeEnabled) {
60
+ return null;
61
+ }
62
+
63
+ return (
64
+ <section className={styles.wrapper}>
65
+ <h4 className={styles.heading}>Encounter time</h4>
66
+ {isActiveVisit && (
67
+ <Checkbox
68
+ checked={manuallyEnableDateTimePicker}
69
+ className={styles.checkbox}
70
+ id={'enable-date-time-picker'}
71
+ labelText={t('enable', 'Enable')}
72
+ onChange={(_, { checked, id }) => {
73
+ setManuallyEnableDateTimePicker(checked);
74
+ }}
75
+ />
76
+ )}
77
+ <div className={styles.pickerWrapper}>
78
+ <Controller
79
+ name={'retrospectiveDate'}
80
+ control={control}
81
+ render={({ field, fieldState }) => (
82
+ <ResponsiveWrapper>
83
+ <OpenmrsDatePicker
84
+ {...field}
85
+ id={'retrospective-date-picker-input'}
86
+ labelText={t('date', 'Date')}
87
+ invalid={Boolean(fieldState?.error?.message)}
88
+ invalidText={fieldState?.error?.message}
89
+ isDisabled={disableInputs}
90
+ maxDate={maxDate}
91
+ minDate={minDate}
92
+ className={styles.datePicker}
93
+ />
94
+ </ResponsiveWrapper>
95
+ )}
96
+ />
97
+ <ResponsiveWrapper>
98
+ <Controller
99
+ name={'retrospectiveTime'}
100
+ control={control}
101
+ render={({ field: { onBlur, onChange, value } }) => (
102
+ <div className={styles.timePickerWrapper}>
103
+ <TimePicker
104
+ id={'retrospective-time-picker-input'}
105
+ labelText={t('time', 'Time')}
106
+ onBlur={onBlur}
107
+ onChange={(event) => onChange(event.target.value)}
108
+ pattern="^(0[1-9]|1[0-2]):([0-5][0-9])$"
109
+ value={value}
110
+ disabled={disableInputs}
111
+ className={styles.timePicker}
112
+ >
113
+ <Controller
114
+ name={'retrospectiveTimeFormat'}
115
+ control={control}
116
+ render={({ field: { onChange, value } }) => (
117
+ <TimePickerSelect
118
+ aria-label={t('timeFormat ', 'Time Format')}
119
+ id={`am-pm-input`}
120
+ onChange={(event) => onChange(event.target.value)}
121
+ value={value}
122
+ disabled={disableInputs}
123
+ >
124
+ <SelectItem value="" text="" />
125
+ <SelectItem value="AM" text={t('AM', 'AM')} />
126
+ <SelectItem value="PM" text={t('PM', 'PM')} />
127
+ </TimePickerSelect>
128
+ )}
129
+ />
130
+ </TimePicker>
131
+ </div>
132
+ )}
133
+ />
134
+ </ResponsiveWrapper>
135
+ </div>
136
+ </section>
137
+ );
138
+ };
139
+
140
+ export default RetrospectiveDateTimePicker;
@@ -0,0 +1,61 @@
1
+ import { Button } from '@carbon/react';
2
+ import { showModal, useFeatureFlag } from '@openmrs/esm-framework';
3
+ import classNames from 'classnames';
4
+ import React from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import styles from './visit-context-header.scss';
7
+ import VisitContextInfo from './visit-context-info.component';
8
+ import { usePatientChartStore, useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
9
+
10
+ interface VisitContextHeaderProps {
11
+ patientUuid: string;
12
+ }
13
+
14
+ /**
15
+ * The visit context header displays the currently select visit context in the patient chart store.
16
+ * When creating encounters (ex: visit notes, order entry, vitals), they are associated
17
+ * with the visit in context.
18
+ *
19
+ * This extension uses the patient chart store and should only be shown within the patient chart.
20
+ * It does not render when mounted outside the patient chart.
21
+ */
22
+ const VisitContextHeader: React.FC<VisitContextHeaderProps> = ({ patientUuid }) => {
23
+ const { t } = useTranslation();
24
+ const { systemVisitEnabled } = useSystemVisitSetting();
25
+ const isRdeEnabled = useFeatureFlag('rde');
26
+
27
+ const { visitContext } = usePatientChartStore(patientUuid);
28
+ const isActiveVisit = Boolean(visitContext && !visitContext.stopDatetime);
29
+
30
+ const showVisitContextHeader = systemVisitEnabled && isRdeEnabled && visitContext;
31
+
32
+ const openVisitSwitcherModal = () => {
33
+ const dispose = showModal('visit-context-switcher', {
34
+ patientUuid,
35
+ closeModal: () => dispose(),
36
+ size: 'sm',
37
+ });
38
+ };
39
+
40
+ if (!showVisitContextHeader) {
41
+ return null;
42
+ }
43
+ return (
44
+ <div
45
+ className={classNames(styles.visitContextHeader, isActiveVisit ? styles.activeVisit : styles.retroactiveVisit)}
46
+ >
47
+ <div className={styles.addingTo}>{t('addingToVisit', 'Adding to:')}</div>
48
+ <div className={styles.visitType}>{visitContext.visitType?.display}</div>
49
+ <div className={styles.changeVisitButton}>
50
+ <Button kind="ghost" size="sm" onClick={openVisitSwitcherModal}>
51
+ {t('change', 'Change')}
52
+ </Button>
53
+ </div>
54
+ <div className={styles.visitInfo}>
55
+ <VisitContextInfo visit={visitContext} />
56
+ </div>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default VisitContextHeader;
@@ -0,0 +1,45 @@
1
+ @use '@carbon/layout';
2
+
3
+ .visitContextHeader {
4
+ display: grid;
5
+ column-gap: layout.$spacing-03;
6
+ padding: layout.$spacing-03;
7
+ font-size: layout.$spacing-04;
8
+ grid-template-areas:
9
+ 'addingTo visitType changeVisitButton'
10
+ 'addingTo visitInfo visitInfo';
11
+ grid-template-columns: max-content 1fr min-content;
12
+ }
13
+
14
+ .activeVisit {
15
+ background-color: #defbe6;
16
+ }
17
+
18
+ .retroactiveVisit {
19
+ background-color: #f4f4f4;
20
+ }
21
+
22
+ .addingTo {
23
+ grid-area: addingTo;
24
+ }
25
+
26
+ .visitType {
27
+ font-size: 14px;
28
+ font-weight: 600;
29
+ grid-area: visitType;
30
+ }
31
+
32
+ .changeVisitButton {
33
+ grid-area: changeVisitButton;
34
+ :global(.cds--btn) {
35
+ min-height: 0;
36
+ padding: 0;
37
+ }
38
+ }
39
+
40
+ .visitInfo {
41
+ grid-area: visitInfo;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-items: center;
45
+ }
@@ -0,0 +1,59 @@
1
+ import { usePatientChartStore, useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
2
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { mockCurrentVisit } from '__mocks__';
5
+ import React from 'react';
6
+ import VisitContextHeader from './visit-context-header.extension';
7
+ import { mockPatient } from 'tools';
8
+
9
+ const mockUsePatientChartStore = vi.mocked(usePatientChartStore);
10
+ const mockUseSystemVisitSetting = vi.mocked(useSystemVisitSetting);
11
+
12
+ vi.mock('@openmrs/esm-patient-common-lib', () => ({
13
+ usePatientChartStore: vi.fn(),
14
+ useSystemVisitSetting: vi.fn(),
15
+ }));
16
+
17
+ describe('VisitContextHeader', () => {
18
+ beforeEach(() => {
19
+ mockUseSystemVisitSetting.mockReturnValue({
20
+ systemVisitEnabled: true,
21
+ errorFetchingSystemVisitSetting: null,
22
+ isLoadingSystemVisitSetting: false,
23
+ });
24
+ });
25
+
26
+ it('should not show header if system does not support visits', () => {
27
+ mockUseSystemVisitSetting.mockReturnValueOnce({
28
+ systemVisitEnabled: false,
29
+ errorFetchingSystemVisitSetting: null,
30
+ isLoadingSystemVisitSetting: false,
31
+ });
32
+ mockUsePatientChartStore.mockReturnValue({
33
+ patientUuid: mockPatient.id,
34
+ patient: mockPatient,
35
+ visitContext: null,
36
+ mutateVisitContext: null,
37
+ setPatient: vi.fn(),
38
+ setVisitContext: vi.fn(),
39
+ });
40
+
41
+ render(<VisitContextHeader patientUuid="some-uuid" />);
42
+ expect(screen.queryByText('Adding to')).not.toBeInTheDocument();
43
+ });
44
+
45
+ it('should show the current visit', () => {
46
+ mockUsePatientChartStore.mockReturnValue({
47
+ patientUuid: mockPatient.id,
48
+ patient: mockPatient,
49
+ visitContext: mockCurrentVisit,
50
+ mutateVisitContext: null,
51
+ setPatient: vi.fn(),
52
+ setVisitContext: vi.fn(),
53
+ });
54
+
55
+ render(<VisitContextHeader patientUuid={mockPatient.id} />);
56
+ expect(screen.getByText(/Adding to/i)).toBeInTheDocument();
57
+ expect(screen.getByText(mockCurrentVisit.visitType.display)).toBeInTheDocument();
58
+ });
59
+ });
@@ -0,0 +1,37 @@
1
+ import { Building } from '@carbon/react/icons';
2
+ import { formatDate, parseDate, type Visit } from '@openmrs/esm-framework';
3
+ import React from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import styles from './visit-context-info.scss';
6
+
7
+ interface VisitContextInfoProps {
8
+ visit: Visit;
9
+ }
10
+
11
+ const VisitContextInfo: React.FC<VisitContextInfoProps> = ({ visit }) => {
12
+ const { t } = useTranslation();
13
+
14
+ if (!visit) {
15
+ return null;
16
+ }
17
+
18
+ const isActive = !visit.stopDatetime;
19
+
20
+ return (
21
+ <div className={styles.visitContextInfoContainer}>
22
+ <span>
23
+ {isActive
24
+ ? t('currentActiveVisit', 'Current active visit')
25
+ : t('fromDateToDate', '{{fromDate}} - {{toDate}}', {
26
+ fromDate: formatDate(parseDate(visit.startDatetime), { time: false }),
27
+ toDate: formatDate(parseDate(visit.stopDatetime), { time: false }),
28
+ })}
29
+ </span>
30
+ <span className={styles.separator}>&middot;</span>
31
+ <Building />
32
+ <span className={styles.visitLocation}>{visit.location.display}</span>
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default VisitContextInfo;
@@ -0,0 +1,12 @@
1
+ @use '@carbon/layout';
2
+
3
+ .visitContextInfoContainer {
4
+ display: flex;
5
+ align-items: center;
6
+ justify-items: center;
7
+ }
8
+
9
+ .separator,
10
+ .visitLocation {
11
+ margin: layout.$spacing-02;
12
+ }
@@ -0,0 +1,166 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import dayjs from 'dayjs';
4
+ import { Button, ModalBody, ModalFooter, ModalHeader, RadioButton, InlineLoading, Tile } from '@carbon/react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import {
7
+ ErrorState,
8
+ launchWorkspace2,
9
+ OpenmrsDatePicker,
10
+ useDebounce,
11
+ useOnVisible,
12
+ type Visit,
13
+ } from '@openmrs/esm-framework';
14
+ import { invalidateVisitByUuid, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
15
+ import { useInfiniteVisits } from '../visit.resource';
16
+ import VisitContextInfo from './visit-context-info.component';
17
+ import styles from './visit-context-switcher.scss';
18
+ import { useSWRConfig } from 'swr';
19
+
20
+ interface VisitContextSwitcherProps {
21
+ patientUuid: string;
22
+ onAfterVisitSelected?(); // optional callback to run after visit is selected
23
+ closeModal();
24
+ }
25
+
26
+ const VisitContextSwitcherModal: React.FC<VisitContextSwitcherProps> = ({
27
+ patientUuid,
28
+ closeModal,
29
+ onAfterVisitSelected,
30
+ }) => {
31
+ const { t } = useTranslation();
32
+ const [maxStartDate, setMaxStartDate] = useState(new Date());
33
+ const maxStartDateDebounced = useDebounce(maxStartDate);
34
+
35
+ const rep = 'custom:(uuid,display,visitType,startDatetime,stopDatetime,location,patient)';
36
+ const { visits, isLoading, error, hasMore, loadMore } = useInfiniteVisits(
37
+ patientUuid,
38
+ { toStartDate: dayjs(maxStartDateDebounced).endOf('day').toISOString() },
39
+ rep,
40
+ );
41
+ const { visitContext, setVisitContext } = usePatientChartStore(patientUuid);
42
+ const [selectedVisitUuid, setSelectedVisitUuid] = useState<string>(visitContext?.uuid ?? null);
43
+ const { mutate: globalMutate } = useSWRConfig();
44
+
45
+ const onScrollToEnd = useCallback(() => {
46
+ if (hasMore) {
47
+ loadMore();
48
+ }
49
+ }, [hasMore, loadMore]);
50
+ const ref = useOnVisible(onScrollToEnd);
51
+
52
+ const openStartVisitWorkspace = () => {
53
+ closeModal();
54
+ launchWorkspace2('start-visit-workspace-form', {
55
+ openedFrom: 'visit-context-switcher',
56
+ });
57
+ };
58
+
59
+ return (
60
+ <>
61
+ <ModalHeader closeModal={closeModal} title={t('selectAVisit', 'Select a visit')}>
62
+ <OpenmrsDatePicker
63
+ id="visit-context-switcher-date-picker"
64
+ className={styles.datepicker}
65
+ labelText={t('showVisitWithStartDateOnOrPriorTo', 'Show visit with start date on or prior to:')}
66
+ maxDate={Date.now()}
67
+ value={maxStartDate}
68
+ onChange={setMaxStartDate}
69
+ />
70
+ </ModalHeader>
71
+ <ModalBody>
72
+ {error ? (
73
+ <ErrorState headerTitle={t('visits', 'visits')} error={error} />
74
+ ) : visits?.length === 0 ? (
75
+ <Tile className={styles.tile}>
76
+ <div className={styles.tileContent}>
77
+ <p className={styles.content}>{t('noVisitsToDisplay', 'No visits to display')}</p>
78
+ <p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
79
+ </div>
80
+ </Tile>
81
+ ) : (
82
+ <div>
83
+ {visits?.map((visit) => {
84
+ return (
85
+ <VisitCardRow
86
+ key={visit.uuid}
87
+ visit={visit}
88
+ setSelectedVisit={setSelectedVisitUuid}
89
+ isSelected={selectedVisitUuid === visit.uuid}
90
+ />
91
+ );
92
+ })}
93
+ {isLoading ? <InlineLoading description={t('loading', 'Loading')} /> : <span ref={ref} />}
94
+ </div>
95
+ )}
96
+ </ModalBody>
97
+ <div className={styles.createVisitButtonContainer}>
98
+ <Button kind="ghost" size="sm" onClick={openStartVisitWorkspace}>
99
+ {t('createNewVisit', 'Create new visit...')}
100
+ </Button>
101
+ </div>
102
+ <ModalFooter>
103
+ <Button kind="secondary" onClick={closeModal}>
104
+ {t('cancel', 'Cancel')}
105
+ </Button>
106
+ <Button
107
+ disabled={selectedVisitUuid === null || isLoading}
108
+ onClick={() => {
109
+ const selectedVisit = visits.find((v) => v.uuid === selectedVisitUuid);
110
+ const mutateVisitContext = () => invalidateVisitByUuid(globalMutate, selectedVisit.uuid);
111
+ setVisitContext(selectedVisit, mutateVisitContext);
112
+ onAfterVisitSelected?.();
113
+ closeModal();
114
+ }}
115
+ >
116
+ {t('continue', 'Continue')}
117
+ </Button>
118
+ </ModalFooter>
119
+ </>
120
+ );
121
+ };
122
+
123
+ interface VisitCardRowProps {
124
+ visit: Visit;
125
+ isSelected: boolean;
126
+ setSelectedVisit(visitUuid: string);
127
+ }
128
+
129
+ /**
130
+ * A clickable row within the visit context switcher to select a visit. This
131
+ * has slightly different UX than a regular radio button, as the entire card
132
+ * (not just the radio button and the label) is clickable
133
+ */
134
+ const VisitCardRow: React.FC<VisitCardRowProps> = ({ visit, setSelectedVisit: setSelected, isSelected }) => {
135
+ const isActive = !visit.stopDatetime;
136
+
137
+ return (
138
+ <div
139
+ className={classNames(
140
+ styles.visitCardRow,
141
+ isActive ? styles.activeVisit : styles.retroactiveVisit,
142
+ isSelected ? styles.isSelected : '',
143
+ )}
144
+ >
145
+ <div className={styles.visitInfoContainer}>
146
+ <div className={styles.visitType}>{visit.visitType.display}</div>
147
+ <div className={styles.visitInfo}>
148
+ <VisitContextInfo visit={visit} />
149
+ </div>
150
+ </div>
151
+ <div className={styles.visitCardRowRadioButton}>
152
+ <RadioButton
153
+ className={styles.visitRow}
154
+ id={`visit-card-row-${visit.uuid}`}
155
+ value={visit.uuid}
156
+ checked={isSelected}
157
+ labelText={visit.visitType.display}
158
+ onChange={(value) => setSelected(String(value))}
159
+ />
160
+ </div>
161
+ <button className={styles.visitCardRowButton} onClick={() => setSelected(visit.uuid)}></button>
162
+ </div>
163
+ );
164
+ };
165
+
166
+ export default VisitContextSwitcherModal;
@@ -0,0 +1,83 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+
6
+ .visitCardRow {
7
+ display: flex;
8
+ position: relative;
9
+ border: solid 1px #e0e0e0;
10
+ padding: layout.$spacing-03;
11
+
12
+ &.activeVisit {
13
+ background-color: #defbe6;
14
+ &:hover {
15
+ background-color: #b6f6c8;
16
+ }
17
+ }
18
+
19
+ &.retroactiveVisit {
20
+ background-color: #f4f4f4;
21
+ &:hover {
22
+ background-color: #e5e5e5;
23
+ }
24
+ }
25
+ }
26
+
27
+ .visitInfoContainer {
28
+ flex: 1;
29
+ }
30
+
31
+ .visitType {
32
+ font-size: 14px;
33
+ font-weight: 600;
34
+ }
35
+
36
+ .visitCardRowButton {
37
+ border: none;
38
+ padding: 0;
39
+
40
+ &::before {
41
+ content: '';
42
+ position: absolute;
43
+ inset: 0;
44
+ z-index: 1;
45
+ cursor: pointer;
46
+ }
47
+ }
48
+
49
+ .visitCardRowRadioButton {
50
+ display: flex;
51
+ align-items: center;
52
+ }
53
+
54
+ .createVisitButtonContainer {
55
+ padding: layout.$spacing-05;
56
+ }
57
+
58
+ .datepicker {
59
+ padding-top: layout.$spacing-05;
60
+ }
61
+
62
+ .tileContainer {
63
+ background-color: $ui-02;
64
+ padding: layout.$spacing-09 0;
65
+ }
66
+
67
+ .tile {
68
+ margin: auto;
69
+ margin-top: layout.$spacing-07;
70
+ width: fit-content;
71
+ }
72
+
73
+ .tileContent {
74
+ display: flex;
75
+ flex-direction: column;
76
+ align-items: center;
77
+ }
78
+
79
+ .content {
80
+ @include type.type-style('heading-compact-02');
81
+ color: $text-02;
82
+ margin-bottom: layout.$spacing-03;
83
+ }
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { vi, describe, it, expect } from 'vitest';
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
6
+ import { mockCurrentVisit, mockVisit2, mockVisit3 } from '__mocks__';
7
+ import { useInfiniteVisits } from '../visit.resource';
8
+ import VisitContextSwitcherModal from './visit-context-switcher.modal';
9
+
10
+ const mockUseSystemVisitSetting = vi.fn(useSystemVisitSetting).mockReturnValue({
11
+ errorFetchingSystemVisitSetting: null,
12
+ isLoadingSystemVisitSetting: false,
13
+ systemVisitEnabled: true,
14
+ });
15
+
16
+ const mockUseInfiniteVisits = vi.fn(useInfiniteVisits).mockReturnValue({
17
+ visits: [mockCurrentVisit, mockVisit2, mockVisit3],
18
+ error: null,
19
+ mutate: vi.fn(),
20
+ isValidating: false,
21
+ isLoading: false,
22
+ totalCount: 3,
23
+ hasMore: false,
24
+ loadMore: vi.fn(),
25
+ nextUri: '',
26
+ });
27
+
28
+ vi.mock('@openmrs/esm-patient-common-lib/src/useSystemVisitSetting', () => ({
29
+ useSystemVisitSetting: () => mockUseSystemVisitSetting(),
30
+ }));
31
+
32
+ vi.mock('../visit.resource', () => ({
33
+ useInfiniteVisits: () => mockUseInfiniteVisits('some-uuid'),
34
+ }));
35
+
36
+ const mockSetVisitContext = vi.fn();
37
+ vi.mock('@openmrs/esm-patient-common-lib', async () => ({
38
+ ...((await vi.importActual('@openmrs/esm-patient-common-lib')) as object),
39
+ usePatientChartStore: vi.fn(() => ({
40
+ visitContext: null,
41
+ setVisitContext: mockSetVisitContext,
42
+ })),
43
+ }));
44
+
45
+ describe('VisitContextSwitcherModal', () => {
46
+ it('should display a list of past visits', () => {
47
+ mockUseSystemVisitSetting.mockReturnValueOnce({
48
+ systemVisitEnabled: false,
49
+ errorFetchingSystemVisitSetting: null,
50
+ isLoadingSystemVisitSetting: false,
51
+ });
52
+ renderVisitContextSwitcherModal();
53
+ // location
54
+ expect(screen.getAllByText('Registration Desk')).toHaveLength(3);
55
+ // visit type - only check the visitType div elements
56
+ expect(screen.getAllByText('Facility Visit', { selector: '.visitType' })).toHaveLength(3);
57
+ });
58
+
59
+ it('should call setVisitContext when continue button is clicked with selected visit', async () => {
60
+ const user = userEvent.setup();
61
+
62
+ renderVisitContextSwitcherModal();
63
+
64
+ // Select a visit by clicking on the first visit card
65
+ const firstVisitRadio = screen.getAllByRole('radio', { name: /Facility Visit/ })[0];
66
+ await user.click(firstVisitRadio);
67
+
68
+ // Click the continue button
69
+ const continueButton = screen.getByRole('button', { name: /continue/i });
70
+ await user.click(continueButton);
71
+
72
+ // Verify setVisitContext was called with the selected visit
73
+ expect(mockSetVisitContext).toHaveBeenCalledWith(mockCurrentVisit, expect.any(Function));
74
+ });
75
+ });
76
+
77
+ function renderVisitContextSwitcherModal() {
78
+ render(<VisitContextSwitcherModal patientUuid="some-uuid" closeModal={vi.fn()} />);
79
+ }