@kenyaemr/esm-express-workflow-app 5.4.4-pre.43 → 5.4.4-pre.430

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 (627) hide show
  1. package/.turbo/turbo-build.log +14 -7
  2. package/dist/1084.js +1 -0
  3. package/dist/1084.js.map +1 -0
  4. package/dist/1278.js +1 -0
  5. package/dist/1278.js.map +1 -0
  6. package/dist/1324.js +1 -0
  7. package/dist/1324.js.map +1 -0
  8. package/dist/1375.js +66 -0
  9. package/dist/1375.js.map +1 -0
  10. package/dist/1402.js +1 -0
  11. package/dist/1402.js.map +1 -0
  12. package/dist/1442.js +1 -0
  13. package/dist/1442.js.map +1 -0
  14. package/dist/1520.js +1 -0
  15. package/dist/1520.js.map +1 -0
  16. package/dist/1559.js +1 -0
  17. package/dist/1559.js.map +1 -0
  18. package/dist/1593.js +1 -0
  19. package/dist/1593.js.map +1 -0
  20. package/dist/162.js +1 -0
  21. package/dist/162.js.map +1 -0
  22. package/dist/1620.js +1 -0
  23. package/dist/1620.js.map +1 -0
  24. package/dist/1658.js +1 -0
  25. package/dist/1658.js.map +1 -0
  26. package/dist/1659.js +1 -0
  27. package/dist/1659.js.map +1 -0
  28. package/dist/1789.js +1 -0
  29. package/dist/1789.js.map +1 -0
  30. package/dist/1827.js +1 -0
  31. package/dist/1827.js.map +1 -0
  32. package/dist/1831.js +1 -0
  33. package/dist/1831.js.map +1 -0
  34. package/dist/1854.js +1 -0
  35. package/dist/1854.js.map +1 -0
  36. package/dist/196.js +1 -0
  37. package/dist/196.js.map +1 -0
  38. package/dist/2127.js +1 -0
  39. package/dist/2127.js.map +1 -0
  40. package/dist/2152.js +1 -0
  41. package/dist/2152.js.map +1 -0
  42. package/dist/2202.js +1 -0
  43. package/dist/2202.js.map +1 -0
  44. package/dist/221.js +1 -0
  45. package/dist/221.js.map +1 -0
  46. package/dist/2269.js +1 -0
  47. package/dist/2269.js.map +1 -0
  48. package/dist/2293.js +1 -0
  49. package/dist/2293.js.map +1 -0
  50. package/dist/234.js +1 -0
  51. package/dist/234.js.map +1 -0
  52. package/dist/2412.js +1 -1
  53. package/dist/2412.js.map +1 -1
  54. package/dist/2466.js +1 -0
  55. package/dist/2466.js.map +1 -0
  56. package/dist/2534.js +1 -1
  57. package/dist/2534.js.map +1 -1
  58. package/dist/2553.js +1 -0
  59. package/dist/2553.js.map +1 -0
  60. package/dist/2556.js +1 -0
  61. package/dist/2556.js.map +1 -0
  62. package/dist/2648.js +1 -0
  63. package/dist/2648.js.map +1 -0
  64. package/dist/274.js +1 -0
  65. package/dist/274.js.map +1 -0
  66. package/dist/2788.js +1 -0
  67. package/dist/2788.js.map +1 -0
  68. package/dist/2842.js +1 -0
  69. package/dist/2842.js.map +1 -0
  70. package/dist/2874.js +1 -0
  71. package/dist/2874.js.map +1 -0
  72. package/dist/2916.js +1 -0
  73. package/dist/2916.js.map +1 -0
  74. package/dist/2947.js +1 -1
  75. package/dist/2947.js.map +1 -1
  76. package/dist/3012.js +1 -0
  77. package/dist/3012.js.map +1 -0
  78. package/dist/3087.js +1 -0
  79. package/dist/3087.js.map +1 -0
  80. package/dist/3335.js +1 -0
  81. package/dist/3335.js.map +1 -0
  82. package/dist/3378.js +1 -0
  83. package/dist/3378.js.map +1 -0
  84. package/dist/3504.js +1 -0
  85. package/dist/3504.js.map +1 -0
  86. package/dist/3509.js +1 -1
  87. package/dist/3509.js.map +1 -1
  88. package/dist/356.js +1 -0
  89. package/dist/356.js.map +1 -0
  90. package/dist/3690.js +1 -0
  91. package/dist/3690.js.map +1 -0
  92. package/dist/371.js +1 -0
  93. package/dist/371.js.map +1 -0
  94. package/dist/3747.js +1 -0
  95. package/dist/3747.js.map +1 -0
  96. package/dist/3819.js +1 -0
  97. package/dist/3819.js.map +1 -0
  98. package/dist/3820.js +1 -0
  99. package/dist/3820.js.map +1 -0
  100. package/dist/3910.js +1 -0
  101. package/dist/3910.js.map +1 -0
  102. package/dist/393.js +6 -0
  103. package/dist/393.js.map +1 -0
  104. package/dist/3933.js +1 -0
  105. package/dist/3933.js.map +1 -0
  106. package/dist/3989.js +1 -0
  107. package/dist/3989.js.map +1 -0
  108. package/dist/3995.js +1 -0
  109. package/dist/3995.js.map +1 -0
  110. package/dist/4032.js +1 -0
  111. package/dist/4032.js.map +1 -0
  112. package/dist/4145.js +1 -1
  113. package/dist/4145.js.map +1 -1
  114. package/dist/4178.js +1 -1
  115. package/dist/4178.js.map +1 -1
  116. package/dist/4191.js +1 -0
  117. package/dist/4191.js.map +1 -0
  118. package/dist/4212.js +1 -0
  119. package/dist/4212.js.map +1 -0
  120. package/dist/4215.js +1 -0
  121. package/dist/4215.js.map +1 -0
  122. package/dist/4523.js +1 -0
  123. package/dist/4523.js.map +1 -0
  124. package/dist/4588.js +1 -0
  125. package/dist/4588.js.map +1 -0
  126. package/dist/4617.js +1 -0
  127. package/dist/4617.js.map +1 -0
  128. package/dist/466.js +1 -1
  129. package/dist/466.js.map +1 -1
  130. package/dist/4688.js +1 -0
  131. package/dist/{7288.js.map → 4688.js.map} +1 -1
  132. package/dist/470.js +1 -0
  133. package/dist/470.js.map +1 -0
  134. package/dist/4759.js +1 -0
  135. package/dist/4759.js.map +1 -0
  136. package/dist/4818.js +1 -1
  137. package/dist/4818.js.map +1 -1
  138. package/dist/5086.js +1 -1
  139. package/dist/5086.js.map +1 -1
  140. package/dist/5093.js +1 -0
  141. package/dist/5093.js.map +1 -0
  142. package/dist/5097.js +1 -0
  143. package/dist/5097.js.map +1 -0
  144. package/dist/510.js +1 -0
  145. package/dist/510.js.map +1 -0
  146. package/dist/5117.js +1 -0
  147. package/dist/5117.js.map +1 -0
  148. package/dist/5279.js +1 -0
  149. package/dist/5279.js.map +1 -0
  150. package/dist/5434.js +1 -0
  151. package/dist/5434.js.map +1 -0
  152. package/dist/5514.js +1 -0
  153. package/dist/5514.js.map +1 -0
  154. package/dist/5568.js +1 -0
  155. package/dist/5568.js.map +1 -0
  156. package/dist/5576.js +1 -1
  157. package/dist/5576.js.map +1 -1
  158. package/dist/5630.js +1 -0
  159. package/dist/5630.js.map +1 -0
  160. package/dist/5676.js +1 -1
  161. package/dist/5676.js.map +1 -1
  162. package/dist/5695.js +1 -0
  163. package/dist/5695.js.map +1 -0
  164. package/dist/5697.js +1 -1
  165. package/dist/5697.js.map +1 -1
  166. package/dist/5728.js +1 -0
  167. package/dist/5728.js.map +1 -0
  168. package/dist/5809.js +1 -1
  169. package/dist/5809.js.map +1 -1
  170. package/dist/585.js +1 -0
  171. package/dist/585.js.map +1 -0
  172. package/dist/5887.js +1 -0
  173. package/dist/5887.js.map +1 -0
  174. package/dist/5958.js +1 -0
  175. package/dist/5958.js.map +1 -0
  176. package/dist/5964.js +1 -0
  177. package/dist/5964.js.map +1 -0
  178. package/dist/6008.js +1 -0
  179. package/dist/602.js +1 -0
  180. package/dist/602.js.map +1 -0
  181. package/dist/6020.js +1 -0
  182. package/dist/6020.js.map +1 -0
  183. package/dist/6161.js +1 -0
  184. package/dist/6161.js.map +1 -0
  185. package/dist/6273.js +1 -0
  186. package/dist/6273.js.map +1 -0
  187. package/dist/6337.js +1 -0
  188. package/dist/6337.js.map +1 -0
  189. package/dist/6427.js +1 -1
  190. package/dist/6427.js.map +1 -1
  191. package/dist/6429.js +1 -0
  192. package/dist/6429.js.map +1 -0
  193. package/dist/6478.js +7 -0
  194. package/dist/6478.js.map +1 -0
  195. package/dist/6508.js +1 -0
  196. package/dist/6508.js.map +1 -0
  197. package/dist/6634.js +1 -0
  198. package/dist/6634.js.map +1 -0
  199. package/dist/6767.js +1 -0
  200. package/dist/6767.js.map +1 -0
  201. package/dist/6783.js +1 -0
  202. package/dist/6783.js.map +1 -0
  203. package/dist/6868.js +1 -1
  204. package/dist/6868.js.map +1 -1
  205. package/dist/688.js +1 -0
  206. package/dist/688.js.map +1 -0
  207. package/dist/689.js +1 -1
  208. package/dist/689.js.map +1 -1
  209. package/dist/694.js +1 -0
  210. package/dist/694.js.map +1 -0
  211. package/dist/6987.js +1 -0
  212. package/dist/6987.js.map +1 -0
  213. package/dist/7061.js +1 -0
  214. package/dist/7061.js.map +1 -0
  215. package/dist/7078.js +1 -0
  216. package/dist/7078.js.map +1 -0
  217. package/dist/710.js +1 -0
  218. package/dist/710.js.map +1 -0
  219. package/dist/7128.js +1 -0
  220. package/dist/7128.js.map +1 -0
  221. package/dist/7209.js +1 -0
  222. package/dist/7209.js.map +1 -0
  223. package/dist/7424.js +1 -0
  224. package/dist/7424.js.map +1 -0
  225. package/dist/770.js +1 -0
  226. package/dist/770.js.map +1 -0
  227. package/dist/7707.js +1 -0
  228. package/dist/7707.js.map +1 -0
  229. package/dist/7784.js +1 -0
  230. package/dist/7784.js.map +1 -0
  231. package/dist/781.js +1 -1
  232. package/dist/781.js.map +1 -1
  233. package/dist/7824.js +1 -0
  234. package/dist/7824.js.map +1 -0
  235. package/dist/7832.js +1 -0
  236. package/dist/7832.js.map +1 -0
  237. package/dist/8178.js +1 -0
  238. package/dist/8178.js.map +1 -0
  239. package/dist/820.js +1 -0
  240. package/dist/820.js.map +1 -0
  241. package/dist/8224.js +1 -0
  242. package/dist/8224.js.map +1 -0
  243. package/dist/8225.js +1 -0
  244. package/dist/8225.js.map +1 -0
  245. package/dist/8240.js +1 -1
  246. package/dist/8240.js.map +1 -1
  247. package/dist/8258.js +1 -0
  248. package/dist/8258.js.map +1 -0
  249. package/dist/8702.js +1 -0
  250. package/dist/8702.js.map +1 -0
  251. package/dist/8725.js +1 -0
  252. package/dist/8725.js.map +1 -0
  253. package/dist/8762.js +1 -0
  254. package/dist/8762.js.map +1 -0
  255. package/dist/8763.js +1 -0
  256. package/dist/8763.js.map +1 -0
  257. package/dist/8815.js +1 -0
  258. package/dist/8815.js.map +1 -0
  259. package/dist/8882.js +1 -0
  260. package/dist/8882.js.map +1 -0
  261. package/dist/8924.js +1 -1
  262. package/dist/8924.js.map +1 -1
  263. package/dist/8937.js +1 -0
  264. package/dist/8937.js.map +1 -0
  265. package/dist/9.js +1 -1
  266. package/dist/9.js.map +1 -1
  267. package/dist/9061.js +1 -0
  268. package/dist/9061.js.map +1 -0
  269. package/dist/9087.js +1 -0
  270. package/dist/9087.js.map +1 -0
  271. package/dist/9301.js +1 -0
  272. package/dist/9301.js.map +1 -0
  273. package/dist/9324.js +1 -0
  274. package/dist/9324.js.map +1 -0
  275. package/dist/9497.js +1 -0
  276. package/dist/9497.js.map +1 -0
  277. package/dist/9520.js +1 -1
  278. package/dist/9520.js.map +1 -1
  279. package/dist/9526.js +1 -0
  280. package/dist/9526.js.map +1 -0
  281. package/dist/9621.js +1 -0
  282. package/dist/9621.js.map +1 -0
  283. package/dist/9676.js +17 -0
  284. package/dist/9676.js.map +1 -0
  285. package/dist/977.js +1 -0
  286. package/dist/977.js.map +1 -0
  287. package/dist/9771.js +1 -0
  288. package/dist/9771.js.map +1 -0
  289. package/dist/9806.js +1 -0
  290. package/dist/9806.js.map +1 -0
  291. package/dist/989.js +1 -0
  292. package/dist/989.js.map +1 -0
  293. package/dist/kenyaemr-esm-express-workflow-app.js +6 -5
  294. package/dist/kenyaemr-esm-express-workflow-app.js.buildmanifest.json +1407 -809
  295. package/dist/kenyaemr-esm-express-workflow-app.js.map +1 -1
  296. package/dist/main.js +22 -5
  297. package/dist/main.js.map +1 -1
  298. package/dist/routes.json +1 -1
  299. package/package.json +4 -6
  300. package/src/components/admissions/admissions-dashboard.component.tsx +2 -2
  301. package/src/components/anaesthetic/anaesthetic-dashboard.meta.ts +8 -0
  302. package/src/components/anaesthetic/anaesthetic-data-form.component.tsx +442 -0
  303. package/src/components/anaesthetic/anaesthetic-data-form.scss +278 -0
  304. package/src/components/anaesthetic/anaesthetic-link.component.tsx +21 -0
  305. package/src/components/anaesthetic/anaesthetic.component.tsx +618 -0
  306. package/src/components/anaesthetic/anaesthetic.resource.ts +1503 -0
  307. package/src/components/anaesthetic/anaesthetic.scss +1885 -0
  308. package/src/components/anaesthetic/forms/anaesthetic-form.component.tsx +304 -0
  309. package/src/components/anaesthetic/forms/anaesthetic-record-form.component.tsx +707 -0
  310. package/src/components/anaesthetic/forms/icd11-diagnosis-search.component.tsx +178 -0
  311. package/src/components/anaesthetic/forms/icd11-diagnosis-search.scss +126 -0
  312. package/src/components/anaesthetic/forms/index.ts +2 -0
  313. package/src/components/anaesthetic/forms/interoperative-record-drug-given-form.component.tsx +231 -0
  314. package/src/components/anaesthetic/forms/post-operative-summary-form.component.tsx +171 -0
  315. package/src/components/anaesthetic/forms/time-picker-dropdown.component.tsx +202 -0
  316. package/src/components/anaesthetic/forms/time-picker-dropdown.scss +107 -0
  317. package/src/components/anaesthetic/forms/time-picker-with-clock.component.tsx +174 -0
  318. package/src/components/anaesthetic/forms/time-picker-with-clock.scss +178 -0
  319. package/src/components/anaesthetic/forms/useAnaestheticData.ts +16 -0
  320. package/src/components/anaesthetic/graphs/anaesthetic-graph-wrapper.component.tsx +266 -0
  321. package/src/components/anaesthetic/graphs/anaesthetic-graph.component.tsx +185 -0
  322. package/src/components/anaesthetic/graphs/index.ts +2 -0
  323. package/src/components/anaesthetic/graphs/interoperative-record-drug-given-graph-wrapper.component.tsx +197 -0
  324. package/src/components/anaesthetic/graphs/interoperative-record-drug-given-graph.component.tsx +93 -0
  325. package/src/components/anaesthetic/resources/anaesthetic-amniotic-fluid.resource.ts +277 -0
  326. package/src/components/anaesthetic/resources/anaesthetic-form.resource.ts +716 -0
  327. package/src/components/anaesthetic/resources/anaesthetic.resource.ts +378 -0
  328. package/src/components/anaesthetic/types/index.ts +1349 -0
  329. package/src/components/consultation/clinical-encounter/clinical-encounter.component.tsx +4 -20
  330. package/src/components/consultation/clinical-encounter/encounter-details.component.tsx +9 -9
  331. package/src/components/consultation/clinical-encounter/encounter-details.test.tsx +19 -18
  332. package/src/components/consultation/consultation.utils.tsx +1 -1
  333. package/src/components/consultation/dashboard.component.tsx +0 -2
  334. package/src/components/consultation/index.ts +1 -1
  335. package/src/components/consultation/tabs/index.ts +67 -0
  336. package/src/components/facility-dashboard/components/header/home-header.component.tsx +8 -13
  337. package/src/components/laboratory/index.ts +11 -0
  338. package/src/components/laboratory/lab-table.component.tsx +46 -12
  339. package/src/components/laboratory/laboratory-tabs.component.tsx +3 -17
  340. package/src/components/mch/dashboard.component.tsx +0 -2
  341. package/src/components/mch/mch.component.tsx +1 -3
  342. package/src/components/pharmacy/index.ts +1 -1
  343. package/src/components/pharmacy/orders/pharmacy-orders.component.tsx +44 -6
  344. package/src/components/pharmacy/orders/pharmacy-orders.test.tsx +8 -7
  345. package/src/components/pharmacy/pharmacy-tabs.component.tsx +4 -20
  346. package/src/components/pharmacy/tabs/index.ts +21 -0
  347. package/src/components/preauth/index.ts +22 -0
  348. package/src/components/preauth/preauth-dashboard.component.tsx +20 -0
  349. package/src/components/procedures/index.ts +16 -0
  350. package/src/components/procedures/procedure-queues/index.ts +13 -0
  351. package/src/components/procedures/procedure-queues/procedure-form-extras.component.tsx +33 -0
  352. package/src/components/procedures/procedure-queues/procedure-form-extras.scss +5 -0
  353. package/src/components/procedures/procedure-queues/procedure-queues.component.tsx +35 -0
  354. package/src/components/procedures/procedure-queues/procedure-queues.resources.ts +37 -0
  355. package/src/components/procedures/procedure-queues/procedure-room-tabs.component.tsx +80 -0
  356. package/src/components/procedures/procedure-queues/procedure-rooms.scss +13 -0
  357. package/src/components/procedures/procedure-queues/queue-procedure-tabs.component.tsx +34 -0
  358. package/src/components/procedures/procedure-queues/summary-cards.component.tsx +81 -0
  359. package/src/components/procedures/procedure-queues/summary-cards.scss +35 -0
  360. package/src/components/procedures/procedures-table.component.tsx +77 -19
  361. package/src/components/procedures/procedures-tabs.component.test.tsx +57 -0
  362. package/src/components/procedures/procedures-tabs.component.tsx +3 -18
  363. package/src/components/radiology-and-imaging/index.ts +11 -0
  364. package/src/components/radiology-and-imaging/radiology-and-imaging-table.component.tsx +73 -21
  365. package/src/components/radiology-and-imaging/radiology-and-imaging.component.tsx +3 -19
  366. package/src/components/registration/card/HIE-card/hie-card.component.tsx +32 -44
  367. package/src/components/registration/card/Local-card/local-card.component.tsx +22 -24
  368. package/src/components/registration/checkin-form-extra/checkin-form-extra.extension.tsx +8 -2
  369. package/src/components/registration/checkin-form-extra/queue-fields.component.tsx +18 -19
  370. package/src/components/registration/dependants/dependants.component.tsx +37 -60
  371. package/src/components/registration/dependants/dependants.resource.ts +75 -46
  372. package/src/components/registration/end-visit-modal/end-visit-dialog.modal.tsx +320 -0
  373. package/src/components/registration/end-visit-modal/end-visit-dialog.scss +12 -0
  374. package/src/components/registration/end-visit-modal/end-visit.resource.tsx +59 -0
  375. package/src/components/registration/end-visit-modal/sha-end-visit.resource.ts +285 -0
  376. package/src/components/registration/search-bar/search-bar.resource.ts +32 -93
  377. package/src/components/registration/start-visit-form/hooks/useDefaultFacilityLocation.tsx +15 -0
  378. package/src/components/registration/start-visit-form/hooks/useDefaultVisitLocation.tsx +24 -0
  379. package/src/components/registration/start-visit-form/hooks/useOfflineVisitType.tsx +18 -0
  380. package/src/components/registration/start-visit-form/hooks/useRecommendedVisitTypes.tsx +36 -0
  381. package/src/components/registration/start-visit-form/hooks/useVisitAttributeType.tsx +102 -0
  382. package/src/components/registration/start-visit-form/overflow-menu-extension/custom-end-active-visit.extension.tsx +44 -0
  383. package/src/components/registration/start-visit-form/overflow-menu-extension/overflow-menu-item.extension.tsx +67 -0
  384. package/src/components/registration/start-visit-form/overflow-menu-extension/overflow-menu-item.scss +3 -0
  385. package/src/components/registration/start-visit-form/start-visit-workspace/base-visit-type.component.tsx +126 -0
  386. package/src/components/registration/start-visit-form/start-visit-workspace/base-visit-type.scss +75 -0
  387. package/src/components/registration/start-visit-form/start-visit-workspace/exported-visit-form.workspace.tsx +778 -0
  388. package/src/components/registration/start-visit-form/start-visit-workspace/location-selector.component.tsx +86 -0
  389. package/src/components/registration/start-visit-form/start-visit-workspace/recommended-visit-type.component.tsx +32 -0
  390. package/src/components/registration/start-visit-form/start-visit-workspace/visit-attribute-type.component.tsx +257 -0
  391. package/src/components/registration/start-visit-form/start-visit-workspace/visit-attribute-type.scss +5 -0
  392. package/src/components/registration/start-visit-form/start-visit-workspace/visit-date-time.component.tsx +193 -0
  393. package/src/components/registration/start-visit-form/start-visit-workspace/visit-form.resource.ts +390 -0
  394. package/src/components/registration/start-visit-form/start-visit-workspace/visit-form.scss +144 -0
  395. package/src/components/registration/start-visit-form/visit-form-workspace/visit-form.workspace.tsx +48 -0
  396. package/src/components/registration/type/index.ts +101 -0
  397. package/src/components/reports/index.ts +1 -0
  398. package/src/components/triage/dashboard.component.tsx +0 -2
  399. package/src/config-schema.ts +476 -0
  400. package/src/hooks/useOrders.tsx +2 -2
  401. package/src/hooks/useServiceQueues.tsx +1 -1
  402. package/src/index.ts +81 -15
  403. package/src/routes.json +298 -32
  404. package/src/shared/dashboard-link/dashboard-link.component.tsx +11 -8
  405. package/src/shared/orders/OrderTable.scss +7 -0
  406. package/src/shared/orders/OrderTable.tsx +68 -8
  407. package/src/shared/orders/print-order/print-order.component.tsx +158 -0
  408. package/src/shared/orders/print-order/print-order.scss +225 -0
  409. package/src/shared/orders/print-prescription/print-prescription.component.tsx +65 -0
  410. package/src/shared/orders/print-prescription/print-prescription.resource.ts +45 -0
  411. package/src/shared/orders/print-prescription/print-prescription.scss +25 -0
  412. package/src/shared/otp-verification/index.ts +112 -29
  413. package/src/shared/otp-verification/otp-verification.modal.tsx +1110 -122
  414. package/src/shared/otp-verification/otp-verification.scss +374 -0
  415. package/src/shared/patient-chart/patient-summary-dashboard/patient-summary-dashboard.component.tsx +5 -84
  416. package/src/shared/pin-put/pinput.component.test.tsx +7 -6
  417. package/src/shared/tabs/create-tab-extension.tsx +33 -0
  418. package/src/shared/tabs/extension-tabs.component.tsx +51 -33
  419. package/src/shared/utils/index.ts +140 -5
  420. package/src/types/index.ts +12 -0
  421. package/translations/am.json +443 -131
  422. package/translations/en.json +327 -15
  423. package/translations/fr.json +372 -63
  424. package/translations/sw.json +347 -35
  425. package/tsconfig.json +1 -0
  426. package/dist/1049.js +0 -1
  427. package/dist/1049.js.map +0 -1
  428. package/dist/1072.js +0 -1
  429. package/dist/1072.js.map +0 -1
  430. package/dist/1091.js +0 -1
  431. package/dist/1091.js.map +0 -1
  432. package/dist/1172.js +0 -1
  433. package/dist/1172.js.map +0 -1
  434. package/dist/132.js +0 -1
  435. package/dist/132.js.map +0 -1
  436. package/dist/1354.js +0 -1
  437. package/dist/1354.js.map +0 -1
  438. package/dist/1704.js +0 -1
  439. package/dist/1704.js.map +0 -1
  440. package/dist/1729.js +0 -17
  441. package/dist/1729.js.map +0 -1
  442. package/dist/1757.js +0 -1
  443. package/dist/1757.js.map +0 -1
  444. package/dist/1761.js +0 -1
  445. package/dist/1761.js.map +0 -1
  446. package/dist/1809.js +0 -7
  447. package/dist/1809.js.map +0 -1
  448. package/dist/1842.js +0 -1
  449. package/dist/1874.js +0 -1
  450. package/dist/1874.js.map +0 -1
  451. package/dist/1962.js +0 -1
  452. package/dist/1962.js.map +0 -1
  453. package/dist/2007.js +0 -1
  454. package/dist/2007.js.map +0 -1
  455. package/dist/2024.js +0 -1
  456. package/dist/2024.js.map +0 -1
  457. package/dist/2045.js +0 -1
  458. package/dist/2045.js.map +0 -1
  459. package/dist/2163.js +0 -1
  460. package/dist/2163.js.map +0 -1
  461. package/dist/2191.js +0 -1
  462. package/dist/2191.js.map +0 -1
  463. package/dist/2267.js +0 -1
  464. package/dist/2267.js.map +0 -1
  465. package/dist/231.js +0 -1
  466. package/dist/231.js.map +0 -1
  467. package/dist/236.js +0 -1
  468. package/dist/236.js.map +0 -1
  469. package/dist/2367.js +0 -1
  470. package/dist/2367.js.map +0 -1
  471. package/dist/2463.js +0 -1
  472. package/dist/2463.js.map +0 -1
  473. package/dist/2561.js +0 -1
  474. package/dist/2561.js.map +0 -1
  475. package/dist/2810.js +0 -2
  476. package/dist/2810.js.map +0 -1
  477. package/dist/2867.js +0 -1
  478. package/dist/2867.js.map +0 -1
  479. package/dist/2909.js +0 -1
  480. package/dist/2909.js.map +0 -1
  481. package/dist/2949.js +0 -1
  482. package/dist/2949.js.map +0 -1
  483. package/dist/3040.js +0 -1
  484. package/dist/3119.js +0 -1
  485. package/dist/3119.js.map +0 -1
  486. package/dist/3215.js +0 -1
  487. package/dist/3215.js.map +0 -1
  488. package/dist/322.js +0 -1
  489. package/dist/322.js.map +0 -1
  490. package/dist/3228.js +0 -1
  491. package/dist/3228.js.map +0 -1
  492. package/dist/330.js +0 -1
  493. package/dist/330.js.map +0 -1
  494. package/dist/3472.js +0 -1
  495. package/dist/3472.js.map +0 -1
  496. package/dist/351.js +0 -1
  497. package/dist/351.js.map +0 -1
  498. package/dist/3576.js +0 -1
  499. package/dist/3576.js.map +0 -1
  500. package/dist/397.js +0 -1
  501. package/dist/397.js.map +0 -1
  502. package/dist/4007.js +0 -66
  503. package/dist/4007.js.map +0 -1
  504. package/dist/4200.js +0 -1
  505. package/dist/4200.js.map +0 -1
  506. package/dist/4224.js +0 -1
  507. package/dist/4224.js.map +0 -1
  508. package/dist/43.js +0 -1
  509. package/dist/43.js.map +0 -1
  510. package/dist/4308.js +0 -1
  511. package/dist/4308.js.map +0 -1
  512. package/dist/4399.js +0 -1
  513. package/dist/4399.js.map +0 -1
  514. package/dist/4505.js +0 -1
  515. package/dist/4505.js.map +0 -1
  516. package/dist/4724.js +0 -1
  517. package/dist/4724.js.map +0 -1
  518. package/dist/498.js +0 -1
  519. package/dist/498.js.map +0 -1
  520. package/dist/509.js +0 -1
  521. package/dist/509.js.map +0 -1
  522. package/dist/5096.js +0 -1
  523. package/dist/5096.js.map +0 -1
  524. package/dist/5159.js +0 -13
  525. package/dist/5159.js.map +0 -1
  526. package/dist/5184.js +0 -1
  527. package/dist/5184.js.map +0 -1
  528. package/dist/5270.js +0 -1
  529. package/dist/5270.js.map +0 -1
  530. package/dist/5361.js +0 -1
  531. package/dist/5361.js.map +0 -1
  532. package/dist/5537.js +0 -1
  533. package/dist/5537.js.map +0 -1
  534. package/dist/566.js +0 -1
  535. package/dist/566.js.map +0 -1
  536. package/dist/5733.js +0 -1
  537. package/dist/5733.js.map +0 -1
  538. package/dist/5854.js +0 -1
  539. package/dist/5854.js.map +0 -1
  540. package/dist/5882.js +0 -1
  541. package/dist/6004.js +0 -1
  542. package/dist/6004.js.map +0 -1
  543. package/dist/6121.js +0 -3
  544. package/dist/6121.js.map +0 -1
  545. package/dist/6137.js +0 -1
  546. package/dist/6137.js.map +0 -1
  547. package/dist/6356.js +0 -1
  548. package/dist/6356.js.map +0 -1
  549. package/dist/6571.js +0 -1
  550. package/dist/6571.js.map +0 -1
  551. package/dist/6721.js +0 -1
  552. package/dist/6721.js.map +0 -1
  553. package/dist/6858.js +0 -1
  554. package/dist/6858.js.map +0 -1
  555. package/dist/6925.js +0 -1
  556. package/dist/6925.js.map +0 -1
  557. package/dist/6960.js +0 -1
  558. package/dist/6960.js.map +0 -1
  559. package/dist/7085.js +0 -1
  560. package/dist/7085.js.map +0 -1
  561. package/dist/7127.js +0 -1
  562. package/dist/7176.js +0 -1
  563. package/dist/7176.js.map +0 -1
  564. package/dist/7237.js +0 -1
  565. package/dist/7237.js.map +0 -1
  566. package/dist/725.js +0 -1
  567. package/dist/725.js.map +0 -1
  568. package/dist/7288.js +0 -1
  569. package/dist/7329.js +0 -1
  570. package/dist/7329.js.map +0 -1
  571. package/dist/7334.js +0 -1
  572. package/dist/7334.js.map +0 -1
  573. package/dist/7461.js +0 -1
  574. package/dist/7461.js.map +0 -1
  575. package/dist/7517.js +0 -1
  576. package/dist/7517.js.map +0 -1
  577. package/dist/7837.js +0 -1
  578. package/dist/7837.js.map +0 -1
  579. package/dist/8103.js +0 -1
  580. package/dist/8103.js.map +0 -1
  581. package/dist/8110.js +0 -1
  582. package/dist/8110.js.map +0 -1
  583. package/dist/8179.js +0 -6
  584. package/dist/8179.js.map +0 -1
  585. package/dist/8189.js +0 -1
  586. package/dist/8189.js.map +0 -1
  587. package/dist/8209.js +0 -1
  588. package/dist/8209.js.map +0 -1
  589. package/dist/8249.js +0 -1
  590. package/dist/8249.js.map +0 -1
  591. package/dist/8285.js +0 -1
  592. package/dist/8285.js.map +0 -1
  593. package/dist/8470.js +0 -1
  594. package/dist/8470.js.map +0 -1
  595. package/dist/8502.js +0 -1
  596. package/dist/8502.js.map +0 -1
  597. package/dist/866.js +0 -1
  598. package/dist/866.js.map +0 -1
  599. package/dist/8889.js +0 -11
  600. package/dist/8889.js.map +0 -1
  601. package/dist/8990.js +0 -1
  602. package/dist/8990.js.map +0 -1
  603. package/dist/8994.js +0 -1
  604. package/dist/8994.js.map +0 -1
  605. package/dist/9049.js +0 -1
  606. package/dist/9049.js.map +0 -1
  607. package/dist/9054.js +0 -1
  608. package/dist/9054.js.map +0 -1
  609. package/dist/916.js +0 -1
  610. package/dist/9231.js +0 -1
  611. package/dist/9231.js.map +0 -1
  612. package/dist/9408.js +0 -1
  613. package/dist/9408.js.map +0 -1
  614. package/dist/9425.js +0 -1
  615. package/dist/9466.js +0 -1
  616. package/dist/9466.js.map +0 -1
  617. package/dist/9919.js +0 -1
  618. package/dist/9919.js.map +0 -1
  619. package/dist/9931.js +0 -1
  620. package/dist/9931.js.map +0 -1
  621. package/jest.config.js +0 -3
  622. package/src/shared/orders/OrdersTabs.tsx +0 -58
  623. package/src/shared/patient-chart/patient-chart.component.tsx +0 -61
  624. package/src/shared/patient-chart/patient-chart.resources.ts +0 -180
  625. package/src/shared/patient-chart/patient-chart.scss +0 -15
  626. package/src/shared/patient-chart/useInitialize.ts +0 -24
  627. /package/src/shared/{patient-chart/patient-summary-dashboard → tabs}/patient-summary-dashboard.scss +0 -0
