@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,163 @@
1
+ import React from 'react';
2
+ import { capitalize } from 'lodash-es';
3
+ import { Tag, Tooltip } from '@carbon/react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { formatDate, useConfig } from '@openmrs/esm-framework';
6
+ import { EmptyState } from '@openmrs/esm-patient-common-lib';
7
+ import { type OrderItem } from '../visit.resource';
8
+ import { type ChartConfig } from '../../../config-schema';
9
+ import styles from '../visit-detail-overview.scss';
10
+
11
+ interface MedicationSummaryProps {
12
+ medications: Array<OrderItem>;
13
+ }
14
+
15
+ const MedicationSummary: React.FC<MedicationSummaryProps> = ({ medications }) => {
16
+ const { t } = useTranslation();
17
+ const { drugOrderTypeUUID } = useConfig<ChartConfig>();
18
+
19
+ const isDiscontinued = (order: OrderItem['order']) => {
20
+ if (!order?.dateStopped) {
21
+ return false;
22
+ }
23
+
24
+ return new Date(order.dateStopped) < new Date();
25
+ };
26
+
27
+ const drugOrders = medications?.filter((medication) => {
28
+ return medication?.order?.orderType?.uuid === drugOrderTypeUUID;
29
+ });
30
+
31
+ if (drugOrders.length === 0) {
32
+ return (
33
+ <EmptyState displayText={t('medications__lower', 'medications')} headerTitle={t('medications', 'Medications')} />
34
+ );
35
+ }
36
+
37
+ return (
38
+ <div className={styles.medicationRecord}>
39
+ {drugOrders.map(
40
+ (medication, index) =>
41
+ (medication?.order?.dose != null || medication?.order?.dosingInstructions) && (
42
+ <React.Fragment key={index}>
43
+ <div className={styles.medicationContainer}>
44
+ <div>
45
+ <p className={styles.bodyLong01}>
46
+ <strong>{capitalize(medication?.order?.drug?.display)}</strong>{' '}
47
+ {medication?.order?.drug?.strength && (
48
+ <>&mdash; {medication?.order?.drug?.strength?.toLowerCase()}</>
49
+ )}{' '}
50
+ {medication?.order?.doseUnits?.display && (
51
+ <>&mdash; {medication?.order?.doseUnits?.display?.toLowerCase()}</>
52
+ )}{' '}
53
+ {isDiscontinued(medication.order) && (
54
+ <Tooltip align="right" label={<>{formatDate(new Date(medication.order.dateStopped))}</>}>
55
+ <Tag type="gray" className={styles.tag}>
56
+ {t('discontinued', 'Discontinued')}
57
+ </Tag>
58
+ </Tooltip>
59
+ )}
60
+ </p>
61
+ <p className={styles.bodyLong01}>
62
+ {medication?.order?.dose != null ? (
63
+ <>
64
+ <span className={styles.label01}> {t('dose', 'Dose').toUpperCase()} </span>{' '}
65
+ <span className={styles.dosage}>
66
+ {medication?.order?.dose} {medication?.order?.doseUnits?.display?.toLowerCase()}
67
+ </span>{' '}
68
+ {medication.order?.route?.display && (
69
+ <span>&mdash; {medication?.order?.route?.display?.toLowerCase()} </span>
70
+ )}
71
+ {medication?.order?.frequency?.display && (
72
+ <>
73
+ {medication.order?.route?.display ? <>&mdash; </> : null}
74
+ {medication?.order?.frequency?.display?.toLowerCase()}{' '}
75
+ </>
76
+ )}
77
+ {medication?.order?.duration != null && (
78
+ <>
79
+ &mdash;{' '}
80
+ {t('orderDurationAndUnit', 'for {{duration}} {{durationUnit}}', {
81
+ duration: medication?.order?.duration,
82
+ durationUnit: medication?.order?.durationUnits?.display?.toLowerCase(),
83
+ })}
84
+ </>
85
+ )}
86
+ {medication?.order?.duration == null && (
87
+ <>&mdash; {t('orderIndefiniteDuration', 'Indefinite duration')}</>
88
+ )}
89
+ {medication?.order?.numRefills != null && medication?.order?.numRefills !== 0 && (
90
+ <span>
91
+ <span className={styles.label01}> &mdash; {t('refills', 'Refills').toUpperCase()}</span>{' '}
92
+ {medication?.order?.numRefills}
93
+ </span>
94
+ )}
95
+ {medication?.order?.dosingInstructions && (
96
+ <span> &mdash; {medication?.order?.dosingInstructions?.toLocaleLowerCase()}</span>
97
+ )}
98
+ </>
99
+ ) : (
100
+ <>
101
+ <span className={styles.label01}>
102
+ {t('dosingInstructions', 'Dosing Instructions').toUpperCase()}{' '}
103
+ </span>
104
+ <span className={styles.dosage}>{medication?.order?.dosingInstructions}</span>
105
+ {medication?.order?.duration != null && (
106
+ <span>
107
+ {' '}
108
+ &mdash;{' '}
109
+ {t('orderDurationAndUnit', 'for {{duration}} {{durationUnit}}', {
110
+ duration: medication?.order?.duration,
111
+ durationUnit: medication?.order?.durationUnits?.display?.toLowerCase(),
112
+ })}
113
+ </span>
114
+ )}
115
+ {medication?.order?.numRefills != null && medication?.order?.numRefills !== 0 && (
116
+ <span>
117
+ <span className={styles.label01}> &mdash; {t('refills', 'Refills').toUpperCase()}</span>{' '}
118
+ {medication?.order?.numRefills}
119
+ </span>
120
+ )}
121
+ </>
122
+ )}
123
+ </p>
124
+ <p className={styles.bodyLong01}>
125
+ {medication?.order?.orderReasonNonCoded ? (
126
+ <span>
127
+ <span className={styles.label01}>{t('indication', 'Indication').toUpperCase()}</span>{' '}
128
+ {medication?.order?.orderReasonNonCoded}
129
+ </span>
130
+ ) : null}
131
+ {medication?.order?.orderReasonNonCoded && medication?.order?.quantity != null && <>&mdash;</>}
132
+ {medication?.order?.quantity != null ? (
133
+ <span>
134
+ <span className={styles.label01}> {t('quantity', 'Quantity').toUpperCase()}</span>{' '}
135
+ {medication?.order?.quantity}
136
+ </span>
137
+ ) : null}
138
+ {medication?.order?.dateStopped ? (
139
+ <span className={styles.bodyShort01}>
140
+ <span className={styles.label01}>
141
+ {medication?.order?.quantity != null ? ` — ` : ''} {t('endDate', 'End date').toUpperCase()}
142
+ </span>{' '}
143
+ {formatDate(new Date(medication?.order?.dateStopped))}
144
+ </span>
145
+ ) : null}
146
+ </p>
147
+ </div>
148
+ </div>
149
+
150
+ <p className={styles.metadata}>
151
+ <div className={styles.startDateColumn}>
152
+ <span>{formatDate(new Date(medication.order.dateActivated))}</span> &middot;{' '}
153
+ <span>{medication.order.orderer?.display ?? '--'}</span>
154
+ </div>
155
+ </p>
156
+ </React.Fragment>
157
+ ),
158
+ )}
159
+ </div>
160
+ );
161
+ };
162
+
163
+ export default MedicationSummary;
@@ -0,0 +1,66 @@
1
+ import React, { useMemo } from 'react';
2
+ import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { useSession } from '@openmrs/esm-framework';
5
+ import type { Note } from '../visit.resource';
6
+ import { EmptyState } from '@openmrs/esm-patient-common-lib';
7
+ import styles from '../visit-detail-overview.scss';
8
+
9
+ interface NotesSummaryProps {
10
+ notes: Array<Note>;
11
+ }
12
+
13
+ const NotesSummary: React.FC<NotesSummaryProps> = ({ notes }) => {
14
+ const { t } = useTranslation();
15
+ const session = useSession();
16
+
17
+ const currentUserIdentifiers = useMemo(
18
+ () =>
19
+ new Set(
20
+ [
21
+ session?.currentProvider?.uuid,
22
+ (session?.currentProvider as { display?: string } | undefined)?.display,
23
+ session?.user?.uuid,
24
+ session?.user?.person?.uuid,
25
+ session?.user?.display,
26
+ ].filter((identifier): identifier is string => Boolean(identifier)),
27
+ ),
28
+ [session],
29
+ );
30
+
31
+ if (notes.length === 0) {
32
+ return <EmptyState displayText={t('notes__lower', 'notes')} headerTitle={t('notes', 'Notes')} />;
33
+ }
34
+
35
+ return (
36
+ <>
37
+ {notes.map((note: Note, index) => {
38
+ const isCurrentUserNote = [...(note.authorUuids ?? []), ...(note.authorNames ?? [])].some((identifier) =>
39
+ currentUserIdentifiers.has(identifier),
40
+ );
41
+
42
+ return (
43
+ <div className={styles.notesContainer} key={index}>
44
+ <p
45
+ className={classNames(styles.noteText, styles.bodyLong01, {
46
+ [styles.currentUserNoteText]: isCurrentUserNote,
47
+ })}
48
+ >
49
+ {note.note}
50
+ </p>
51
+ <p
52
+ className={classNames(styles.metadata, {
53
+ [styles.currentUserNoteMetadata]: isCurrentUserNote,
54
+ })}
55
+ >
56
+ {note.time} {note.provider.name ? <span>&middot; {note.provider.name} </span> : null}
57
+ {note.provider.role ? <span>&middot; {note.provider.role}</span> : null}
58
+ </p>
59
+ </div>
60
+ );
61
+ })}
62
+ </>
63
+ );
64
+ };
65
+
66
+ export default NotesSummary;
@@ -0,0 +1,318 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import classNames from 'classnames';
4
+ import {
5
+ DataTable,
6
+ DataTableSkeleton,
7
+ Pagination,
8
+ Tab,
9
+ TabList,
10
+ TabPanel,
11
+ TabPanels,
12
+ Table,
13
+ TableBody,
14
+ TableCell,
15
+ TableContainer,
16
+ TableExpandHeader,
17
+ TableExpandRow,
18
+ TableExpandedRow,
19
+ TableHead,
20
+ TableHeader,
21
+ TableRow,
22
+ Tabs,
23
+ Tile,
24
+ } from '@carbon/react';
25
+ import {
26
+ formatDatetime,
27
+ isDesktop,
28
+ parseDate,
29
+ useConfig,
30
+ useLayoutType,
31
+ useSession,
32
+ type Encounter,
33
+ type OpenmrsResource,
34
+ } from '@openmrs/esm-framework';
35
+ import { ErrorState } from '@openmrs/esm-patient-common-lib';
36
+ import type { ChartConfig } from '../../../config-schema';
37
+ import type { Note } from '../visit.resource';
38
+ import { useAllEncounters } from './encounters-table/encounters-table.resource';
39
+ import tableStyles from './encounters-table/encounters-table.scss';
40
+ import notesStyles from '../visit-detail-overview.scss';
41
+
42
+ interface PatientNotesSummaryProps {
43
+ patientUuid: string;
44
+ isTabActive: boolean;
45
+ }
46
+
47
+ interface EncounterProvider {
48
+ uuid?: string;
49
+ encounterRole?: OpenmrsResource;
50
+ provider?: {
51
+ uuid?: string;
52
+ person?: {
53
+ uuid?: string;
54
+ display?: string;
55
+ };
56
+ };
57
+ }
58
+
59
+ type EncounterWithNotes = Omit<Encounter, 'encounterProviders'> & {
60
+ auditInfo?: {
61
+ creator?: {
62
+ uuid?: string;
63
+ display?: string;
64
+ };
65
+ };
66
+ encounterProviders?: Array<EncounterProvider>;
67
+ };
68
+
69
+ interface PatientNote extends Note {
70
+ authorUuids: Array<string>;
71
+ authorNames: Array<string>;
72
+ datetime: string;
73
+ id: string;
74
+ notePreview: string;
75
+ }
76
+
77
+ const getNoteDatetime = (obsDatetime: string, encounterDatetime: string) => obsDatetime || encounterDatetime || '';
78
+
79
+ interface PatientNotesTableProps {
80
+ notes: Array<PatientNote>;
81
+ }
82
+
83
+ const PatientNotesTable: React.FC<PatientNotesTableProps> = ({ notes }) => {
84
+ const { t } = useTranslation();
85
+ const pageSizes = [10, 20, 30, 40, 50];
86
+ const [currentPage, setCurrentPage] = useState(1);
87
+ const [pageSize, setPageSize] = useState(10);
88
+ const desktopLayout = isDesktop(useLayoutType());
89
+
90
+ useEffect(() => {
91
+ setCurrentPage(1);
92
+ }, [notes]);
93
+
94
+ const tableHeaders = [
95
+ {
96
+ header: t('dateAndTime', 'Date & time'),
97
+ key: 'time',
98
+ },
99
+ {
100
+ header: t('note', 'Note'),
101
+ key: 'notePreview',
102
+ },
103
+ {
104
+ header: t('provider', 'Provider'),
105
+ key: 'providerName',
106
+ },
107
+ {
108
+ header: t('role', 'Role'),
109
+ key: 'providerRole',
110
+ },
111
+ ];
112
+
113
+ const paginatedNotes = useMemo(() => {
114
+ const startIndex = (currentPage - 1) * pageSize;
115
+ return notes.slice(startIndex, startIndex + pageSize);
116
+ }, [currentPage, notes, pageSize]);
117
+
118
+ const rows = useMemo(
119
+ () =>
120
+ paginatedNotes.map((note) => ({
121
+ id: note.id,
122
+ notePreview: <span className={notesStyles.notePreview}>{note.notePreview}</span>,
123
+ providerName: note.provider.name || '--',
124
+ providerRole: note.provider.role || '--',
125
+ time: note.time || '--',
126
+ })),
127
+ [paginatedNotes],
128
+ );
129
+
130
+ const notesById = useMemo(() => new Map(paginatedNotes.map((note) => [note.id, note])), [paginatedNotes]);
131
+
132
+ return (
133
+ <div className={tableStyles.container}>
134
+ <DataTable
135
+ headers={tableHeaders}
136
+ rows={rows}
137
+ size={desktopLayout ? 'sm' : 'lg'}
138
+ useZebraStyles={notes.length > 1}
139
+ >
140
+ {({ rows, headers, getTableProps, getHeaderProps, getExpandHeaderProps, getRowProps }) => (
141
+ <TableContainer className={tableStyles.tableContainer}>
142
+ <Table {...getTableProps()}>
143
+ <TableHead>
144
+ <TableRow>
145
+ <TableExpandHeader enableToggle {...getExpandHeaderProps()} />
146
+ {headers.map((header) => (
147
+ <TableHeader key={header.key} {...getHeaderProps({ header })}>
148
+ {header.header}
149
+ </TableHeader>
150
+ ))}
151
+ </TableRow>
152
+ </TableHead>
153
+ <TableBody>
154
+ {rows.map((row) => {
155
+ const note = notesById.get(row.id);
156
+
157
+ if (!note) {
158
+ return null;
159
+ }
160
+
161
+ return (
162
+ <React.Fragment key={row.id}>
163
+ <TableExpandRow {...getRowProps({ row })}>
164
+ {row.cells.map((cell) => (
165
+ <TableCell key={cell.id}>{cell.value}</TableCell>
166
+ ))}
167
+ </TableExpandRow>
168
+ {row.isExpanded ? (
169
+ <TableExpandedRow className={tableStyles.expandedRow} colSpan={headers.length + 1}>
170
+ <div className={notesStyles.noteTableDetails}>
171
+ <p className={classNames(notesStyles.noteText, notesStyles.bodyLong01)}>{note.note}</p>
172
+ <p className={notesStyles.metadata}>
173
+ {note.time} {note.provider.name ? <span>&middot; {note.provider.name} </span> : null}
174
+ {note.provider.role ? <span>&middot; {note.provider.role}</span> : null}
175
+ </p>
176
+ </div>
177
+ </TableExpandedRow>
178
+ ) : (
179
+ <TableExpandedRow className={tableStyles.hiddenRow} colSpan={headers.length + 1} />
180
+ )}
181
+ </React.Fragment>
182
+ );
183
+ })}
184
+ </TableBody>
185
+ </Table>
186
+ {rows.length === 0 && (
187
+ <div className={tableStyles.tileContainer}>
188
+ <Tile className={tableStyles.tile}>
189
+ <div className={tableStyles.tileContent}>
190
+ <p className={tableStyles.content}>{t('noNotesToDisplay', 'No notes to display')}</p>
191
+ </div>
192
+ </Tile>
193
+ </div>
194
+ )}
195
+ </TableContainer>
196
+ )}
197
+ </DataTable>
198
+ <Pagination
199
+ forwardText={t('nextPage', 'Next page')}
200
+ backwardText={t('previousPage', 'Previous page')}
201
+ page={currentPage}
202
+ pageSize={pageSize}
203
+ pageSizes={pageSizes}
204
+ totalItems={notes.length}
205
+ onChange={({ pageSize: newPageSize, page }) => {
206
+ if (newPageSize !== pageSize) {
207
+ setPageSize(newPageSize);
208
+ }
209
+ if (page !== currentPage) {
210
+ setCurrentPage(page);
211
+ }
212
+ }}
213
+ />
214
+ </div>
215
+ );
216
+ };
217
+
218
+ const PatientNotesSummary: React.FC<PatientNotesSummaryProps> = ({ patientUuid, isTabActive }) => {
219
+ const { t } = useTranslation();
220
+ const config = useConfig<ChartConfig>();
221
+ const session = useSession();
222
+ const { data: encounters, isLoading, error } = useAllEncounters(isTabActive ? patientUuid : null);
223
+
224
+ const currentUserIdentifiers = useMemo(
225
+ () =>
226
+ new Set(
227
+ [
228
+ session?.user?.uuid,
229
+ session?.user?.person?.uuid,
230
+ session?.user?.display,
231
+ session?.currentProvider?.uuid,
232
+ (session?.currentProvider as { display?: string } | undefined)?.display,
233
+ ].filter(Boolean),
234
+ ),
235
+ [session],
236
+ );
237
+
238
+ const allNotes: Array<PatientNote> = useMemo(() => {
239
+ return ((encounters as Array<EncounterWithNotes>) ?? [])
240
+ .flatMap((encounter) => {
241
+ const encounterProvider = encounter.encounterProviders?.[0];
242
+ const providerName = encounterProvider?.provider?.person?.display ?? '';
243
+ const providerRole = encounterProvider?.encounterRole?.display ?? '';
244
+ const authorUuids = [
245
+ encounter.auditInfo?.creator?.uuid,
246
+ encounterProvider?.provider?.uuid,
247
+ encounterProvider?.provider?.person?.uuid,
248
+ ].filter(Boolean);
249
+ const authorNames = [encounter.auditInfo?.creator?.display, providerName].filter(Boolean);
250
+
251
+ return (encounter.obs ?? [])
252
+ .filter((obs) => config.notesConceptUuids?.includes(obs.concept.uuid))
253
+ .map((obs, obsIndex) => {
254
+ const datetime = getNoteDatetime(obs.obsDatetime, encounter.encounterDatetime);
255
+ const note = (typeof obs.value === 'string' ? obs.value : obs.display) ?? '';
256
+
257
+ return {
258
+ authorNames,
259
+ authorUuids,
260
+ concept: obs.concept,
261
+ datetime,
262
+ id: obs.uuid ?? `${encounter.uuid}-${obsIndex}`,
263
+ note,
264
+ notePreview: note?.length > 120 ? `${note.substring(0, 120)}...` : note,
265
+ provider: {
266
+ name: providerName,
267
+ role: providerRole,
268
+ },
269
+ time: datetime ? formatDatetime(parseDate(datetime), { noToday: true }) : '',
270
+ };
271
+ });
272
+ })
273
+ .sort((noteA, noteB) => new Date(noteB.datetime).getTime() - new Date(noteA.datetime).getTime());
274
+ }, [config.notesConceptUuids, encounters]);
275
+
276
+ const myNotes = useMemo(
277
+ () =>
278
+ allNotes.filter((note) =>
279
+ [...note.authorUuids, ...note.authorNames].some((identifier) => currentUserIdentifiers.has(identifier)),
280
+ ),
281
+ [allNotes, currentUserIdentifiers],
282
+ );
283
+
284
+ if (!isTabActive) {
285
+ return null;
286
+ }
287
+
288
+ if (isLoading) {
289
+ return <DataTableSkeleton role="progressbar" />;
290
+ }
291
+
292
+ if (error) {
293
+ return <ErrorState error={error} headerTitle={t('notes', 'Notes')} />;
294
+ }
295
+
296
+ return (
297
+ <Tabs>
298
+ <TabList aria-label={t('patientNotesTabs', 'Patient notes tabs')} className={notesStyles.patientNotesTabList}>
299
+ <Tab className={notesStyles.patientNotesTab} id="all-notes-tab">
300
+ {t('allNotes', 'All Notes')}
301
+ </Tab>
302
+ <Tab className={notesStyles.patientNotesTab} id="my-notes-tab">
303
+ {t('myNotes', 'My Notes')}
304
+ </Tab>
305
+ </TabList>
306
+ <TabPanels>
307
+ <TabPanel>
308
+ <PatientNotesTable notes={allNotes} />
309
+ </TabPanel>
310
+ <TabPanel>
311
+ <PatientNotesTable notes={myNotes} />
312
+ </TabPanel>
313
+ </TabPanels>
314
+ </Tabs>
315
+ );
316
+ };
317
+
318
+ export default PatientNotesSummary;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { type Encounter, ExtensionSlot } from '@openmrs/esm-framework';
3
+ import { type ExternalOverviewProps } from '@openmrs/esm-patient-common-lib';
4
+
5
+ const TestsSummary = ({ patientUuid, encounters = [] }: { patientUuid: string; encounters: Array<Encounter> }) => {
6
+ const filter = React.useMemo<ExternalOverviewProps['filter']>(() => {
7
+ const encounterIds = encounters.map((e) => `Encounter/${e.uuid}`);
8
+ return ([entry]) => {
9
+ return encounterIds.includes(entry.encounter?.reference);
10
+ };
11
+ }, [encounters]);
12
+
13
+ return <ExtensionSlot name="test-results-filtered-overview-slot" state={{ filter, patientUuid }} />;
14
+ };
15
+
16
+ export default TestsSummary;