@@ -0,0 +1,1503 @@
1
+ import { openmrsFetch, restBaseUrl, toOmrsIsoString, useConfig } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+ import { useState, useEffect, useMemo } from 'react';
4
+ import {
5
+ PARTOGRAPHY_CONCEPTS,
6
+ PARTOGRAPHY_ENCOUNTER_TYPES,
7
+ MCH_PARTOGRAPHY_ENCOUNTER_UUID,
8
+ URINE_TEST_TIME_SLOT_CONCEPT,
9
+ getPartographyUnit,
10
+ type OpenMRSResponse,
11
+ type PartographyObservation,
12
+ type PartographyEncounter,
13
+ type PartographyGraphType,
14
+ } from './types';
15
+ import {
16
+ configSchema,
17
+ type ConfigObject,
18
+ TIME_RESULTS_RETURNED,
19
+ TIME_SAMPLE_COLLECTED,
20
+ EVENT_DESCRIPTION_CONCEPT,
21
+ CONTRACTION_COUNT_CONCEPT,
22
+ UTERINE_CONTRACTIONS_CONCEPT,
23
+ CONTRACTION_LEVEL_MILD_CONCEPT,
24
+ CONTRACTION_LEVEL_MODERATE_CONCEPT,
25
+ CONTRACTION_LEVEL_STRONG_CONCEPT,
26
+ MOULDING_NONE_CONCEPT,
27
+ } from '../../config-schema';
28
+
29
+ // Hook to fetch and manage membrane amniotic fluid and moulding data from OpenMRS
30
+ export function useMembraneAmnioticFluidData(patientUuid: string) {
31
+ const fetcher = (url: string) => openmrsFetch(url).then((res) => res.json());
32
+
33
+ const { data, error, isLoading, mutate } = useSWR(
34
+ patientUuid
35
+ ? `${restBaseUrl}/encounter?patient=${patientUuid}&encounterType=${MCH_PARTOGRAPHY_ENCOUNTER_UUID}&v=full&limit=100&order=desc`
36
+ : null,
37
+ fetcher,
38
+ {
39
+ revalidateOnFocus: true,
40
+ revalidateOnReconnect: true,
41
+ },
42
+ );
43
+
44
+ const membraneAmnioticFluidEntries = useMemo(() => {
45
+ try {
46
+ if (!data?.results || !Array.isArray(data.results)) {
47
+ return [];
48
+ }
49
+
50
+ const entries = [];
51
+ for (const encounter of data.results) {
52
+ if (!encounter.obs || !Array.isArray(encounter.obs)) {
53
+ continue;
54
+ }
55
+
56
+ const amnioticFluidObs = encounter.obs.find(
57
+ (obs: PartographyObservation) => obs.concept.uuid === PARTOGRAPHY_CONCEPTS['amniotic-fluid'],
58
+ );
59
+ const mouldingObs = encounter.obs.find(
60
+ (obs: PartographyObservation) => obs.concept.uuid === PARTOGRAPHY_CONCEPTS['moulding'],
61
+ );
62
+ const timeObs = encounter.obs.find(
63
+ (obs: PartographyObservation) =>
64
+ obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-time'] &&
65
+ typeof obs.value === 'string' &&
66
+ obs.value.startsWith('Time:'),
67
+ );
68
+
69
+ let time = '';
70
+ if (timeObs && typeof timeObs.value === 'string') {
71
+ const timeMatch = timeObs.value.match(/Time:\s*(.+)/);
72
+ if (timeMatch) {
73
+ time = timeMatch[1].trim();
74
+ }
75
+ }
76
+
77
+ if (amnioticFluidObs || mouldingObs) {
78
+ entries.push({
79
+ id: `maf-${encounter.uuid}`,
80
+ uuid: encounter.uuid,
81
+ amnioticFluid: amnioticFluidObs?.value?.display || amnioticFluidObs?.value || '',
82
+ moulding: mouldingObs?.value?.display || mouldingObs?.value || '',
83
+ time,
84
+ date: new Date(encounter.encounterDatetime).toLocaleDateString(),
85
+ encounterDatetime: encounter.encounterDatetime,
86
+ });
87
+ }
88
+ }
89
+ return entries;
90
+ } catch (error) {
91
+ return [];
92
+ }
93
+ }, [data]);
94
+
95
+ return {
96
+ membraneAmnioticFluidEntries,
97
+ isLoading,
98
+ error,
99
+ mutate,
100
+ };
101
+ }
102
+ function toOpenMrsIsoWithColonOffset(date: Date): string {
103
+ const iso = toOmrsIsoString(date);
104
+ return iso.replace(/([+-]\d{2})(\d{2})$/, '$1:$2');
105
+ }
106
+
107
+ // Function to save membrane amniotic fluid and moulding data to OpenMRS
108
+ export async function saveMembraneAmnioticFluidData(
109
+ patientUuid: string,
110
+ formData: { amnioticFluid: string; moulding: string; time: string },
111
+ t: unknown,
112
+ locationUuid?: string,
113
+ providerUuid?: string,
114
+ ) {
115
+ try {
116
+ const result = await createPartographyEncounter(
117
+ patientUuid,
118
+ 'membrane-amniotic-fluid',
119
+ formData,
120
+ locationUuid,
121
+ providerUuid,
122
+ );
123
+ if (result?.success && result?.encounter) {
124
+ return result;
125
+ } else {
126
+ throw new Error(result?.message || 'Failed to save membrane amniotic fluid data');
127
+ }
128
+ } catch (error) {
129
+ throw new Error(error instanceof Error ? error.message : 'Failed to save fetal heart rate data');
130
+ }
131
+ }
132
+ export type { PartographyObservation, PartographyEncounter };
133
+ const defaultAnaestheticConfig = configSchema.anaesthetic._default;
134
+ const getConceptMappingsForGraphType = (graphType: string): string[] => {
135
+ return (
136
+ defaultAnaestheticConfig.conceptMappings[graphType as keyof typeof defaultAnaestheticConfig.conceptMappings] || []
137
+ );
138
+ };
139
+
140
+ const getFallbackEncounterTypes = (graphType: string): string[] => {
141
+ return (
142
+ defaultAnaestheticConfig.fallbackEncounterMapping[
143
+ graphType as keyof typeof defaultAnaestheticConfig.fallbackEncounterMapping
144
+ ] || defaultAnaestheticConfig.defaultFallbackSequence
145
+ );
146
+ };
147
+
148
+ const getStationDisplay = (conceptUuid: string): string => {
149
+ return (
150
+ defaultAnaestheticConfig.stationDisplayMapping[
151
+ conceptUuid as keyof typeof defaultAnaestheticConfig.stationDisplayMapping
152
+ ] || conceptUuid
153
+ );
154
+ };
155
+
156
+ const getStationValue = (conceptUuid: string): number => {
157
+ return (
158
+ defaultAnaestheticConfig.stationValueMapping[
159
+ conceptUuid as keyof typeof defaultAnaestheticConfig.stationValueMapping
160
+ ] ?? 0
161
+ );
162
+ };
163
+
164
+ const getProgressEventInfo = (conceptUuid: string): { name: string; unit: string } | null => {
165
+ return (
166
+ defaultAnaestheticConfig.progressEventConceptNames[
167
+ conceptUuid as keyof typeof defaultAnaestheticConfig.progressEventConceptNames
168
+ ] || null
169
+ );
170
+ };
171
+
172
+ const getGraphTypeDisplayName = (graphType: string): string => {
173
+ const config = defaultAnaestheticConfig;
174
+ const mapped = config.graphTypeDisplayNames[graphType as keyof typeof config.graphTypeDisplayNames];
175
+ return mapped || graphType.replace('-', ' ').replace(/\b\w/g, (l) => l.toUpperCase());
176
+ };
177
+
178
+ const getTestDataConfig = () => {
179
+ const config = defaultAnaestheticConfig;
180
+ return config.testData;
181
+ };
182
+
183
+ const doesObservationMapToGraphType = (obsConceptUuid: string, targetGraphType: string): boolean => {
184
+ const conceptUuids = getConceptMappingsForGraphType(targetGraphType);
185
+ return conceptUuids.includes(obsConceptUuid);
186
+ };
187
+
188
+ const generateCacheKey = (patientUuid: string, graphType: string): string => {
189
+ const config = defaultAnaestheticConfig;
190
+ return `${config.storage.cacheKeyPrefix}${patientUuid}_${graphType}`;
191
+ };
192
+
193
+ const getDefaultLocationUuid = (): string => {
194
+ const config = defaultAnaestheticConfig;
195
+ return config.defaultLocationUuid;
196
+ };
197
+
198
+ const getDefaultEncounterProviderRole = (): string => {
199
+ const config = defaultAnaestheticConfig;
200
+ return config.defaultEncounterProviderRole;
201
+ };
202
+
203
+ const getTimeConfig = () => {
204
+ const config = defaultAnaestheticConfig;
205
+ return config.timeConfig;
206
+ };
207
+
208
+ export const usePartographyConfig = () => {
209
+ const config = useConfig<ConfigObject>();
210
+ return config.partography;
211
+ };
212
+
213
+ let encounterTypeCache: { [key: string]: string } = {};
214
+
215
+ async function discoverEncounterTypes(): Promise<{ [key: string]: string }> {
216
+ if (Object.keys(encounterTypeCache).length > 0) {
217
+ return encounterTypeCache;
218
+ }
219
+
220
+ try {
221
+ const response = await openmrsFetch(`${restBaseUrl}/encountertype?v=default`);
222
+
223
+ if (response.ok) {
224
+ const data = await response.json();
225
+ const encounterTypes = data.results || [];
226
+ encounterTypes.forEach((et: { name: string; uuid: string; display?: string }) => {
227
+ const normalizedName = et.name.toLowerCase().replace(/\s+/g, '-');
228
+ encounterTypeCache[normalizedName] = et.uuid;
229
+ if (et.display) {
230
+ const normalizedDisplay = et.display.toLowerCase().replace(/\s+/g, '-');
231
+ encounterTypeCache[normalizedDisplay] = et.uuid;
232
+ }
233
+ });
234
+
235
+ return encounterTypeCache;
236
+ } else {
237
+ return {};
238
+ }
239
+ } catch (error) {
240
+ return {};
241
+ }
242
+ }
243
+
244
+ async function getEncounterTypeForGraph(graphType: string): Promise<string | null> {
245
+ const predefinedType = PARTOGRAPHY_ENCOUNTER_TYPES[graphType as keyof typeof PARTOGRAPHY_ENCOUNTER_TYPES];
246
+ if (predefinedType) {
247
+ return predefinedType;
248
+ }
249
+ const availableTypes = await discoverEncounterTypes();
250
+ const specificType = availableTypes[graphType];
251
+ if (specificType) {
252
+ return specificType;
253
+ }
254
+
255
+ for (const pattern of defaultAnaestheticConfig.encounterPatterns) {
256
+ if (availableTypes[pattern]) {
257
+ return availableTypes[pattern];
258
+ }
259
+ }
260
+
261
+ const fallbacks = getFallbackEncounterTypes(graphType);
262
+
263
+ for (const fallback of fallbacks) {
264
+ if (availableTypes[fallback]) {
265
+ return availableTypes[fallback];
266
+ }
267
+ }
268
+
269
+ const anyType = Object.values(availableTypes)[0];
270
+ if (anyType) {
271
+ return anyType;
272
+ }
273
+
274
+ return null;
275
+ }
276
+
277
+ export function usePartographyEncounters(patientUuid: string, graphType: string) {
278
+ const customRep =
279
+ 'custom:(uuid,encounterDatetime,encounterType:(uuid,display),obs:(uuid,concept:(uuid,display),value,obsDatetime))';
280
+
281
+ const [encounterTypeUuid, setEncounterTypeUuid] = useState<string | null>(null);
282
+ useEffect(() => {
283
+ if (patientUuid && graphType) {
284
+ getEncounterTypeForGraph(graphType).then(setEncounterTypeUuid);
285
+ }
286
+ }, [patientUuid, graphType]);
287
+ const apiUrl =
288
+ encounterTypeUuid && patientUuid
289
+ ? `${restBaseUrl}/encounter?patient=${patientUuid}&encounterType=${encounterTypeUuid}&v=${customRep}`
290
+ : null;
291
+
292
+ const { data, error, isLoading, mutate } = useSWR<OpenMRSResponse<PartographyEncounter>>(
293
+ apiUrl,
294
+ async (url: string) => {
295
+ const response = await openmrsFetch(url);
296
+ return response.data;
297
+ },
298
+ );
299
+
300
+ const encounters = data?.results ?? [];
301
+
302
+ return {
303
+ encounters,
304
+ isLoading: isLoading || !encounterTypeUuid,
305
+ error,
306
+ mutate,
307
+ };
308
+ }
309
+
310
+ export function usePartographyData(patientUuid: string, graphType: string) {
311
+ const { encounters, isLoading, error, mutate } = usePartographyEncounters(patientUuid, graphType);
312
+
313
+ return {
314
+ data: encounters,
315
+ isLoading,
316
+ error,
317
+ mutate,
318
+ };
319
+ }
320
+
321
+ export async function createPartographyEncounter(
322
+ patientUuid: string,
323
+ graphType: string,
324
+ formData: any,
325
+ locationUuid?: string,
326
+ providerUuid?: string,
327
+ signal?: AbortSignal,
328
+ t?: (key: string, fallback?: string) => string,
329
+ ): Promise<{ success: boolean; message: string; encounter?: PartographyEncounter }> {
330
+ try {
331
+ const observations = buildObservations(graphType, formData);
332
+
333
+ if (observations.length === 0) {
334
+ throw new Error(t?.('noValidObservations', 'No valid observations to save') || 'No valid observations to save');
335
+ }
336
+
337
+ const encounterTypeUuid = await getEncounterTypeForGraph(graphType);
338
+ if (!encounterTypeUuid) {
339
+ throw new Error(
340
+ t?.('noEncounterTypeFound', 'No encounter type found for graph: {{graphType}}')?.replace(
341
+ '{{graphType}}',
342
+ graphType,
343
+ ) || `No encounter type found for graph: ${graphType}`,
344
+ );
345
+ }
346
+
347
+ let finalLocationUuid = locationUuid;
348
+
349
+ // If no location provided, try to get from session or use default
350
+ if (!finalLocationUuid) {
351
+ try {
352
+ const sessionResponse = await openmrsFetch(`${restBaseUrl}/session`);
353
+ if (sessionResponse.ok) {
354
+ const sessionData = await sessionResponse.json();
355
+ if (sessionData.sessionLocation?.uuid) {
356
+ finalLocationUuid = sessionData.sessionLocation.uuid;
357
+ }
358
+ }
359
+ } catch (error) {
360
+ // Session location not available, will use default
361
+ }
362
+
363
+ // Fallback to default if still no location
364
+ if (!finalLocationUuid) {
365
+ finalLocationUuid = getDefaultLocationUuid();
366
+ }
367
+ }
368
+
369
+ const timeConfig = getTimeConfig();
370
+ const encounterDatetime = toOpenMrsIsoWithColonOffset(new Date(Date.now() - timeConfig.defaultEncounterOffset));
371
+
372
+ const encounterPayload: any = {
373
+ patient: patientUuid,
374
+ location: finalLocationUuid,
375
+ encounterDatetime: encounterDatetime,
376
+ obs: observations,
377
+ encounterType: encounterTypeUuid,
378
+ };
379
+
380
+ let finalProviderUuid = providerUuid;
381
+
382
+ if (!finalProviderUuid) {
383
+ try {
384
+ const sessionResponse = await openmrsFetch(`${restBaseUrl}/session`);
385
+ if (sessionResponse.ok) {
386
+ const sessionData = await sessionResponse.json();
387
+ const currentUser = sessionData.user;
388
+ if (currentUser && currentUser.person) {
389
+ const providerResponse = await openmrsFetch(
390
+ `${restBaseUrl}/provider?person=${currentUser.person.uuid}&v=default`,
391
+ );
392
+ if (providerResponse.ok) {
393
+ const providerData = await providerResponse.json();
394
+ if (providerData.results && providerData.results.length > 0) {
395
+ finalProviderUuid = providerData.results[0].uuid;
396
+ }
397
+ }
398
+ }
399
+ }
400
+ } catch (error) {}
401
+
402
+ if (!finalProviderUuid) {
403
+ try {
404
+ const anyProviderResponse = await openmrsFetch(`${restBaseUrl}/provider?v=default&limit=1`);
405
+ if (anyProviderResponse.ok) {
406
+ const anyProviderData = await anyProviderResponse.json();
407
+ if (anyProviderData.results && anyProviderData.results.length > 0) {
408
+ finalProviderUuid = anyProviderData.results[0].uuid;
409
+ }
410
+ }
411
+ } catch (error) {}
412
+ }
413
+ }
414
+
415
+ if (finalProviderUuid) {
416
+ try {
417
+ const providerValidationResponse = await openmrsFetch(`${restBaseUrl}/provider/${finalProviderUuid}?v=default`);
418
+ if (providerValidationResponse.ok) {
419
+ encounterPayload.encounterProviders = [
420
+ {
421
+ provider: finalProviderUuid,
422
+ encounterRole: getDefaultEncounterProviderRole(),
423
+ voided: false,
424
+ },
425
+ ];
426
+ }
427
+ } catch (validationError) {}
428
+ }
429
+
430
+ // Validate required fields before making API call
431
+ if (!patientUuid) {
432
+ throw new Error('Patient UUID is required');
433
+ }
434
+ if (!finalLocationUuid) {
435
+ throw new Error('Location UUID is required');
436
+ }
437
+ if (!encounterTypeUuid) {
438
+ throw new Error('Encounter type UUID is required');
439
+ }
440
+ if (!observations || observations.length === 0) {
441
+ throw new Error('At least one observation is required');
442
+ }
443
+
444
+ const encounterUrl = `${restBaseUrl}/encounter`;
445
+ const response = await openmrsFetch(encounterUrl, {
446
+ method: 'POST',
447
+ headers: { 'Content-Type': 'application/json' },
448
+ body: JSON.stringify(encounterPayload),
449
+ signal: signal, // Add abort signal support
450
+ });
451
+
452
+ if (!response.ok) {
453
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
454
+ try {
455
+ const responseText = await response.text();
456
+ if (responseText) {
457
+ try {
458
+ const detailedError = JSON.parse(responseText);
459
+ if (detailedError.error) {
460
+ if (detailedError.error.message) {
461
+ errorMessage += ` - ${detailedError.error.message}`;
462
+ }
463
+ if (detailedError.error.detail) {
464
+ errorMessage += ` (${detailedError.error.detail})`;
465
+ }
466
+ }
467
+ } catch (parseError) {
468
+ errorMessage += ` - Raw Response: ${responseText}`;
469
+ }
470
+ }
471
+ } catch (e) {}
472
+ throw new Error(errorMessage);
473
+ }
474
+
475
+ const encounter = await response.json();
476
+
477
+ const cacheKey = generateCacheKey(patientUuid, graphType);
478
+ try {
479
+ const { mutate: globalMutate } = await import('swr');
480
+ await globalMutate(cacheKey);
481
+ const timeConfig = getTimeConfig();
482
+ setTimeout(async () => {
483
+ await globalMutate(cacheKey);
484
+ }, timeConfig.cacheInvalidationDelay);
485
+ } catch (mutateError) {}
486
+
487
+ return {
488
+ success: true,
489
+ message:
490
+ t?.('partographyDataSavedSuccessfully', 'Partography data saved successfully') ||
491
+ 'Partography data saved successfully',
492
+ encounter,
493
+ };
494
+ } catch (error) {
495
+ return {
496
+ success: false,
497
+ message:
498
+ t?.('failedToSavePartographyData', 'Failed to save partography data: {{error}}')?.replace(
499
+ '{{error}}',
500
+ error instanceof Error ? error.message : 'Failed to save partography data',
501
+ ) ||
502
+ `Failed to save partography data: ${
503
+ error instanceof Error ? error.message : 'Failed to save partography data'
504
+ }`,
505
+ };
506
+ }
507
+ }
508
+
509
+ function buildObservations(graphType: string, formData: any): any[] {
510
+ const observations = [];
511
+ const timeConfig = getTimeConfig();
512
+ const obsDatetime = toOpenMrsIsoWithColonOffset(new Date(Date.now() - timeConfig.defaultEncounterOffset));
513
+ const ANAESTHETIC_SYSTOLIC_BP_CONCEPT = '5085AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
514
+ const ANAESTHETIC_DIASTOLIC_BP_CONCEPT = '5086AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
515
+ const ANAESTHETIC_HEART_RATE_CONCEPT = '5087AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
516
+ const ANAESTHETIC_SPO2_CONCEPT = '5092AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
517
+ const ANAESTHETIC_ETCO2_CONCEPT = 'c6f370bd-d25d-4c1b-87d4-973eaaa73569';
518
+ const MAINTENANCE_OF_ANAESTHESIA_CONCEPT = '164254AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
519
+ const CONCENTRATION_RATE_CONCEPT = '160632AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
520
+ const MEDICATION_GIVEN_CONCEPT = '164231AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
521
+ const POST_OPERATIVE_POSITION_CONCEPT = '357b7ae8-1326-4c2c-b1c0-7e7260fba2b7';
522
+ const ESTIMATED_BLOOD_LOSS_CONCEPT = '161928AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
523
+ const RESULTS_OF_OPERATION_CONCEPT = 'da2029a1-193f-48df-88d8-45d3258458c1';
524
+ const POST_OPERATIVE_MANAGEMENT_CONCEPT = 'dd22e450-256e-4925-bcd1-ce39454fb46f';
525
+
526
+ switch (graphType) {
527
+ case 'pulse-bp-combined':
528
+ if (formData.time) {
529
+ observations.push({
530
+ concept: PARTOGRAPHY_CONCEPTS['pulse-time-slot'] || PARTOGRAPHY_CONCEPTS['time-slot'],
531
+ value: `Time: ${formData.time}`,
532
+ obsDatetime,
533
+ });
534
+ }
535
+ if (formData.heartRate || formData.pulse) {
536
+ observations.push({
537
+ concept: ANAESTHETIC_HEART_RATE_CONCEPT,
538
+ value: parseFloat(formData.heartRate ?? formData.pulse),
539
+ obsDatetime,
540
+ });
541
+ }
542
+ if (formData.systolic) {
543
+ observations.push({
544
+ concept: ANAESTHETIC_SYSTOLIC_BP_CONCEPT,
545
+ value: parseFloat(formData.systolic),
546
+ obsDatetime,
547
+ });
548
+ } else if (formData.systolicBP) {
549
+ observations.push({
550
+ concept: ANAESTHETIC_SYSTOLIC_BP_CONCEPT,
551
+ value: parseFloat(formData.systolicBP),
552
+ obsDatetime,
553
+ });
554
+ }
555
+ if (formData.diastolic) {
556
+ observations.push({
557
+ concept: ANAESTHETIC_DIASTOLIC_BP_CONCEPT,
558
+ value: parseFloat(formData.diastolic),
559
+ obsDatetime,
560
+ });
561
+ } else if (formData.diastolicBP) {
562
+ observations.push({
563
+ concept: ANAESTHETIC_DIASTOLIC_BP_CONCEPT,
564
+ value: parseFloat(formData.diastolicBP),
565
+ obsDatetime,
566
+ });
567
+ }
568
+ if (formData.spo2) {
569
+ observations.push({
570
+ concept: ANAESTHETIC_SPO2_CONCEPT,
571
+ value: parseFloat(formData.spo2),
572
+ obsDatetime,
573
+ });
574
+ }
575
+ if (formData.etco2) {
576
+ observations.push({
577
+ concept: ANAESTHETIC_ETCO2_CONCEPT,
578
+ value: parseFloat(formData.etco2),
579
+ obsDatetime,
580
+ });
581
+ }
582
+ break;
583
+ case 'membrane-amniotic-fluid':
584
+ if (formData.time) {
585
+ observations.push({
586
+ concept: PARTOGRAPHY_CONCEPTS['fetal-heart-rate-time'],
587
+ value: `Time: ${formData.time}`,
588
+ obsDatetime,
589
+ });
590
+ }
591
+
592
+ const amnioticFluidMap = {
593
+ 'Membrane intact': '164899AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
594
+ 'Clear liquor': '159484AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
595
+ 'Meconium Stained': '134488AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
596
+ Absent: '163747AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
597
+ 'Blood Stained': '1077AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
598
+ };
599
+ let amnioticFluidValue = formData.amnioticFluid;
600
+ if (amnioticFluidValue && amnioticFluidMap[amnioticFluidValue as keyof typeof amnioticFluidMap]) {
601
+ amnioticFluidValue = amnioticFluidMap[amnioticFluidValue as keyof typeof amnioticFluidMap];
602
+ }
603
+ if (amnioticFluidValue) {
604
+ observations.push({
605
+ concept: '162653AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
606
+ value: amnioticFluidValue,
607
+ obsDatetime,
608
+ });
609
+ }
610
+ const mouldingMap = {
611
+ '0': '1107AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
612
+ '+': '1362AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
613
+ '++': '1363AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
614
+ '+++': '1364AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
615
+ };
616
+ let mouldingValue = formData.moulding;
617
+ if (mouldingValue && mouldingValue in mouldingMap) {
618
+ mouldingValue = mouldingMap[mouldingValue as keyof typeof mouldingMap];
619
+ }
620
+ if (mouldingValue) {
621
+ observations.push({
622
+ concept: '166527AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
623
+ value: mouldingValue,
624
+ obsDatetime,
625
+ });
626
+ }
627
+ break;
628
+ case 'fetal-heart-rate':
629
+ if (formData.fetalHeartRate || formData.value || formData.measurementValue) {
630
+ observations.push({
631
+ concept: PARTOGRAPHY_CONCEPTS['fetal-heart-rate'],
632
+ value: parseFloat(formData.fetalHeartRate || formData.value || formData.measurementValue),
633
+ obsDatetime,
634
+ });
635
+ }
636
+
637
+ // Save hour as a string in the format 'Hour: X' to match backend expectations
638
+ if (formData.hour !== undefined && formData.hour !== '') {
639
+ try {
640
+ const hourValue = parseFloat(formData.hour);
641
+ if (!isNaN(hourValue) && hourValue >= 0 && hourValue <= 24) {
642
+ observations.push({
643
+ concept: PARTOGRAPHY_CONCEPTS['fetal-heart-rate-hour'],
644
+ value: `Hour: ${hourValue}`,
645
+ obsDatetime,
646
+ });
647
+ }
648
+ } catch (error) {
649
+ // Skipping invalid hour observation
650
+ }
651
+ }
652
+
653
+ // Using text concept for time (should work fine)
654
+ if (formData.time) {
655
+ try {
656
+ observations.push({
657
+ concept: PARTOGRAPHY_CONCEPTS['fetal-heart-rate-time'],
658
+ value: `Time: ${formData.time}`,
659
+ obsDatetime,
660
+ });
661
+ } catch (error) {
662
+ // Skipping invalid time observation
663
+ }
664
+ }
665
+ break;
666
+
667
+ case 'cervical-dilation':
668
+ if (formData.value || formData.measurementValue) {
669
+ observations.push({
670
+ concept: PARTOGRAPHY_CONCEPTS['cervical-dilation'],
671
+ value: parseFloat(formData.value || formData.measurementValue),
672
+ obsDatetime,
673
+ });
674
+ }
675
+
676
+ if (formData.amnioticFluid) {
677
+ observations.push({
678
+ concept: PARTOGRAPHY_CONCEPTS['amniotic-fluid'],
679
+ value: formData.amnioticFluid,
680
+ obsDatetime,
681
+ });
682
+ }
683
+
684
+ if (formData.moulding) {
685
+ observations.push({
686
+ concept: PARTOGRAPHY_CONCEPTS['moulding'],
687
+ value: formData.moulding,
688
+ obsDatetime,
689
+ });
690
+ }
691
+ break;
692
+
693
+ case 'descent-of-head':
694
+ if (formData.value || formData.measurementValue) {
695
+ let conceptValue = formData.value || formData.measurementValue;
696
+
697
+ if (formData.conceptUuid) {
698
+ conceptValue = formData.conceptUuid;
699
+ }
700
+
701
+ observations.push({
702
+ concept: PARTOGRAPHY_CONCEPTS['descent-of-head'],
703
+ value: conceptValue,
704
+ obsDatetime,
705
+ });
706
+ }
707
+ break;
708
+
709
+ case 'uterine-contractions':
710
+ if (formData.contractionCount !== undefined) {
711
+ observations.push({
712
+ concept: '159682AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
713
+ value: parseInt(formData.contractionCount, 10),
714
+ obsDatetime,
715
+ });
716
+ }
717
+ if (formData.contractionLevel) {
718
+ observations.push({
719
+ concept: '163750AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
720
+ value: formData.contractionLevel, // UUID string for coded value
721
+ obsDatetime,
722
+ });
723
+ }
724
+ if (formData.timeSlot) {
725
+ observations.push({
726
+ concept: URINE_TEST_TIME_SLOT_CONCEPT,
727
+ value: `Time: ${formData.timeSlot}`,
728
+ obsDatetime,
729
+ });
730
+ }
731
+ break;
732
+
733
+ case 'maternal-pulse':
734
+ if (formData.value || formData.measurementValue) {
735
+ observations.push({
736
+ concept: PARTOGRAPHY_CONCEPTS['maternal-pulse'],
737
+ value: parseFloat(formData.value || formData.measurementValue),
738
+ obsDatetime,
739
+ });
740
+ }
741
+ break;
742
+
743
+ case 'blood-pressure':
744
+ if (formData.systolic) {
745
+ observations.push({
746
+ concept: PARTOGRAPHY_CONCEPTS['systolic-bp'],
747
+ value: parseFloat(formData.systolic),
748
+ obsDatetime,
749
+ });
750
+ }
751
+ if (formData.diastolic) {
752
+ observations.push({
753
+ concept: PARTOGRAPHY_CONCEPTS['diastolic-bp'],
754
+ value: parseFloat(formData.diastolic),
755
+ obsDatetime,
756
+ });
757
+ }
758
+ break;
759
+
760
+ case 'temperature':
761
+ if (formData.value || formData.measurementValue) {
762
+ observations.push({
763
+ concept: PARTOGRAPHY_CONCEPTS['temperature'],
764
+ value: parseFloat(formData.value || formData.measurementValue),
765
+ obsDatetime,
766
+ });
767
+ }
768
+ // Save time observation if provided
769
+ if (formData.time) {
770
+ observations.push({
771
+ concept: '160632AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
772
+ value: `Time: ${formData.time}`,
773
+ obsDatetime,
774
+ });
775
+ }
776
+ break;
777
+
778
+ case 'urine-analysis':
779
+ if (formData.proteinLevel) {
780
+ observations.push({
781
+ concept: PARTOGRAPHY_CONCEPTS['protein-level'],
782
+ value: formData.proteinLevel,
783
+ obsDatetime,
784
+ });
785
+ }
786
+ if (formData.glucoseLevel) {
787
+ observations.push({
788
+ concept: PARTOGRAPHY_CONCEPTS['glucose-level'],
789
+ value: formData.glucoseLevel,
790
+ obsDatetime,
791
+ });
792
+ }
793
+ if (formData.ketoneLevel) {
794
+ observations.push({
795
+ concept: PARTOGRAPHY_CONCEPTS['ketone-level'],
796
+ value: formData.ketoneLevel,
797
+ obsDatetime,
798
+ });
799
+ }
800
+ if (formData.volume !== undefined && formData.volume !== null && formData.volume !== '') {
801
+ observations.push({
802
+ concept: PARTOGRAPHY_CONCEPTS['urine-volume'],
803
+ value: formData.volume,
804
+ obsDatetime,
805
+ });
806
+ }
807
+
808
+ if (formData.timeSlot) {
809
+ observations.push({
810
+ concept: URINE_TEST_TIME_SLOT_CONCEPT,
811
+ value: `Time: ${formData.timeSlot}`,
812
+ obsDatetime,
813
+ });
814
+ }
815
+
816
+ if (formData.timeSampleCollected) {
817
+ observations.push({
818
+ concept: TIME_SAMPLE_COLLECTED,
819
+ value: formData.timeSampleCollected,
820
+ obsDatetime,
821
+ });
822
+ }
823
+ if (formData.eventDescription) {
824
+ observations.push({
825
+ concept: PARTOGRAPHY_CONCEPTS['event-description'],
826
+ value: formData.eventDescription,
827
+ obsDatetime,
828
+ });
829
+ }
830
+
831
+ if (formData.timeResultsReturned) {
832
+ observations.push({
833
+ concept: TIME_RESULTS_RETURNED,
834
+ value: formData.timeResultsReturned,
835
+ obsDatetime,
836
+ });
837
+ }
838
+ break;
839
+
840
+ case 'drugs-fluids':
841
+ if (formData.maintenanceAgent) {
842
+ observations.push({
843
+ concept: MAINTENANCE_OF_ANAESTHESIA_CONCEPT,
844
+ value: formData.maintenanceAgent,
845
+ obsDatetime,
846
+ });
847
+ }
848
+ if (formData.concentrationRate) {
849
+ observations.push({
850
+ concept: CONCENTRATION_RATE_CONCEPT,
851
+ value: String(formData.concentrationRate).trim(),
852
+ obsDatetime,
853
+ });
854
+ }
855
+ if (formData.medicationGiven) {
856
+ observations.push({
857
+ concept: MEDICATION_GIVEN_CONCEPT,
858
+ value: formData.medicationGiven,
859
+ obsDatetime,
860
+ });
861
+ }
862
+ if (formData.fluidsGiven) {
863
+ observations.push({
864
+ concept: MEDICATION_GIVEN_CONCEPT,
865
+ value: formData.fluidsGiven,
866
+ obsDatetime,
867
+ });
868
+ }
869
+ if (formData.medication || formData.drugName) {
870
+ observations.push({
871
+ concept: PARTOGRAPHY_CONCEPTS['medication'],
872
+ value: formData.medication || formData.drugName,
873
+ obsDatetime,
874
+ });
875
+ }
876
+ break;
877
+
878
+ case 'post-operative-summary':
879
+ if (formData.position) {
880
+ observations.push({
881
+ concept: POST_OPERATIVE_POSITION_CONCEPT,
882
+ value: formData.position,
883
+ obsDatetime,
884
+ });
885
+ }
886
+ if (
887
+ formData.estimatedBloodLoss !== undefined &&
888
+ formData.estimatedBloodLoss !== null &&
889
+ formData.estimatedBloodLoss !== ''
890
+ ) {
891
+ observations.push({
892
+ concept: ESTIMATED_BLOOD_LOSS_CONCEPT,
893
+ value: Number(formData.estimatedBloodLoss),
894
+ obsDatetime,
895
+ });
896
+ }
897
+ if (formData.resultsOfOperation) {
898
+ observations.push({
899
+ concept: RESULTS_OF_OPERATION_CONCEPT,
900
+ value: formData.resultsOfOperation,
901
+ obsDatetime,
902
+ });
903
+ }
904
+ if (formData.postOperativeManagement) {
905
+ observations.push({
906
+ concept: POST_OPERATIVE_MANAGEMENT_CONCEPT,
907
+ value: formData.postOperativeManagement,
908
+ obsDatetime,
909
+ });
910
+ }
911
+ break;
912
+
913
+ case 'progress-events':
914
+ if (formData.eventType) {
915
+ observations.push({
916
+ concept: PARTOGRAPHY_CONCEPTS['event-type'],
917
+ value: formData.eventType,
918
+ obsDatetime,
919
+ });
920
+ }
921
+ if (formData.eventDescription) {
922
+ observations.push({
923
+ concept: PARTOGRAPHY_CONCEPTS['event-description'],
924
+ value: formData.eventDescription,
925
+ obsDatetime,
926
+ });
927
+ }
928
+ break;
929
+
930
+ default:
931
+ if (formData.value || formData.measurementValue) {
932
+ const conceptUuid = PARTOGRAPHY_CONCEPTS[graphType as keyof typeof PARTOGRAPHY_CONCEPTS];
933
+ if (conceptUuid) {
934
+ observations.push({
935
+ concept: conceptUuid,
936
+ value: parseFloat(formData.value || formData.measurementValue),
937
+ obsDatetime,
938
+ });
939
+ }
940
+ }
941
+ break;
942
+ }
943
+
944
+ return observations;
945
+ }
946
+
947
+ export function transformEncounterToChartData(encounters: PartographyEncounter[], graphType: string): any[] {
948
+ const chartData: any[] = [];
949
+
950
+ encounters.forEach((encounter) => {
951
+ const encounterTime = new Date(encounter.encounterDatetime).toLocaleTimeString('en-US', {
952
+ hour: '2-digit',
953
+ minute: '2-digit',
954
+ hour12: false,
955
+ });
956
+
957
+ encounter.obs?.forEach((obs) => {
958
+ try {
959
+ if (typeof obs.value === 'string' && obs.value.startsWith('{')) {
960
+ const parsedData = JSON.parse(obs.value);
961
+ if (parsedData.graphType === graphType && parsedData.data) {
962
+ const formData = parsedData.data;
963
+ const value = formData.value || formData.measurementValue;
964
+ if (value) {
965
+ const dataPoint = {
966
+ group: getGraphTypeDisplayName(graphType),
967
+ time: formData.time || encounterTime,
968
+ value: parseFloat(value),
969
+ };
970
+ chartData.push(dataPoint);
971
+ }
972
+ }
973
+ } else {
974
+ const conceptUuid = obs.concept.uuid;
975
+ let value = null;
976
+ let groupName = '';
977
+
978
+ if (graphType === 'uterine-contractions') {
979
+ if (conceptUuid === PARTOGRAPHY_CONCEPTS['uterine-contractions']) {
980
+ value = parseFloat(obs.value as string);
981
+ groupName = getGraphTypeDisplayName('uterine-contractions');
982
+ }
983
+
984
+ const contractionLevelMap: Record<string, string> = {
985
+ MOULDING_NONE_CONCEPT: 'none',
986
+ CONTRACTION_LEVEL_MILD_CONCEPT: 'mild',
987
+ CONTRACTION_LEVEL_MODERATE_CONCEPT: 'moderate',
988
+ CONTRACTION_LEVEL_STRONG_CONCEPT: 'strong',
989
+ };
990
+ if (contractionLevelMap[conceptUuid]) {
991
+ const contractionLevelValueMap: Record<string, number> = {
992
+ none: 0,
993
+ mild: 1,
994
+ moderate: 2,
995
+ strong: 3,
996
+ };
997
+ value = contractionLevelValueMap[contractionLevelMap[conceptUuid]];
998
+ groupName = contractionLevelMap[conceptUuid];
999
+ }
1000
+ } else {
1001
+ switch (conceptUuid) {
1002
+ case PARTOGRAPHY_CONCEPTS['fetal-heart-rate']:
1003
+ if (graphType === 'fetal-heart-rate') {
1004
+ value = parseFloat(obs.value as string);
1005
+ groupName = getGraphTypeDisplayName('fetal-heart-rate');
1006
+ }
1007
+ break;
1008
+ case PARTOGRAPHY_CONCEPTS['cervical-dilation']:
1009
+ if (graphType === 'cervical-dilation') {
1010
+ value = parseFloat(obs.value as string);
1011
+ groupName = getGraphTypeDisplayName('cervical-dilation');
1012
+ }
1013
+ break;
1014
+ case PARTOGRAPHY_CONCEPTS['descent-of-head']:
1015
+ if (graphType === 'descent-of-head') {
1016
+ value = getStationValue(obs.value as string) ?? parseFloat(obs.value as string);
1017
+ groupName = getGraphTypeDisplayName('descent-of-head');
1018
+ }
1019
+ break;
1020
+ case PARTOGRAPHY_CONCEPTS['maternal-pulse']:
1021
+ if (graphType === 'maternal-pulse') {
1022
+ value = parseFloat(obs.value as string);
1023
+ groupName = getGraphTypeDisplayName('maternal-pulse');
1024
+ }
1025
+ break;
1026
+ case PARTOGRAPHY_CONCEPTS['systolic-bp']:
1027
+ if (graphType === 'blood-pressure') {
1028
+ value = parseFloat(obs.value as string);
1029
+ groupName = 'Systolic';
1030
+ }
1031
+ break;
1032
+ case PARTOGRAPHY_CONCEPTS['diastolic-bp']:
1033
+ if (graphType === 'blood-pressure') {
1034
+ value = parseFloat(obs.value as string);
1035
+ groupName = 'Diastolic';
1036
+ }
1037
+ break;
1038
+ case PARTOGRAPHY_CONCEPTS['temperature']:
1039
+ if (graphType === 'temperature') {
1040
+ value = parseFloat(obs.value as string);
1041
+ groupName = getGraphTypeDisplayName('temperature');
1042
+ }
1043
+ break;
1044
+ default:
1045
+ if (typeof obs.value === 'number' || !isNaN(parseFloat(obs.value as string))) {
1046
+ value = parseFloat(obs.value as string);
1047
+ groupName = getGraphTypeDisplayName(graphType);
1048
+ }
1049
+ break;
1050
+ }
1051
+ }
1052
+ if (value !== null && groupName) {
1053
+ const dataPoint = {
1054
+ group: groupName,
1055
+ time: encounterTime,
1056
+ value: value,
1057
+ };
1058
+ chartData.push(dataPoint);
1059
+ }
1060
+ }
1061
+ } catch (e) {}
1062
+ });
1063
+ });
1064
+
1065
+ return chartData.sort((a, b) => {
1066
+ const timeA = a.time.split(':').map(Number);
1067
+ const timeB = b.time.split(':').map(Number);
1068
+ const minutesA = timeA[0] * 60 + (timeA[1] || 0);
1069
+ const minutesB = timeB[0] * 60 + (timeB[1] || 0);
1070
+
1071
+ return minutesA - minutesB;
1072
+ });
1073
+ }
1074
+
1075
+ export async function createTestPartographyData(patientUuid: string) {
1076
+ const testConfig = getTestDataConfig();
1077
+ const testGraphTypes = testConfig.testGraphTypes;
1078
+ const sampleDataPoints = testConfig.sampleDataPoints;
1079
+ const valueIncrement = testConfig.valueIncrement;
1080
+ const bloodPressureDecrement = testConfig.bloodPressureDecrement;
1081
+
1082
+ for (const [index, graphType] of testGraphTypes.entries()) {
1083
+ const enhancedDataPoints = sampleDataPoints.map((point: { time: string; value: number }) => ({
1084
+ ...point,
1085
+ value: point.value + index * valueIncrement,
1086
+ }));
1087
+
1088
+ for (const dataPoint of enhancedDataPoints) {
1089
+ const formData =
1090
+ graphType === 'blood-pressure'
1091
+ ? {
1092
+ systolic: dataPoint.value,
1093
+ diastolic: dataPoint.value - bloodPressureDecrement,
1094
+ time: dataPoint.time,
1095
+ }
1096
+ : { value: dataPoint.value, time: dataPoint.time };
1097
+
1098
+ try {
1099
+ await createPartographyEncounter(patientUuid, graphType, formData);
1100
+ } catch (error) {}
1101
+ }
1102
+ }
1103
+ }
1104
+
1105
+ export function transformEncounterToTableData(
1106
+ encounters: PartographyEncounter[],
1107
+ graphType: string,
1108
+ t?: (key: string, fallback?: string) => string,
1109
+ ): any[] {
1110
+ const tableData: any[] = [];
1111
+
1112
+ const getUnitForGraphType = (type: string): string => {
1113
+ return getPartographyUnit(type as PartographyGraphType) || '';
1114
+ };
1115
+
1116
+ interface ObservationConcept {
1117
+ uuid: string;
1118
+ display?: string;
1119
+ }
1120
+
1121
+ interface ObservationForMapping {
1122
+ concept?: ObservationConcept;
1123
+ }
1124
+
1125
+ const mapObservationToGraphType = (obs: ObservationForMapping, targetGraphType: string): boolean => {
1126
+ return doesObservationMapToGraphType(obs.concept?.uuid || '', targetGraphType);
1127
+ };
1128
+
1129
+ encounters.forEach((encounter, index) => {
1130
+ const encounterDate = new Date(encounter.encounterDatetime);
1131
+ const dateTime = `${encounterDate.toLocaleDateString()} — ${encounterDate.toLocaleTimeString('en-US', {
1132
+ hour: '2-digit',
1133
+ minute: '2-digit',
1134
+ hour12: false,
1135
+ })}`;
1136
+
1137
+ if (graphType === 'uterine-contractions') {
1138
+ let timeSlot = '';
1139
+ let contractionCount = '';
1140
+ let contractionLevel = 'none';
1141
+ if (Array.isArray(encounter.obs)) {
1142
+ for (const obs of encounter.obs) {
1143
+ // Time slot
1144
+ if (
1145
+ obs.concept.uuid === EVENT_DESCRIPTION_CONCEPT &&
1146
+ typeof obs.value === 'string' &&
1147
+ obs.value.startsWith('Time:')
1148
+ ) {
1149
+ const match = obs.value.match(/Time:\s*(.+)/);
1150
+ if (match) {
1151
+ timeSlot = match[1].trim();
1152
+ }
1153
+ }
1154
+
1155
+ if (obs.concept.uuid === CONTRACTION_COUNT_CONCEPT) {
1156
+ contractionCount = String(obs.value);
1157
+ }
1158
+ }
1159
+
1160
+ for (const obs of encounter.obs) {
1161
+ if (obs.concept.uuid === UTERINE_CONTRACTIONS_CONCEPT) {
1162
+ let levelUuid = '';
1163
+ if (typeof obs.value === 'string') {
1164
+ levelUuid = obs.value;
1165
+ } else if (obs.value != null && typeof obs.value === 'object' && (obs.value as any)?.uuid) {
1166
+ levelUuid = (obs.value as any).uuid;
1167
+ }
1168
+ if (levelUuid === MOULDING_NONE_CONCEPT) {
1169
+ contractionLevel = 'none';
1170
+ }
1171
+ if (levelUuid === CONTRACTION_LEVEL_MILD_CONCEPT) {
1172
+ contractionLevel = 'mild';
1173
+ }
1174
+ if (levelUuid === CONTRACTION_LEVEL_MODERATE_CONCEPT) {
1175
+ contractionLevel = 'moderate';
1176
+ }
1177
+ if (levelUuid === CONTRACTION_LEVEL_STRONG_CONCEPT) {
1178
+ contractionLevel = 'strong';
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1183
+ if (contractionCount || contractionLevel !== 'none') {
1184
+ tableData.push({
1185
+ id: `${graphType}-${index}`,
1186
+ dateTime,
1187
+ timeSlot,
1188
+ contractionCount,
1189
+ contractionLevel,
1190
+ unit: getUnitForGraphType(graphType),
1191
+ });
1192
+ }
1193
+ } else {
1194
+ encounter.obs.forEach((obs, obsIndex) => {
1195
+ try {
1196
+ if (typeof obs.value === 'string') {
1197
+ const parsedData = JSON.parse(obs.value);
1198
+ if (parsedData.graphType === graphType && parsedData.data) {
1199
+ const formData = parsedData.data;
1200
+ let value = formData.value || formData.measurementValue;
1201
+ if (graphType === 'descent-of-head' && formData.conceptUuid) {
1202
+ value = formData.conceptUuid;
1203
+ }
1204
+ if (value) {
1205
+ let displayValue;
1206
+ if (graphType === 'descent-of-head') {
1207
+ let valueToMap;
1208
+ if (typeof value === 'object' && value !== null) {
1209
+ const valueObj = value as any;
1210
+ if (valueObj.conceptUuid) {
1211
+ valueToMap = valueObj.conceptUuid;
1212
+ } else if (valueObj.value) {
1213
+ valueToMap = valueObj.value;
1214
+ } else {
1215
+ valueToMap = String(value);
1216
+ }
1217
+ } else {
1218
+ valueToMap = String(value);
1219
+ }
1220
+ displayValue = getStationDisplay(valueToMap);
1221
+ } else {
1222
+ const numericValue = parseFloat(String(value));
1223
+ if (!isNaN(numericValue)) {
1224
+ displayValue = numericValue.toFixed(1);
1225
+ } else {
1226
+ displayValue = String(value);
1227
+ }
1228
+ }
1229
+ const rowData = {
1230
+ id: `${graphType}-${index}-${obsIndex}`,
1231
+ dateTime,
1232
+ value: displayValue,
1233
+ unit: getUnitForGraphType(graphType),
1234
+ };
1235
+ tableData.push(rowData);
1236
+ }
1237
+ }
1238
+ } else {
1239
+ throw new Error('Not a JSON string');
1240
+ }
1241
+ } catch (e) {
1242
+ if (mapObservationToGraphType(obs, graphType)) {
1243
+ let conceptName = obs.concept?.display || t?.('unknown', 'Unknown') || 'Unknown';
1244
+ let unit = getUnitForGraphType(graphType);
1245
+ if (graphType === 'progress-events') {
1246
+ const eventInfo = getProgressEventInfo(obs.concept?.uuid || '');
1247
+ if (eventInfo) {
1248
+ conceptName = eventInfo.name;
1249
+ unit = eventInfo.unit;
1250
+ } else {
1251
+ conceptName = obs.concept?.display || t?.('progressEvent', 'Progress Event') || 'Progress Event';
1252
+ }
1253
+ }
1254
+ let displayValue;
1255
+ if (graphType === 'descent-of-head') {
1256
+ let valueToMap;
1257
+ if (typeof obs.value === 'object' && obs.value !== null) {
1258
+ const valueObj = obs.value as any;
1259
+ if (valueObj.conceptUuid) {
1260
+ valueToMap = valueObj.conceptUuid;
1261
+ } else if (valueObj.value) {
1262
+ valueToMap = valueObj.value;
1263
+ } else {
1264
+ valueToMap = String(obs.value);
1265
+ }
1266
+ } else {
1267
+ valueToMap = String(obs.value);
1268
+ }
1269
+ displayValue = getStationDisplay(valueToMap);
1270
+ } else {
1271
+ const numericValue = parseFloat(String(obs.value));
1272
+ if (!isNaN(numericValue)) {
1273
+ displayValue = numericValue.toFixed(1);
1274
+ } else {
1275
+ displayValue = String(obs.value);
1276
+ }
1277
+ }
1278
+ const rowData = {
1279
+ id: `${graphType}-${index}-${obsIndex}`,
1280
+ dateTime,
1281
+ measurement: conceptName,
1282
+ value: displayValue,
1283
+ unit: unit,
1284
+ };
1285
+ tableData.push(rowData);
1286
+ }
1287
+ }
1288
+ });
1289
+ }
1290
+ });
1291
+
1292
+ return tableData.sort(
1293
+ (a, b) => new Date(a.dateTime.split(' — ')[0]).getTime() - new Date(b.dateTime.split(' — ')[0]).getTime(),
1294
+ );
1295
+ }
1296
+
1297
+ export function useFetalHeartRateData(patientUuid: string) {
1298
+ const fetcher = (url: string) => openmrsFetch(url).then((res) => res.json());
1299
+
1300
+ const { data, error, isLoading, mutate } = useSWR(
1301
+ patientUuid
1302
+ ? `${restBaseUrl}/encounter?patient=${patientUuid}&encounterType=${MCH_PARTOGRAPHY_ENCOUNTER_UUID}&v=full&limit=100&order=desc`
1303
+ : null,
1304
+ fetcher,
1305
+ {},
1306
+ );
1307
+
1308
+ const fetalHeartRateData = useMemo(() => {
1309
+ try {
1310
+ if (!data?.results || !Array.isArray(data.results)) {
1311
+ return [];
1312
+ }
1313
+
1314
+ const fetalHeartRateEntries = [];
1315
+
1316
+ for (const encounter of data.results) {
1317
+ if (!encounter.obs || !Array.isArray(encounter.obs)) {
1318
+ continue;
1319
+ }
1320
+
1321
+ const fetalHeartRateObs = encounter.obs.find(
1322
+ (obs: PartographyObservation) => obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate'],
1323
+ );
1324
+
1325
+ if (fetalHeartRateObs) {
1326
+ const encounterDatetime = new Date(encounter.encounterDatetime);
1327
+
1328
+ let hour = 0;
1329
+ let time = '';
1330
+
1331
+ const hourObs = encounter.obs.find(
1332
+ (obs: PartographyObservation) =>
1333
+ obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-hour'] &&
1334
+ obs.value &&
1335
+ typeof obs.value === 'string' &&
1336
+ obs.value.startsWith('Hour:'),
1337
+ );
1338
+
1339
+ const timeObs = encounter.obs.find(
1340
+ (obs: PartographyObservation) =>
1341
+ obs.concept.uuid === PARTOGRAPHY_CONCEPTS['fetal-heart-rate-time'] &&
1342
+ obs.value &&
1343
+ typeof obs.value === 'string' &&
1344
+ obs.value.startsWith('Time:'),
1345
+ );
1346
+
1347
+ if (hourObs && typeof hourObs.value === 'string') {
1348
+ const hourMatch = hourObs.value.match(/Hour:\s*([0-9.]+)/);
1349
+ if (hourMatch) {
1350
+ hour = parseFloat(hourMatch[1]) || 0;
1351
+ }
1352
+ }
1353
+
1354
+ if (timeObs && typeof timeObs.value === 'string') {
1355
+ const timeMatch = timeObs.value.match(/Time:\s*(.+)/);
1356
+ if (timeMatch) {
1357
+ time = timeMatch[1].trim();
1358
+ }
1359
+ }
1360
+
1361
+ const entry = {
1362
+ id: `fhr-${fetalHeartRateObs.uuid}`,
1363
+ uuid: fetalHeartRateObs.uuid,
1364
+ encounterUuid: encounter.uuid,
1365
+ fetalHeartRate: parseFloat(fetalHeartRateObs.value) || 0,
1366
+ hour,
1367
+ time,
1368
+ date: encounterDatetime.toLocaleDateString(),
1369
+ encounterDatetime: encounterDatetime.toISOString(),
1370
+ obsDatetime: fetalHeartRateObs.obsDatetime,
1371
+ };
1372
+
1373
+ fetalHeartRateEntries.push(entry);
1374
+ }
1375
+ }
1376
+
1377
+ const sortedEntries = fetalHeartRateEntries.sort(
1378
+ (a, b) => new Date(b.encounterDatetime).getTime() - new Date(a.encounterDatetime).getTime(),
1379
+ );
1380
+
1381
+ return sortedEntries;
1382
+ } catch (error) {
1383
+ return [];
1384
+ }
1385
+ }, [data]);
1386
+
1387
+ return {
1388
+ fetalHeartRateData,
1389
+ isLoading,
1390
+ error,
1391
+ mutate,
1392
+ };
1393
+ }
1394
+
1395
+ // Function to save fetal heart rate data to OpenMRS
1396
+ export async function saveFetalHeartRateData(
1397
+ patientUuid: string,
1398
+ formData: { hour: number; time: string; fetalHeartRate: number },
1399
+ locationUuid?: string,
1400
+ providerUuid?: string,
1401
+ ) {
1402
+ try {
1403
+ const result = await createPartographyEncounter(
1404
+ patientUuid,
1405
+ 'fetal-heart-rate',
1406
+ formData,
1407
+ locationUuid,
1408
+ providerUuid,
1409
+ );
1410
+
1411
+ return result;
1412
+ } catch (error) {
1413
+ return {
1414
+ success: false,
1415
+ message: error instanceof Error ? error.message : 'Failed to save fetal heart rate data',
1416
+ };
1417
+ }
1418
+ }
1419
+
1420
+ // Function to save drug order data to OpenMRS
1421
+ export async function saveDrugOrderData(
1422
+ patientUuid: string,
1423
+ formData: {
1424
+ maintenanceAgent: string;
1425
+ concentrationRate: string;
1426
+ medicationGiven: string;
1427
+ fluidsGiven: string;
1428
+ },
1429
+ locationUuid?: string,
1430
+ providerUuid?: string,
1431
+ signal?: AbortSignal,
1432
+ ) {
1433
+ try {
1434
+ const result = await createPartographyEncounter(
1435
+ patientUuid,
1436
+ 'drugs-fluids',
1437
+ formData,
1438
+ locationUuid,
1439
+ providerUuid,
1440
+ signal,
1441
+ );
1442
+ return result;
1443
+ } catch (error) {
1444
+ return {
1445
+ success: false,
1446
+ message: error instanceof Error ? error.message : 'Failed to save fetal heart rate data',
1447
+ };
1448
+ }
1449
+ }
1450
+
1451
+ export function useDrugOrders(patientUuid: string) {
1452
+ const apiUrl = patientUuid
1453
+ ? `${restBaseUrl}/order?patient=${patientUuid}&orderType=131168f4-15f5-102d-96e4-000c29c2a5d7&v=full&limit=50`
1454
+ : null;
1455
+
1456
+ const { data, error, isLoading, mutate } = useSWR(apiUrl, openmrsFetch);
1457
+
1458
+ const drugOrders = useMemo(() => {
1459
+ const responseData = data?.data as any;
1460
+
1461
+ if (!responseData?.results || !Array.isArray(responseData.results)) {
1462
+ return [];
1463
+ }
1464
+
1465
+ const allOrders = responseData.results;
1466
+ const activeOrders = allOrders
1467
+ .filter((order: any) => order.action === 'NEW' && !order.dateStopped)
1468
+ .sort((a: any, b: any) => {
1469
+ const dateA = a.dateActivated ? new Date(a.dateActivated).getTime() : 0;
1470
+ const dateB = b.dateActivated ? new Date(b.dateActivated).getTime() : 0;
1471
+ return dateB - dateA;
1472
+ });
1473
+
1474
+ const processedOrders = activeOrders.map((order: any) => {
1475
+ const processed = {
1476
+ id: order.uuid,
1477
+ drugName: order.drug?.display || order.drugNonCoded || 'Unknown Drug',
1478
+ dosage: `${order.dose || ''} ${order.doseUnits?.display || ''}`.trim(),
1479
+ route: order.route?.display || '',
1480
+ frequency: order.frequency?.display || '',
1481
+ date: order.dateActivated ? new Date(order.dateActivated).toLocaleDateString() : '',
1482
+ orderNumber: order.orderNumber,
1483
+ display: order.display,
1484
+ quantity: order.quantity,
1485
+ duration: order.duration,
1486
+ durationUnits: order.durationUnits?.display,
1487
+ asNeeded: order.asNeeded,
1488
+ instructions: order.instructions,
1489
+ orderer: order.orderer?.display,
1490
+ };
1491
+ return processed;
1492
+ });
1493
+
1494
+ return processedOrders;
1495
+ }, [data]);
1496
+
1497
+ return {
1498
+ drugOrders,
1499
+ isLoading,
1500
+ error,
1501
+ mutate,
1502
+ };
1503
+ }