@ampath/esm-dha-workflow-app 4.0.0-next.35 → 4.0.0-next.355

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 (793) hide show
  1. package/README.md +19 -0
  2. package/dist/1149.js +1 -0
  3. package/dist/1149.js.map +1 -0
  4. package/dist/1171.js +1 -0
  5. package/dist/1171.js.map +1 -0
  6. package/dist/1197.js +1 -0
  7. package/dist/1239.js +1 -0
  8. package/dist/1239.js.map +1 -0
  9. package/dist/1277.js +1 -0
  10. package/dist/1277.js.map +1 -0
  11. package/dist/1363.js +1 -0
  12. package/dist/1363.js.map +1 -0
  13. package/dist/1551.js +1 -0
  14. package/dist/1551.js.map +1 -0
  15. package/dist/1556.js +1 -0
  16. package/dist/1556.js.map +1 -0
  17. package/dist/1560.js +1 -0
  18. package/dist/1560.js.map +1 -0
  19. package/dist/1925.js +1 -0
  20. package/dist/1925.js.map +1 -0
  21. package/dist/1927.js +1 -0
  22. package/dist/1927.js.map +1 -0
  23. package/dist/2054.js +1 -0
  24. package/dist/2054.js.map +1 -0
  25. package/dist/2093.js +1 -0
  26. package/dist/2093.js.map +1 -0
  27. package/dist/2177.js +2 -0
  28. package/dist/2177.js.map +1 -0
  29. package/dist/2230.js +1 -0
  30. package/dist/2230.js.map +1 -0
  31. package/dist/2493.js +1 -0
  32. package/dist/2493.js.map +1 -0
  33. package/dist/3041.js +1 -0
  34. package/dist/3041.js.map +1 -0
  35. package/dist/3099.js +1 -0
  36. package/dist/3101.js +1 -0
  37. package/dist/3101.js.map +1 -0
  38. package/dist/313.js +1 -0
  39. package/dist/313.js.map +1 -0
  40. package/dist/3184.js +2 -0
  41. package/dist/3184.js.map +1 -0
  42. package/dist/3202.js +1 -0
  43. package/dist/3202.js.map +1 -0
  44. package/dist/3222.js +1 -0
  45. package/dist/3222.js.map +1 -0
  46. package/dist/327.js +1 -0
  47. package/dist/327.js.map +1 -0
  48. package/dist/347.js +1 -0
  49. package/dist/347.js.map +1 -0
  50. package/dist/3595.js +1 -0
  51. package/dist/3595.js.map +1 -0
  52. package/dist/3928.js +1 -0
  53. package/dist/3928.js.map +1 -0
  54. package/dist/3939.js +1 -0
  55. package/dist/3939.js.map +1 -0
  56. package/dist/4003.js +1 -0
  57. package/dist/4003.js.map +1 -0
  58. package/dist/4055.js +1 -0
  59. package/dist/4205.js +1 -0
  60. package/dist/4205.js.map +1 -0
  61. package/dist/4225.js +1 -0
  62. package/dist/4225.js.map +1 -0
  63. package/dist/4231.js +1 -0
  64. package/dist/4231.js.map +1 -0
  65. package/dist/4300.js +1 -0
  66. package/dist/4335.js +1 -0
  67. package/dist/4353.js +1 -0
  68. package/dist/4353.js.map +1 -0
  69. package/dist/4431.js +1 -0
  70. package/dist/4431.js.map +1 -0
  71. package/dist/4454.js +1 -0
  72. package/dist/4454.js.map +1 -0
  73. package/dist/4457.js +2 -0
  74. package/dist/4457.js.LICENSE.txt +282 -0
  75. package/dist/4457.js.map +1 -0
  76. package/dist/4605.js +1 -0
  77. package/dist/4605.js.map +1 -0
  78. package/dist/4611.js +1 -0
  79. package/dist/4611.js.map +1 -0
  80. package/dist/4652.js +1 -0
  81. package/dist/4674.js +1 -0
  82. package/dist/4674.js.map +1 -0
  83. package/dist/4755.js +1 -0
  84. package/dist/4755.js.map +1 -0
  85. package/dist/4856.js +1 -0
  86. package/dist/4856.js.map +1 -0
  87. package/dist/4986.js +1 -0
  88. package/dist/4986.js.map +1 -0
  89. package/dist/5119.js +1 -0
  90. package/dist/5119.js.map +1 -0
  91. package/dist/5167.js +1 -0
  92. package/dist/5167.js.map +1 -0
  93. package/dist/5317.js +1 -0
  94. package/dist/5317.js.map +1 -0
  95. package/dist/5344.js +1 -0
  96. package/dist/5344.js.map +1 -0
  97. package/dist/5380.js +1 -0
  98. package/dist/5380.js.map +1 -0
  99. package/dist/5422.js +1 -0
  100. package/dist/5422.js.map +1 -0
  101. package/dist/5576.js +1 -0
  102. package/dist/5576.js.map +1 -0
  103. package/dist/5724.js +1 -0
  104. package/dist/5724.js.map +1 -0
  105. package/dist/5851.js +1 -0
  106. package/dist/5851.js.map +1 -0
  107. package/dist/5961.js +1 -0
  108. package/dist/5961.js.map +1 -0
  109. package/dist/6177.js +1 -0
  110. package/dist/6177.js.map +1 -0
  111. package/dist/6264.js +1 -0
  112. package/dist/6264.js.map +1 -0
  113. package/dist/6540.js +2 -0
  114. package/dist/6540.js.map +1 -0
  115. package/dist/6606.js +1 -0
  116. package/dist/6606.js.map +1 -0
  117. package/dist/6720.js +1 -0
  118. package/dist/6720.js.map +1 -0
  119. package/dist/6824.js +1 -0
  120. package/dist/6824.js.map +1 -0
  121. package/dist/6942.js +1 -0
  122. package/dist/6942.js.map +1 -0
  123. package/dist/71.js +1 -0
  124. package/dist/71.js.map +1 -0
  125. package/dist/7207.js +1 -0
  126. package/dist/7207.js.map +1 -0
  127. package/dist/7255.js +1 -0
  128. package/dist/7255.js.map +1 -0
  129. package/dist/7420.js +1 -0
  130. package/dist/7420.js.map +1 -0
  131. package/dist/7521.js +1 -0
  132. package/dist/7521.js.map +1 -0
  133. package/dist/7549.js +1 -0
  134. package/dist/7549.js.map +1 -0
  135. package/dist/7557.js +1 -0
  136. package/dist/7557.js.map +1 -0
  137. package/dist/7717.js +1 -0
  138. package/dist/7717.js.map +1 -0
  139. package/dist/7830.js +2 -0
  140. package/dist/7830.js.map +1 -0
  141. package/dist/7857.js +1 -0
  142. package/dist/7857.js.map +1 -0
  143. package/dist/7941.js +1 -0
  144. package/dist/7941.js.map +1 -0
  145. package/dist/8091.js +1 -0
  146. package/dist/8091.js.map +1 -0
  147. package/dist/810.js +1 -0
  148. package/dist/810.js.map +1 -0
  149. package/dist/8146.js +1 -0
  150. package/dist/8146.js.map +1 -0
  151. package/dist/850.js +1 -0
  152. package/dist/850.js.map +1 -0
  153. package/dist/8558.js +1 -0
  154. package/dist/8558.js.map +1 -0
  155. package/dist/8684.js +1 -0
  156. package/dist/8684.js.map +1 -0
  157. package/dist/8895.js +1 -0
  158. package/dist/8895.js.map +1 -0
  159. package/dist/9070.js +1 -0
  160. package/dist/9070.js.map +1 -0
  161. package/dist/9091.js +1 -0
  162. package/dist/9091.js.map +1 -0
  163. package/dist/9093.js +1 -0
  164. package/dist/9093.js.map +1 -0
  165. package/dist/9338.js +1 -0
  166. package/dist/9338.js.map +1 -0
  167. package/dist/9349.js +1 -0
  168. package/dist/9349.js.map +1 -0
  169. package/dist/9418.js +2 -0
  170. package/dist/9418.js.LICENSE.txt +1 -0
  171. package/dist/9418.js.map +1 -0
  172. package/dist/9428.js +1 -0
  173. package/dist/9428.js.map +1 -0
  174. package/dist/9583.js +2 -0
  175. package/dist/9583.js.map +1 -0
  176. package/dist/9871.js +1 -0
  177. package/dist/9871.js.map +1 -0
  178. package/dist/esm-dha-workflow-app.js +1 -1
  179. package/dist/esm-dha-workflow-app.js.buildmanifest.json +1830 -221
  180. package/dist/esm-dha-workflow-app.js.map +1 -1
  181. package/dist/main.js +1 -1
  182. package/dist/main.js.map +1 -1
  183. package/dist/routes.json +1 -1
  184. package/local-config.json +9 -0
  185. package/package.json +16 -6
  186. package/src/admissions/admission-request-list/admission-request-list.scss +0 -0
  187. package/src/admissions/admission-request-list/admission-request-list.tsx +263 -0
  188. package/src/admissions/admissions-dashboard.component.scss +15 -0
  189. package/src/admissions/admissions-dashboard.component.tsx +277 -0
  190. package/src/admissions/admissions.resource.ts +233 -0
  191. package/src/admissions/admitted-list/admitted-patients-list.tsx +206 -0
  192. package/src/admissions/awaiting-discharge-list/awaiting-discharge-list.tsx +197 -0
  193. package/src/admissions/bed-assigment-request/bed-assignment-request-list.tsx +134 -0
  194. package/src/admissions/constants/index.ts +33 -0
  195. package/src/admissions/discharged-list/discharged-list.scss +0 -0
  196. package/src/admissions/discharged-list/discharged-list.tsx +67 -0
  197. package/src/admissions/form-entry-workspace/form-entry-workspace.tsx +62 -0
  198. package/src/admissions/inpatient/admission-history/patient-admission-history.tsx +76 -0
  199. package/src/admissions/inpatient/admission-requests/patient-admission-requests.scss +19 -0
  200. package/src/admissions/inpatient/admission-requests/patient-admission-requests.tsx +72 -0
  201. package/src/admissions/inpatient/inpatient-admissions.component.scss +7 -0
  202. package/src/admissions/inpatient/inpatient-admissions.component.tsx +46 -0
  203. package/src/admissions/modal/add-bill-items/add-bill-items.modal.scss +17 -0
  204. package/src/admissions/modal/add-bill-items/add-bill-items.modal.tsx +233 -0
  205. package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.scss +23 -0
  206. package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.tsx +156 -0
  207. package/src/admissions/modal/admit-patient/admit-patient.modal.scss +0 -0
  208. package/src/admissions/modal/admit-patient/admit-patient.modal.tsx +111 -0
  209. package/src/admissions/modal/bed-swap/bed-swap.modal.scss +0 -0
  210. package/src/admissions/modal/bed-swap/bed-swap.modal.tsx +102 -0
  211. package/src/admissions/modal/cancel-admission-request/cancel-admission-request.scss +11 -0
  212. package/src/admissions/modal/cancel-admission-request/cancel-admission-request.tsx +105 -0
  213. package/src/admissions/modal/discharge/discharge-patient.modal.scss +13 -0
  214. package/src/admissions/modal/discharge/discharge-patient.modal.tsx +86 -0
  215. package/src/admissions/types/index.ts +263 -0
  216. package/src/appointments/appointments.component.scss +6 -0
  217. package/src/appointments/appointments.component.tsx +8 -1
  218. package/src/billing/api/billing.api.ts +139 -0
  219. package/src/billing/billing-claims.resource.ts +916 -0
  220. package/src/billing/billing-dashboard-link.component.tsx +31 -0
  221. package/src/billing/billing-dashboard-reset.ts +27 -0
  222. package/src/billing/billing-root.tsx +42 -0
  223. package/src/billing/dashboard/billingDashboard.component.scss +0 -0
  224. package/src/billing/dashboard/billingDashboard.component.tsx +25 -0
  225. package/src/billing/dashboard/v2/active-visits/active-visits.component.tsx +257 -0
  226. package/src/billing/dashboard/v2/active-visits/active-visits.resource.ts +85 -0
  227. package/src/billing/dashboard/v2/active-visits/active-visits.scss +15 -0
  228. package/src/billing/dashboard/v2/active-visits.component.tsx +102 -0
  229. package/src/billing/dashboard/v2/admissions/admission-requests-tab.component.tsx +79 -0
  230. package/src/billing/dashboard/v2/billing-claims-dashboard.component.scss +209 -0
  231. package/src/billing/dashboard/v2/billing-claims-dashboard.component.tsx +396 -0
  232. package/src/billing/dashboard/v2/cash-checklist/bill-document.component.scss +90 -0
  233. package/src/billing/dashboard/v2/cash-checklist/bill-document.component.tsx +101 -0
  234. package/src/billing/dashboard/v2/cash-checklist/cash-checklist.component.scss +199 -0
  235. package/src/billing/dashboard/v2/cash-checklist/cash-checklist.component.tsx +361 -0
  236. package/src/billing/dashboard/v2/cash-checklist/cash-checklist.resource.ts +239 -0
  237. package/src/billing/dashboard/v2/cash-checklist/payment-drawer.component.scss +246 -0
  238. package/src/billing/dashboard/v2/cash-checklist/payment-drawer.component.tsx +182 -0
  239. package/src/billing/dashboard/v2/claim-statuses.ts +143 -0
  240. package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.scss +0 -0
  241. package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.tsx +35 -0
  242. package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-panel.component.scss +67 -0
  243. package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-panel.component.tsx +90 -0
  244. package/src/billing/dashboard/v2/claim-visits/claim-doctors/claim-doctors.scss +28 -0
  245. package/src/billing/dashboard/v2/claim-visits/claim-doctors/claim-doctors.tsx +36 -0
  246. package/src/billing/dashboard/v2/claim-visits/claim-history/claim-history.component.scss +119 -0
  247. package/src/billing/dashboard/v2/claim-visits/claim-history/claim-history.component.tsx +198 -0
  248. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-details.component.scss +5 -0
  249. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +160 -0
  250. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-panel.component.scss +105 -0
  251. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-panel.component.tsx +184 -0
  252. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/intervention-attachments.component.scss +161 -0
  253. package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/intervention-attachments.component.tsx +328 -0
  254. package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-details.component.tsx +96 -0
  255. package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-document.component.tsx +236 -0
  256. package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-panel.component.scss +98 -0
  257. package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-panel.component.tsx +292 -0
  258. package/src/billing/dashboard/v2/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.scss +133 -0
  259. package/src/billing/dashboard/v2/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.tsx +217 -0
  260. package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-by-token.component.tsx +150 -0
  261. package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-page.component.scss +13 -0
  262. package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-page.component.tsx +116 -0
  263. package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-visit-details.component.scss +338 -0
  264. package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-visit-details.component.tsx +616 -0
  265. package/src/billing/dashboard/v2/claim-visits/claim-visits.component.scss +5 -0
  266. package/src/billing/dashboard/v2/claim-visits/claim-visits.component.tsx +133 -0
  267. package/src/billing/dashboard/v2/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.scss +5 -0
  268. package/src/billing/dashboard/v2/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.tsx +49 -0
  269. package/src/billing/dashboard/v2/claim-visits/modal/claim-visit-details/claim-visit-details.modal.scss +17 -0
  270. package/src/billing/dashboard/v2/claim-visits/modal/claim-visit-details/claim-visit-details.modal.tsx +56 -0
  271. package/src/billing/dashboard/v2/claim-visits/modal/close-claim/close-claim.modal.scss +0 -0
  272. package/src/billing/dashboard/v2/claim-visits/modal/close-claim/close-claim.modal.tsx +129 -0
  273. package/src/billing/dashboard/v2/claim-visits/modal/submit-claim/submit-claim.modal.tsx +204 -0
  274. package/src/billing/dashboard/v2/claim-visits/shared/_empty-state.scss +28 -0
  275. package/src/billing/dashboard/v2/claim-visits/shared/record-cards.component.scss +298 -0
  276. package/src/billing/dashboard/v2/claim-visits/shared/record-cards.component.tsx +252 -0
  277. package/src/billing/dashboard/v2/claim-visits/shared/record-details.workspace.scss +42 -0
  278. package/src/billing/dashboard/v2/claim-visits/shared/record-details.workspace.tsx +54 -0
  279. package/src/billing/dashboard/v2/claim-visits/shared/record-table.component.scss +61 -0
  280. package/src/billing/dashboard/v2/claim-visits/shared/record-table.component.tsx +150 -0
  281. package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.resource.ts +51 -0
  282. package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.workspace.scss +191 -0
  283. package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.workspace.tsx +648 -0
  284. package/src/billing/dashboard/v2/claim-workspace/claim-workspace.component.scss +595 -0
  285. package/src/billing/dashboard/v2/claim-workspace/claim-workspace.component.tsx +810 -0
  286. package/src/billing/dashboard/v2/claim-workspace/claim-workspace.resource.ts +282 -0
  287. package/src/billing/dashboard/v2/claims-accounting/bill-utils.ts +134 -0
  288. package/src/billing/dashboard/v2/claims-accounting/claim-detail.component.tsx +346 -0
  289. package/src/billing/dashboard/v2/claims-accounting/claims-accounting.component.scss +546 -0
  290. package/src/billing/dashboard/v2/claims-accounting/claims-accounting.component.tsx +206 -0
  291. package/src/billing/dashboard/v2/claims-accounting/claims-accounting.resource.ts +337 -0
  292. package/src/billing/dashboard/v2/claims-accounting/invoice-document.component.scss +90 -0
  293. package/src/billing/dashboard/v2/claims-accounting/invoice-document.component.tsx +90 -0
  294. package/src/billing/dashboard/v2/claims-accounting/remittances.component.tsx +169 -0
  295. package/src/billing/dashboard/v2/claims-accounting/remittances.resource.ts +46 -0
  296. package/src/billing/dashboard/v2/claims-accounting/status-meta.ts +25 -0
  297. package/src/billing/dashboard/v2/clearance/cash-patients.scss +18 -0
  298. package/src/billing/dashboard/v2/clearance/cash.component.tsx +145 -0
  299. package/src/billing/dashboard/v2/clearance/clearance.component.scss +84 -0
  300. package/src/billing/dashboard/v2/clearance/clearance.component.tsx +225 -0
  301. package/src/billing/dashboard/v2/clearance/pay-cash.component.tsx +202 -0
  302. package/src/billing/dashboard/v2/clearance/pay-cash.scss +28 -0
  303. package/src/billing/dashboard/v2/clearance/prepaid-services.component.tsx +112 -0
  304. package/src/billing/dashboard/v2/facility-bills/claim-status.ts +249 -0
  305. package/src/billing/dashboard/v2/facility-bills/facility-bills.component.scss +140 -0
  306. package/src/billing/dashboard/v2/facility-bills/facility-bills.component.tsx +688 -0
  307. package/src/billing/dashboard/v2/facility-bills/preauths-datatables/pending-preauths.component.tsx +132 -0
  308. package/src/billing/dashboard/v2/facility-bills/preauths-datatables/preauth-preview-table.component.tsx +267 -0
  309. package/src/billing/dashboard/v2/facility-bills/preauths.component.tsx +130 -0
  310. package/src/billing/dashboard/v2/inpatient-requests/inpatient-requests.component.scss +22 -0
  311. package/src/billing/dashboard/v2/inpatient-requests/inpatient-requests.component.tsx +389 -0
  312. package/src/billing/dashboard/v2/inpatient-requests/modal/start-inpatient-claim/start-inpatient-claim.component.scss +0 -0
  313. package/src/billing/dashboard/v2/inpatient-requests/modal/start-inpatient-claim/start-inpatient-claim.component.tsx +0 -0
  314. package/src/billing/dashboard/v2/new-facility-bills/bills-table.component.tsx +78 -0
  315. package/src/billing/dashboard/v2/new-facility-bills/bills-table.scss +18 -0
  316. package/src/billing/dashboard/v2/new-facility-bills/new-facility-bills.component.tsx +64 -0
  317. package/src/billing/dashboard/v2/new-facility-bills/new-facility-bills.scss +146 -0
  318. package/src/billing/dashboard/v2/patient-bill-details/attachments/add-attachments.component.tsx +223 -0
  319. package/src/billing/dashboard/v2/patient-bill-details/attachments/attachment.component.tsx +157 -0
  320. package/src/billing/dashboard/v2/patient-bill-details/attachments/attachments.scss +92 -0
  321. package/src/billing/dashboard/v2/patient-bill-details/attachments/case-summary/case-summary.scss +295 -0
  322. package/src/billing/dashboard/v2/patient-bill-details/attachments/case-summary/case-summary.tsx +326 -0
  323. package/src/billing/dashboard/v2/patient-bill-details/attachments/dialysis/dialysis-chart.scss +238 -0
  324. package/src/billing/dashboard/v2/patient-bill-details/attachments/dialysis/dialysis-chart.tsx +269 -0
  325. package/src/billing/dashboard/v2/patient-bill-details/attachments/discharge-summary/discharge-summary.scss +96 -0
  326. package/src/billing/dashboard/v2/patient-bill-details/attachments/discharge-summary/discharge-summary.tsx +124 -0
  327. package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.component.tsx +152 -0
  328. package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.scss +225 -0
  329. package/src/billing/dashboard/v2/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.scss +182 -0
  330. package/src/billing/dashboard/v2/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.tsx +173 -0
  331. package/src/billing/dashboard/v2/patient-bill-details/attachments/generate-attachments.component.tsx +371 -0
  332. package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.component.tsx +151 -0
  333. package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.scss +225 -0
  334. package/src/billing/dashboard/v2/patient-bill-details/attachments/type.ts +44 -0
  335. package/src/billing/dashboard/v2/patient-bill-details/bill-details/bill-details.scss +107 -0
  336. package/src/billing/dashboard/v2/patient-bill-details/bill-details/bill-details.tsx +508 -0
  337. package/src/billing/dashboard/v2/patient-bill-details/bill-details-page.component.scss +30 -0
  338. package/src/billing/dashboard/v2/patient-bill-details/bill-details-page.component.tsx +94 -0
  339. package/src/billing/dashboard/v2/patient-bill-details/claim-details/claim-details-skeleton.component.scss +89 -0
  340. package/src/billing/dashboard/v2/patient-bill-details/claim-details/claim-details-skeleton.component.tsx +53 -0
  341. package/src/billing/dashboard/v2/patient-bill-details/claim-details/patient-claim-details.component.scss +32 -0
  342. package/src/billing/dashboard/v2/patient-bill-details/claim-details/patient-claim-details.component.tsx +215 -0
  343. package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.scss +0 -0
  344. package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.tsx +99 -0
  345. package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-line/add-claim-line.modal.tsx +187 -0
  346. package/src/billing/dashboard/v2/patient-bill-details/patient-bill-details.scss +153 -0
  347. package/src/billing/dashboard/v2/patient-bill-details/patient-bill-details.tsx +458 -0
  348. package/src/billing/dashboard/v2/patient-bill-details/payment-mode.ts +124 -0
  349. package/src/billing/dashboard/v2/patient-bill-details/workspaces/add-claim-line/add-claim-line.workspace.scss +27 -0
  350. package/src/billing/dashboard/v2/patient-bill-details/workspaces/add-claim-line/add-claim-line.workspace.tsx +223 -0
  351. package/src/billing/dashboard/v2/patient-bill-details/workspaces/bill-item-payment/bill-item-payment.workspace.scss +27 -0
  352. package/src/billing/dashboard/v2/patient-bill-details/workspaces/bill-item-payment/bill-item-payment.workspace.tsx +109 -0
  353. package/src/billing/dashboard/v2/preauth/preauth-attachments.component.tsx +389 -0
  354. package/src/billing/dashboard/v2/preauth/preauth-list.component.scss +38 -0
  355. package/src/billing/dashboard/v2/preauth/preauth-list.component.tsx +241 -0
  356. package/src/billing/dashboard/v2/preauth/preauth-status-tag.component.tsx +75 -0
  357. package/src/billing/dashboard/v2/preauth/preauth-typed-fields.md +125 -0
  358. package/src/billing/dashboard/v2/preauth/preauth.resource.ts +1067 -0
  359. package/src/billing/dashboard/v2/preauth/preauth.workspace.scss +91 -0
  360. package/src/billing/dashboard/v2/preauth/preauth.workspace.tsx +1802 -0
  361. package/src/billing/dashboard/v2/preauth/preauthorizations-tab.component.tsx +49 -0
  362. package/src/billing/dashboard/v2/sha-claims/sha-claims-table.component.tsx +36 -0
  363. package/src/billing/dashboard/v2/sha-claims/sha-claims.component.tsx +60 -0
  364. package/src/billing/dashboard/v2/sha-claims/sha-claims.scss +8 -0
  365. package/src/billing/dashboard/v2/shared/dash-board-card/dash-board-card.component.scss +17 -0
  366. package/src/billing/dashboard/v2/shared/dash-board-card/dash-board-card.component.tsx +15 -0
  367. package/src/billing/dashboard/v2/shared/empty-state.component.scss +24 -0
  368. package/src/billing/dashboard/v2/shared/empty-state.component.tsx +13 -0
  369. package/src/billing/dashboard/v2/shared/scroll-to-top.component.scss +54 -0
  370. package/src/billing/dashboard/v2/shared/scroll-to-top.component.tsx +54 -0
  371. package/src/billing/dashboard/v2/shared/table-toolbar.component.scss +77 -0
  372. package/src/billing/dashboard/v2/shared/table-toolbar.component.tsx +48 -0
  373. package/src/billing/dashboard/v2/types/index.ts +514 -0
  374. package/src/billing/dashboard/v3/active-visits/active-visits.component.tsx +256 -0
  375. package/src/billing/dashboard/v3/active-visits/active-visits.resource.ts +57 -0
  376. package/src/billing/dashboard/v3/active-visits/active-visits.scss +15 -0
  377. package/src/billing/dashboard/v3/cash-checklist/bill-document.component.scss +90 -0
  378. package/src/billing/dashboard/v3/cash-checklist/bill-document.component.tsx +101 -0
  379. package/src/billing/dashboard/v3/cash-checklist/cash-checklist.component.scss +199 -0
  380. package/src/billing/dashboard/v3/cash-checklist/cash-checklist.component.tsx +361 -0
  381. package/src/billing/dashboard/v3/cash-checklist/cash-checklist.resource.ts +240 -0
  382. package/src/billing/dashboard/v3/cash-checklist/payment-drawer.component.scss +246 -0
  383. package/src/billing/dashboard/v3/cash-checklist/payment-drawer.component.tsx +182 -0
  384. package/src/billing/dashboard/v3/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.scss +0 -0
  385. package/src/billing/dashboard/v3/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.tsx +44 -0
  386. package/src/billing/dashboard/v3/claim-visits/claim-doctors/claim-doctors.scss +5 -0
  387. package/src/billing/dashboard/v3/claim-visits/claim-doctors/claim-doctors.tsx +42 -0
  388. package/src/billing/dashboard/v3/claim-visits/claim-documents/claim-documents.scss +10 -0
  389. package/src/billing/dashboard/v3/claim-visits/claim-documents/claim-documents.tsx +25 -0
  390. package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +220 -0
  391. package/src/billing/dashboard/v3/claim-visits/claim-invoice-details/claim-invoice-details.component.scss +6 -0
  392. package/src/billing/dashboard/v3/claim-visits/claim-invoice-details/claim-invoice-details.component.tsx +85 -0
  393. package/src/billing/dashboard/v3/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.scss +0 -0
  394. package/src/billing/dashboard/v3/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.tsx +109 -0
  395. package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.scss +97 -0
  396. package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.tsx +343 -0
  397. package/src/billing/dashboard/v3/claim-visits/claim-visits.component.scss +5 -0
  398. package/src/billing/dashboard/v3/claim-visits/claim-visits.component.tsx +139 -0
  399. package/src/billing/dashboard/v3/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.scss +5 -0
  400. package/src/billing/dashboard/v3/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.tsx +49 -0
  401. package/src/billing/dashboard/v3/claim-visits/modal/claim-invoice-lines/claim-invoice-lines.modal.scss +7 -0
  402. package/src/billing/dashboard/v3/claim-visits/modal/claim-invoice-lines/claim-invoice-lines.modal.tsx +35 -0
  403. package/src/billing/dashboard/v3/claim-visits/modal/claim-visit-details/claim-visit-details.modal.scss +6 -0
  404. package/src/billing/dashboard/v3/claim-visits/modal/claim-visit-details/claim-visit-details.modal.tsx +33 -0
  405. package/src/billing/dashboard/v3/claim-visits/modal/close-claim/close-claim.modal.scss +0 -0
  406. package/src/billing/dashboard/v3/claim-visits/modal/close-claim/close-claim.modal.tsx +129 -0
  407. package/src/billing/dashboard/v3/claim-visits/modal/submit-claim/submit-claim.modal.tsx +204 -0
  408. package/src/billing/dashboard/v3/claim-visits/payer-preview/index.scss +140 -0
  409. package/src/billing/dashboard/v3/claim-visits/payer-preview/payer-preview-tile.component.tsx +55 -0
  410. package/src/billing/dashboard/v3/claim-visits/payer-preview/payer-preview-workspace.component.tsx +157 -0
  411. package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-doctors.component.tsx +54 -0
  412. package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-lines.component.tsx +48 -0
  413. package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-notes.component.tsx +47 -0
  414. package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-diagnoses.component.tsx +44 -0
  415. package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-interventions.component.tsx +44 -0
  416. package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.resource.ts +51 -0
  417. package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.workspace.scss +191 -0
  418. package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.workspace.tsx +648 -0
  419. package/src/billing/dashboard/v3/claim-workspace/claim-workspace.component.scss +595 -0
  420. package/src/billing/dashboard/v3/claim-workspace/claim-workspace.component.tsx +810 -0
  421. package/src/billing/dashboard/v3/claim-workspace/claim-workspace.resource.ts +282 -0
  422. package/src/billing/dashboard/v3/claims-accounting/bill-utils.ts +134 -0
  423. package/src/billing/dashboard/v3/claims-accounting/claim-detail.component.tsx +346 -0
  424. package/src/billing/dashboard/v3/claims-accounting/claims-accounting.component.scss +546 -0
  425. package/src/billing/dashboard/v3/claims-accounting/claims-accounting.component.tsx +206 -0
  426. package/src/billing/dashboard/v3/claims-accounting/claims-accounting.resource.ts +337 -0
  427. package/src/billing/dashboard/v3/claims-accounting/invoice-document.component.scss +90 -0
  428. package/src/billing/dashboard/v3/claims-accounting/invoice-document.component.tsx +90 -0
  429. package/src/billing/dashboard/v3/claims-accounting/remittances.component.tsx +169 -0
  430. package/src/billing/dashboard/v3/claims-accounting/remittances.resource.ts +46 -0
  431. package/src/billing/dashboard/v3/claims-accounting/status-meta.ts +25 -0
  432. package/src/billing/dashboard/v3/clearance/clearance.component.scss +100 -0
  433. package/src/billing/dashboard/v3/clearance/clearance.component.tsx +0 -0
  434. package/src/billing/dashboard/v3/clearance/prepaid-services.component.tsx +112 -0
  435. package/src/billing/dashboard/v3/facility-bills/claim-status.ts +243 -0
  436. package/src/billing/dashboard/v3/facility-bills/facility-bills.component.scss +72 -0
  437. package/src/billing/dashboard/v3/facility-bills/facility-bills.component.tsx +181 -0
  438. package/src/billing/dashboard/v3/facility-bills/preauths-datatables/pending-preauths.component.tsx +132 -0
  439. package/src/billing/dashboard/v3/facility-bills/preauths-datatables/preauth-preview-table.component.tsx +232 -0
  440. package/src/billing/dashboard/v3/facility-bills/preauths.component.tsx +127 -0
  441. package/src/billing/dashboard/v3/patient-bill-details/attachments/add-attachments.component.tsx +223 -0
  442. package/src/billing/dashboard/v3/patient-bill-details/attachments/attachment.component.tsx +157 -0
  443. package/src/billing/dashboard/v3/patient-bill-details/attachments/attachments.scss +92 -0
  444. package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary-helper.ts +58 -0
  445. package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.scss +295 -0
  446. package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.tsx +287 -0
  447. package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-chart.scss +238 -0
  448. package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-chart.tsx +317 -0
  449. package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-helper.ts +94 -0
  450. package/src/billing/dashboard/v3/patient-bill-details/attachments/discharge-summary/discharge-summary.scss +96 -0
  451. package/src/billing/dashboard/v3/patient-bill-details/attachments/discharge-summary/discharge-summary.tsx +126 -0
  452. package/src/billing/dashboard/v3/patient-bill-details/attachments/final-bill.component.tsx +75 -0
  453. package/src/billing/dashboard/v3/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.scss +182 -0
  454. package/src/billing/dashboard/v3/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.tsx +253 -0
  455. package/src/billing/dashboard/v3/patient-bill-details/attachments/generate-attachments.component.tsx +345 -0
  456. package/src/billing/dashboard/v3/patient-bill-details/attachments/invoice.component.tsx +150 -0
  457. package/src/billing/dashboard/v3/patient-bill-details/attachments/invoice.scss +225 -0
  458. package/src/billing/dashboard/v3/patient-bill-details/attachments/type.ts +259 -0
  459. package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.scss +17 -0
  460. package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.tsx +260 -0
  461. package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.scss +0 -0
  462. package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.tsx +68 -0
  463. package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.scss +0 -0
  464. package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.tsx +99 -0
  465. package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-line/add-claim-line.modal.scss +14 -0
  466. package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-line/add-claim-line.modal.tsx +112 -0
  467. package/src/billing/dashboard/v3/patient-bill-details/modals/bill-item-payment/bill-item-payment.modal.scss +6 -0
  468. package/src/billing/dashboard/v3/patient-bill-details/modals/bill-item-payment/bill-item-payment.modal.tsx +139 -0
  469. package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.scss +85 -0
  470. package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.tsx +343 -0
  471. package/src/billing/dashboard/v3/patient-bill-details/visit-diagnosis-details/visit-diagnosis-details.component.scss +0 -0
  472. package/src/billing/dashboard/v3/patient-bill-details/visit-diagnosis-details/visit-diagnosis-details.component.tsx +92 -0
  473. package/src/billing/dashboard/v3/preauth/elective/elective-authorize.resource.ts +284 -0
  474. package/src/billing/dashboard/v3/preauth/elective/elective-preauth-request.workspace.scss +125 -0
  475. package/src/billing/dashboard/v3/preauth/elective/elective-preauth-request.workspace.tsx +1184 -0
  476. package/src/billing/dashboard/v3/preauth/elective/elective-preauth.component.scss +15 -0
  477. package/src/billing/dashboard/v3/preauth/elective/elective-preauth.component.tsx +377 -0
  478. package/src/billing/dashboard/v3/preauth/elective/elective-preauth.resource.ts +1378 -0
  479. package/src/billing/dashboard/v3/preauth/elective/elective-requests-list.component.tsx +357 -0
  480. package/src/billing/dashboard/v3/preauth/preauth-attachments.component.tsx +389 -0
  481. package/src/billing/dashboard/v3/preauth/preauth-list.component.scss +34 -0
  482. package/src/billing/dashboard/v3/preauth/preauth-list.component.tsx +300 -0
  483. package/src/billing/dashboard/v3/preauth/preauth-status-tag.component.tsx +75 -0
  484. package/src/billing/dashboard/v3/preauth/preauth-typed-fields.md +125 -0
  485. package/src/billing/dashboard/v3/preauth/preauth.resource.ts +1197 -0
  486. package/src/billing/dashboard/v3/preauth/preauth.workspace.scss +176 -0
  487. package/src/billing/dashboard/v3/preauth/preauth.workspace.tsx +2266 -0
  488. package/src/billing/dashboard/v3/preauth/preauthorizations-tab.component.tsx +55 -0
  489. package/src/billing/dashboard/v3/shared/empty-state.component.scss +25 -0
  490. package/src/billing/dashboard/v3/shared/empty-state.component.tsx +13 -0
  491. package/src/billing/dashboard/v3/shared/table-toolbar.component.scss +64 -0
  492. package/src/billing/dashboard/v3/shared/table-toolbar.component.tsx +48 -0
  493. package/src/billing/dashboard/v3/types/index.ts +384 -0
  494. package/src/billing/extensions/generate-order-bill-button/generate-order-bill-button.extension.tsx +55 -0
  495. package/src/billing/extensions/generate-order-bill-button/generate-order-bill-button.scss +16 -0
  496. package/src/billing/extensions/visit-billing/visit-billing.extension.scss +0 -0
  497. package/src/billing/extensions/visit-billing/visit-billing.extension.tsx +509 -0
  498. package/src/billing/invoice/bill.resource.ts +47 -0
  499. package/src/billing/invoice/invoice-header/header-card/header-card.scss +17 -0
  500. package/src/billing/invoice/invoice-header/header-card/header-card.tsx +21 -0
  501. package/src/billing/invoice/invoice.scss +113 -0
  502. package/src/billing/invoice/invoice.tsx +1441 -0
  503. package/src/billing/invoice/line-items/line-items.scss +0 -0
  504. package/src/billing/invoice/line-items/line-items.tsx +120 -0
  505. package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.scss +23 -0
  506. package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.tsx +114 -0
  507. package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.scss +0 -0
  508. package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.tsx +122 -0
  509. package/src/billing/invoice/payment-details/payment-details.scss +0 -0
  510. package/src/billing/invoice/payment-details/payment-details.tsx +43 -0
  511. package/src/billing/invoice/print-invoice/print-receipt.component.tsx +44 -0
  512. package/src/billing/invoice/print-invoice/print-receipt.scss +14 -0
  513. package/src/billing/maternity-discharge.resource.ts +21 -0
  514. package/src/billing/types/index.ts +354 -0
  515. package/src/billing/utils/index.ts +17 -0
  516. package/src/billing/widgets/billingTotalsRow.component.scss +0 -0
  517. package/src/billing/widgets/billingTotalsRow.component.tsx +712 -0
  518. package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.resource.ts +130 -0
  519. package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.scss +58 -0
  520. package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.workspace.tsx +656 -0
  521. package/src/billing/workspaces/create-order-bill-form-workspace/schema.ts +12 -0
  522. package/src/bookings/bookings.component.scss +7 -0
  523. package/src/bookings/bookings.component.tsx +35 -0
  524. package/src/bookings/daily/daily-bookings.resource.ts +2 -1
  525. package/src/case-summary/case-summary.extension.scss +13 -0
  526. package/src/case-summary/case-summary.extension.tsx +40 -0
  527. package/src/case-summary/case-summary.resource.test.ts +69 -0
  528. package/src/case-summary/case-summary.resource.ts +52 -0
  529. package/src/case-summary/components/case-summary-printable.component.scss +174 -0
  530. package/src/case-summary/components/case-summary-printable.component.tsx +288 -0
  531. package/src/case-summary/components/case-summary-printable.utils.ts +51 -0
  532. package/src/case-summary/types/case-summary.types.ts +152 -0
  533. package/src/claims/claims.component.scss +70 -0
  534. package/src/claims/claims.component.tsx +743 -0
  535. package/src/claims/claims.resource.ts +1840 -0
  536. package/src/claims/index.ts +365 -0
  537. package/src/claims/interventions.resource.ts +131 -0
  538. package/src/claims/utils.ts +64 -0
  539. package/src/config-schema.ts +149 -1
  540. package/src/context/patient-context.tsx +27 -0
  541. package/src/createDashboardLink.tsx +9 -0
  542. package/src/dashboard/charts/chart.component.scss +24 -0
  543. package/src/dashboard/charts/chart.component.tsx +56 -0
  544. package/src/dashboard/dashboard.component.scss +48 -3
  545. package/src/dashboard/dashboard.component.tsx +29 -38
  546. package/src/dashboard/overview/overview.component.scss +40 -59
  547. package/src/dashboard/overview/overview.component.tsx +55 -41
  548. package/src/dashboard-meta/accounting-dashboard.meta.ts +6 -0
  549. package/src/dashboard-meta/admissions-dashboard.meta.ts +6 -0
  550. package/src/dashboard-meta/billing-dashboard.meta.ts +6 -0
  551. package/src/dashboard-meta/bookings-dashboard.meta.ts +6 -0
  552. package/src/dashboard-meta/case-summary.meta.ts +6 -0
  553. package/src/dashboard-meta/consultation-dashboard.meta.ts +6 -0
  554. package/src/dashboard-meta/dha-workflow-dashboard.meta.ts +6 -0
  555. package/src/dashboard-meta/elective-preauth.meta.ts +6 -0
  556. package/src/dashboard-meta/emt-dashboard.meta.ts +6 -0
  557. package/src/dashboard-meta/inpatient-admissions.meta.ts +6 -0
  558. package/src/dashboard-meta/maternity-dashboard.meta.ts +6 -0
  559. package/src/dashboard-meta/mch-dashboard.meta.ts +6 -0
  560. package/src/dashboard-meta/pharmacy-dashboard.meta.ts +6 -0
  561. package/src/dashboard-meta/queue-dashboard.meta.ts +6 -0
  562. package/src/dashboard-meta/registers-dashboard.meta.ts +6 -0
  563. package/src/dashboard-meta/registry-dashboard.meta.ts +6 -0
  564. package/src/dashboard-meta/service-queue-admin.meta.ts +6 -0
  565. package/src/dashboard-meta/specialized-clinics-dashboard.meta.ts +6 -0
  566. package/src/dashboard-meta/triage-dashboard.meta.ts +6 -0
  567. package/src/emt/cr-lookup.resource.test.ts +88 -0
  568. package/src/emt/cr-lookup.resource.ts +42 -0
  569. package/src/emt/emt-queue.component.test.tsx +353 -0
  570. package/src/emt/emt-queue.component.tsx +463 -0
  571. package/src/emt/emt-queue.scss +85 -0
  572. package/src/emt/emt.resource.test.ts +247 -0
  573. package/src/emt/emt.resource.ts +150 -0
  574. package/src/emt/handover-modal/handover-modal.component.test.tsx +273 -0
  575. package/src/emt/handover-modal/handover-modal.component.tsx +541 -0
  576. package/src/emt/handover-modal/handover-modal.scss +113 -0
  577. package/src/emt/types/emt.types.ts +137 -0
  578. package/src/index.ts +204 -6
  579. package/src/mnch/maternity/maternity-queues.component.scss +7 -0
  580. package/src/mnch/maternity/maternity-queues.component.tsx +32 -0
  581. package/src/mnch/maternity/queues/triage/maternity-triage.tsx +15 -0
  582. package/src/mnch/mch/mch-queues.component.scss +10 -0
  583. package/src/mnch/mch/mch-queues.component.tsx +36 -0
  584. package/src/mnch/mch/queues/consultation/mch-consultation.tsx +18 -0
  585. package/src/mnch/mch/queues/triage/mch-triage.tsx +15 -0
  586. package/src/mnch/mnch-root.tsx +17 -0
  587. package/src/mortuary/mortuary.component.tsx +13 -0
  588. package/src/pharmacy/pharmacy.component.scss +16 -0
  589. package/src/pharmacy/pharmacy.component.tsx +11 -6
  590. package/src/registers/registers.component.tsx +8 -0
  591. package/src/registry/drawer/emr-compare.component.scss +263 -0
  592. package/src/registry/drawer/emr-compare.component.tsx +302 -0
  593. package/src/registry/drawer/emr-compare.resource.ts +181 -0
  594. package/src/registry/drawer/emr-create-preview.component.tsx +123 -0
  595. package/src/registry/drawer/otp-verification-step.component.tsx +398 -0
  596. package/src/registry/drawer/pomsf-balance.util.test.ts +177 -0
  597. package/src/registry/drawer/pomsf-balance.util.ts +79 -0
  598. package/src/registry/drawer/verification.resource.ts +60 -0
  599. package/src/registry/drawer/workflow-drawer.component.scss +1242 -0
  600. package/src/registry/drawer/workflow-drawer.component.tsx +1478 -0
  601. package/src/registry/eligibility/eligibility-details/eligibility-details.scss +38 -0
  602. package/src/registry/eligibility/eligibility-details/eligibility-details.tsx +101 -0
  603. package/src/registry/eligibility/eliigibility-tags/eligibility-tags.scss +16 -0
  604. package/src/registry/eligibility/eliigibility-tags/eligibility-tags.tsx +120 -0
  605. package/src/registry/eligibility/modal/eligibility-details.modal.scss +0 -0
  606. package/src/registry/eligibility/modal/eligibility-details.modal.tsx +45 -0
  607. package/src/registry/hie.resource.ts +466 -0
  608. package/src/registry/hie.types.ts +63 -0
  609. package/src/registry/modal/biometrics-verification-modal/biometrics-verification-modal.scss +277 -0
  610. package/src/registry/modal/biometrics-verification-modal/biometrics-verification-modal.tsx +218 -0
  611. package/src/registry/modal/biometrics-verification-modal/type.ts +39 -0
  612. package/src/registry/modal/client-details-modal/client-details-modal.tsx +65 -29
  613. package/src/registry/modal/otp-modal/otp-whitlisting-modal.component.tsx +809 -0
  614. package/src/registry/modal/otp-modal/otp-whitlisting-modal.scss +354 -0
  615. package/src/registry/modal/otp-modal/otp-whitlisting.scss +0 -0
  616. package/src/registry/modal/otp-verification-modal/claims-consent.scss +72 -0
  617. package/src/registry/modal/otp-verification-modal/claims-consent.tsx +135 -0
  618. package/src/registry/modal/otp-verification-modal/extension/claims-consent.extension.tsx +130 -0
  619. package/src/registry/modal/otp-verification-modal/otp-verification-modal.scss +30 -0
  620. package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +154 -54
  621. package/src/registry/modal/send-to-triage/payment-details.tsx +120 -0
  622. package/src/registry/modal/send-to-triage/payment-method.component.tsx +54 -0
  623. package/src/registry/modal/send-to-triage/send-to-queue.modal.tsx +981 -0
  624. package/src/registry/modal/send-to-triage/send-to-triage.modal.scss +156 -3
  625. package/src/registry/modal/send-to-triage/send-to-triage.modal.tsx +1094 -135
  626. package/src/registry/modal/start-patient-visit/start-patient-visit.modal.scss +34 -0
  627. package/src/registry/modal/start-patient-visit/start-patient-visit.modal.tsx +231 -0
  628. package/src/registry/payment-details/payment-options/payment-options.scss +13 -0
  629. package/src/registry/payment-details/payment-options/payment-options.tsx +61 -14
  630. package/src/registry/registration-list/registration-list.component.scss +32 -0
  631. package/src/registry/registration-list/registration-list.component.tsx +103 -0
  632. package/src/registry/registry-entry.tsx +11 -0
  633. package/src/registry/registry.component.scss +402 -78
  634. package/src/registry/registry.component.tsx +602 -222
  635. package/src/registry/registry.resource.ts +22 -3
  636. package/src/registry/types/index.ts +118 -9
  637. package/src/registry/utils/error-handler.ts +54 -0
  638. package/src/registry/utils/format-dependant-display-data.ts +1 -1
  639. package/src/registry/utils/hie-client-adapter.ts +7 -4
  640. package/src/registry/utils/mask-data.ts +40 -0
  641. package/src/registry/utils/phone-number-formatter.ts +11 -0
  642. package/src/resources/dashboard.resource.ts +20 -0
  643. package/src/resources/identifier-types.ts +2 -0
  644. package/src/resources/patient-resource.ts +6 -1
  645. package/src/resources/queue.resource.ts +24 -1
  646. package/src/resources/superset.resource.ts +21 -0
  647. package/src/resources/visit.resource.ts +19 -1
  648. package/src/root.component.tsx +10 -5
  649. package/src/routes.json +477 -23
  650. package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.scss +31 -0
  651. package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.tsx +153 -0
  652. package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.scss +0 -0
  653. package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.tsx +182 -0
  654. package/src/service-queues/admin/queue/queue-list/queue-list.scss +33 -0
  655. package/src/service-queues/admin/queue/queue-list/queue-list.tsx +113 -0
  656. package/src/service-queues/admin/queue-entry/queue-entry-list.component.scss +6 -0
  657. package/src/service-queues/admin/queue-entry/queue-entry-list.component.tsx +137 -0
  658. package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.scss +31 -0
  659. package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.tsx +137 -0
  660. package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.scss +0 -0
  661. package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.tsx +146 -0
  662. package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.scss +22 -0
  663. package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.tsx +120 -0
  664. package/src/service-queues/admin/service-queue-admin-dashboard.component.scss +6 -0
  665. package/src/service-queues/admin/service-queue-admin-dashboard.component.tsx +34 -0
  666. package/src/service-queues/consultation/consultation.component.tsx +9 -0
  667. package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.scss +30 -0
  668. package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.tsx +108 -0
  669. package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.scss +26 -0
  670. package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.tsx +110 -0
  671. package/src/service-queues/modals/move/move-patient.component.scss +1 -1
  672. package/src/service-queues/modals/move/move-patient.component.tsx +31 -17
  673. package/src/service-queues/queue-list/check-in.service.ts +26 -0
  674. package/src/service-queues/queue-list/queue-list.component.scss +37 -0
  675. package/src/service-queues/queue-list/queue-list.component.tsx +256 -56
  676. package/src/service-queues/service-queue/service-queue.component.tsx +151 -8
  677. package/src/service-queues/service-queue/stats/stat-details/stat-details.component.scss +5 -0
  678. package/src/service-queues/service-queue/stats/stat-details/stat-details.component.tsx +36 -7
  679. package/src/service-queues/service-queue.tsx +22 -16
  680. package/src/service-queues/service-queues.resource.ts +197 -1
  681. package/src/service-queues/service.resource.ts +1 -1
  682. package/src/shared/constants/concepts.ts +50 -0
  683. package/src/shared/constants/patient-category.ts +6 -0
  684. package/src/shared/constants/patient-type.ts +10 -0
  685. package/src/shared/constants/visit-types.ts +7 -0
  686. package/src/shared/services/billing.resource.ts +75 -0
  687. package/src/shared/services/claims.resource.ts +47 -0
  688. package/src/shared/services/client-payment-mode.resource.ts +27 -0
  689. package/src/shared/services/client-payment.resource.ts +17 -0
  690. package/src/shared/services/consultation-clearance.resource.ts +210 -0
  691. package/src/shared/services/eligibility.resource.ts +20 -0
  692. package/src/shared/services/encounters.resource.ts +33 -0
  693. package/src/shared/services/location.resource.ts +9 -0
  694. package/src/shared/types/index.ts +156 -0
  695. package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.scss +42 -0
  696. package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.tsx +42 -0
  697. package/src/shared/ui/confirm-modal/confirm.modal.scss +18 -0
  698. package/src/shared/ui/confirm-modal/confirm.modal.tsx +38 -0
  699. package/src/shared/ui/facility-worker-slot/facility-worker.component-slot.component.scss +39 -0
  700. package/src/shared/ui/facility-worker-slot/facility-worker.component-slot.component.tsx +19 -0
  701. package/src/shared/ui/otp-input/otp-input.component.scss +14 -0
  702. package/src/shared/ui/otp-input/otp-input.component.tsx +97 -0
  703. package/src/{service-queues/service-queue/stats → shared/ui}/stat-card/stat-card.component.tsx +6 -3
  704. package/src/shared/ui/timer/timer.component.scss +5 -0
  705. package/src/shared/ui/timer/timer.component.tsx +40 -0
  706. package/src/shared/utils/get-base-url.ts +15 -0
  707. package/src/shared/utils/get-tag-type.ts +75 -0
  708. package/src/side-nav-menu/mnch-nav-link-config.ts +16 -0
  709. package/src/side-nav-menu/mnch-nav-links.tsx +42 -0
  710. package/src/side-nav-menu/nav-link-config.ts +5 -5
  711. package/src/side-nav-menu/specialized-clinics-nav-link-config.ts +36 -0
  712. package/src/side-nav-menu/specialized-clinics-nav-links.tsx +42 -0
  713. package/src/specialized-clinics/critital-care-unit/critical-care-unit-queues.component.scss +7 -0
  714. package/src/specialized-clinics/critital-care-unit/critical-care-unit-queues.component.tsx +36 -0
  715. package/src/specialized-clinics/critital-care-unit/queues/consultation/critical-care-unit-consultation.tsx +15 -0
  716. package/src/specialized-clinics/critital-care-unit/queues/triage/critical-care-unit-triage.tsx +15 -0
  717. package/src/specialized-clinics/dental/dental-queues.component.scss +7 -0
  718. package/src/specialized-clinics/dental/dental-queues.component.tsx +36 -0
  719. package/src/specialized-clinics/dental/queues/consultation/dental-consultation.tsx +15 -0
  720. package/src/specialized-clinics/dental/queues/triage/dental-triage.tsx +15 -0
  721. package/src/specialized-clinics/diagnostic-and-imaging/diagnostic-and-imaging.component.scss +7 -0
  722. package/src/specialized-clinics/diagnostic-and-imaging/diagnostic-and-imaging.component.tsx +36 -0
  723. package/src/specialized-clinics/diagnostic-and-imaging/queues/consultation/diagnosis-and-imaging-consultation.tsx +15 -0
  724. package/src/specialized-clinics/diagnostic-and-imaging/queues/triage/diagnosis-and-imaging-triage.tsx +15 -0
  725. package/src/specialized-clinics/oncology/oncology-queues.component.scss +7 -0
  726. package/src/specialized-clinics/oncology/oncology-queues.component.tsx +36 -0
  727. package/src/specialized-clinics/oncology/queues/consultation/oncology-consultation.tsx +15 -0
  728. package/src/specialized-clinics/oncology/queues/triage/oncology-triage.tsx +15 -0
  729. package/src/specialized-clinics/ophthalmology/ophthalmology-queues.component.scss +7 -0
  730. package/src/specialized-clinics/ophthalmology/ophthalmology-queues.component.tsx +36 -0
  731. package/src/specialized-clinics/ophthalmology/queues/consultation/ophthalmology-consultation.tsx +15 -0
  732. package/src/specialized-clinics/ophthalmology/queues/triage/ophthalmology-triage.tsx +15 -0
  733. package/src/specialized-clinics/psychiatry/psychiatry-queues.component.scss +7 -0
  734. package/src/specialized-clinics/psychiatry/psychiatry-queues.component.tsx +36 -0
  735. package/src/specialized-clinics/psychiatry/queues/consultation/psychiatry-consultation.tsx +15 -0
  736. package/src/specialized-clinics/psychiatry/queues/triage/psychiatry-triage.tsx +15 -0
  737. package/src/specialized-clinics/renal/queues/consultation/dialysis-consultation.tsx +15 -0
  738. package/src/specialized-clinics/renal/queues/triage/renal-triage.tsx +15 -0
  739. package/src/specialized-clinics/renal/renal-queues.component.scss +7 -0
  740. package/src/specialized-clinics/renal/renal-queues.component.tsx +36 -0
  741. package/src/specialized-clinics/specialized-clinics-root.tsx +27 -0
  742. package/src/triage/triage.component.scss +6 -0
  743. package/src/triage/triage.component.tsx +4 -0
  744. package/src/types/types.ts +56 -1
  745. package/webpack.config.js +54 -1
  746. package/dist/104.js +0 -2
  747. package/dist/104.js.map +0 -1
  748. package/dist/15.js +0 -1
  749. package/dist/15.js.map +0 -1
  750. package/dist/184.js +0 -2
  751. package/dist/184.js.map +0 -1
  752. package/dist/197.js +0 -1
  753. package/dist/246.js +0 -2
  754. package/dist/246.js.map +0 -1
  755. package/dist/255.js +0 -1
  756. package/dist/255.js.map +0 -1
  757. package/dist/300.js +0 -1
  758. package/dist/335.js +0 -1
  759. package/dist/339.js +0 -1
  760. package/dist/339.js.map +0 -1
  761. package/dist/353.js +0 -1
  762. package/dist/353.js.map +0 -1
  763. package/dist/420.js +0 -1
  764. package/dist/420.js.map +0 -1
  765. package/dist/540.js +0 -2
  766. package/dist/540.js.map +0 -1
  767. package/dist/55.js +0 -1
  768. package/dist/652.js +0 -1
  769. package/dist/709.js +0 -1
  770. package/dist/709.js.map +0 -1
  771. package/dist/710.js +0 -2
  772. package/dist/710.js.map +0 -1
  773. package/dist/729.js +0 -1
  774. package/dist/729.js.map +0 -1
  775. package/dist/752.js +0 -1
  776. package/dist/752.js.map +0 -1
  777. package/dist/833.js +0 -1
  778. package/dist/833.js.map +0 -1
  779. package/dist/907.js +0 -1
  780. package/dist/907.js.map +0 -1
  781. package/dist/91.js +0 -1
  782. package/dist/91.js.map +0 -1
  783. package/dist/93.js +0 -1
  784. package/dist/93.js.map +0 -1
  785. package/dist/943.js +0 -1
  786. package/dist/943.js.map +0 -1
  787. package/dist/99.js +0 -1
  788. /package/dist/{104.js.LICENSE.txt → 2177.js.LICENSE.txt} +0 -0
  789. /package/dist/{184.js.LICENSE.txt → 3184.js.LICENSE.txt} +0 -0
  790. /package/dist/{540.js.LICENSE.txt → 6540.js.LICENSE.txt} +0 -0
  791. /package/dist/{710.js.LICENSE.txt → 7830.js.LICENSE.txt} +0 -0
  792. /package/dist/{246.js.LICENSE.txt → 9583.js.LICENSE.txt} +0 -0
  793. /package/src/{service-queues/service-queue/stats → shared/ui}/stat-card/stat-card.component.scss +0 -0
@@ -0,0 +1,2266 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ Button,
4
+ ButtonSet,
5
+ Checkbox,
6
+ ComboBox,
7
+ DatePicker,
8
+ DatePickerInput,
9
+ Dropdown,
10
+ Form,
11
+ InlineLoading,
12
+ InlineNotification,
13
+ RadioButton,
14
+ RadioButtonGroup,
15
+ Tag,
16
+ TextArea,
17
+ TextInput,
18
+ } from '@carbon/react';
19
+ import { showSnackbar, useConfig, useSession, type DefaultWorkspaceProps } from '@openmrs/esm-framework';
20
+ import { useTranslation } from 'react-i18next';
21
+ import dayjs from 'dayjs';
22
+ import {
23
+ cancelPreauth,
24
+ createPreauth,
25
+ extractHieErrorMessage,
26
+ fetchShaInterventionByCode,
27
+ findExistingPreauthForCrAndShaCode,
28
+ getServiceType,
29
+ pollPreauthUntilSubmitted,
30
+ preauthAttachmentFieldName,
31
+ type ExistingPreauthMatch,
32
+ type PreauthFormPayload,
33
+ } from '../../../../claims/claims.resource';
34
+ import { resolvePreVisitConsentAuthorization, sendClaimsOTP } from '../../../../registry/hie.resource';
35
+ import type { PatientContactResult } from '../../../../registry/hie.types';
36
+ import {
37
+ createElectivePreauth,
38
+ extractElectiveAuthorizationToken,
39
+ fetchElectivePatientContacts,
40
+ resolveOrCreateElectivePreVisitAuthorization,
41
+ sendElectivePreauthOtp,
42
+ } from './elective/elective-authorize.resource';
43
+ import { addClaimDiagnosis, fetchPatientDiagnosesForBilling, preferredDiagnosisForPreauth } from '../../../billing-claims.resource';
44
+ import { ensureInterventionOnVisit } from '../../../../claims/interventions.resource';
45
+ import type { ConfigObject } from '../../../../config-schema';
46
+ import { type AmrsVisitDiagnosis } from '../../../types';
47
+ import { type PatientFacilityBillDetails } from '../types';
48
+ import PreauthAttachments, { type PreauthAttachmentRow } from './preauth-attachments.component';
49
+ import {
50
+ billingDateToVisitDate,
51
+ dateToServiceIso,
52
+ fetchPreauthFormValues,
53
+ mergeSpecialtyFlags,
54
+ preauthFormLabel,
55
+ readSpecialtyFlags,
56
+ resolveUnitPriceFromPatientBills,
57
+ searchDiagnosisConcepts,
58
+ searchHealthWorkerRegistry,
59
+ searchOpenMrsProviders,
60
+ storePreauthCode,
61
+ usePreauthBillableServices,
62
+ type DiagnosisConceptHit,
63
+ type HwrSearchResult,
64
+ type OpenMrsProviderHit,
65
+ type PreauthFormFieldKey,
66
+ type PreauthInterventionProps,
67
+ } from './preauth.resource';
68
+ import styles from './preauth.workspace.scss';
69
+
70
+ export type { PreauthInterventionProps };
71
+
72
+ type ServicePrice = {
73
+ uuid?: string;
74
+ name?: string;
75
+ price?: number;
76
+ paymentMode?: { uuid?: string; name?: string };
77
+ };
78
+
79
+ type BillableServiceOption = {
80
+ uuid: string;
81
+ name: string;
82
+ servicePrices?: ServicePrice[];
83
+ };
84
+
85
+ function unitPriceFromBillableService(
86
+ item: BillableServiceOption | null | undefined,
87
+ shaPaymentModeUuid: string,
88
+ ): string {
89
+ const prices = item?.servicePrices ?? [];
90
+ if (!prices.length) return '';
91
+ const sha = prices.find((sp) => sp.paymentMode?.uuid === shaPaymentModeUuid);
92
+ const pick = sha ?? prices[0];
93
+ if (pick?.price == null || Number.isNaN(Number(pick.price))) return '';
94
+ return String(pick.price);
95
+ }
96
+
97
+ /** ComboBox row: visit diagnosis (ETL) or concept-dictionary hit. */
98
+ type DiagnosisPick =
99
+ | { kind: 'visit'; key: string; dx: AmrsVisitDiagnosis }
100
+ | { kind: 'concept'; key: string; hit: DiagnosisConceptHit };
101
+
102
+ const visitDxPick = (dx: AmrsVisitDiagnosis): DiagnosisPick => ({
103
+ kind: 'visit',
104
+ key: `visit-${dx.encounter_id}-${dx.icd11_code}-${dx.diagnosis_coded ?? dx.value_coded ?? ''}`,
105
+ dx,
106
+ });
107
+
108
+ const conceptDxPick = (hit: DiagnosisConceptHit): DiagnosisPick => ({
109
+ kind: 'concept',
110
+ key: `concept-${hit.uuid}-${hit.icd11Code}`,
111
+ hit,
112
+ });
113
+
114
+ const diagnosisPickLabel = (item: DiagnosisPick | null) => {
115
+ if (!item) return '';
116
+ if (item.kind === 'visit') {
117
+ const d = item.dx;
118
+ const rank =
119
+ d.dx_rank != null && Number.isFinite(Number(d.dx_rank)) ? ` · Rank ${Number(d.dx_rank)}` : '';
120
+ return `${d.icd11_code ?? '—'} · ${d.concept_source_name || d.encounter_type || 'Visit diagnosis'}${rank}`;
121
+ }
122
+ return `${item.hit.icd11Code} · ${item.hit.display}`;
123
+ };
124
+
125
+ const diagnosisPickCode = (item: DiagnosisPick | null) => {
126
+ if (!item) return '';
127
+ return item.kind === 'visit' ? item.dx.icd11_code ?? '' : item.hit.icd11Code;
128
+ };
129
+
130
+ const DEFAULT_DOCTOR_ID_TYPE = 'National ID' as const;
131
+
132
+ type FormFieldLoadStatus = 'idle' | 'loading' | 'from_form' | 'missing';
133
+
134
+ const FORM_MISSING_HINT =
135
+ 'Missing on the Pre-authorization form. Complete the form on the patient chart, then reopen Raise preauth.';
136
+
137
+ const SURGICAL_FORM_KEYS: PreauthFormFieldKey[] = [
138
+ 'chiefComplaint',
139
+ 'hpi',
140
+ 'physicalExam',
141
+ 'investigations',
142
+ 'anaesthesia',
143
+ 'surgeryDate',
144
+ 'relatedToEmployment',
145
+ 'relatedToAccident',
146
+ 'isCoInsured',
147
+ 'coInsuranceDetails',
148
+ ];
149
+
150
+ const RENAL_FORM_KEYS: PreauthFormFieldKey[] = [
151
+ 'clinicalIndications',
152
+ 'startDate',
153
+ 'sessionsRequired',
154
+ 'frequency',
155
+ 'isCoInsured',
156
+ 'coInsuranceDetails',
157
+ ];
158
+
159
+ interface PreauthWorkspaceProps extends DefaultWorkspaceProps {
160
+ consentToken: string;
161
+ patientUuid?: string;
162
+ locationUuid: string;
163
+ /** Elective (pre-visit) mode — uses authorize token + expected_service_start_date */
164
+ isElective?: boolean;
165
+ billItem: Partial<PatientFacilityBillDetails> & {
166
+ intervention_code: string;
167
+ patient_uuid?: string;
168
+ cr_no?: string;
169
+ service_type?: string;
170
+ };
171
+ intervention: PreauthInterventionProps;
172
+ onSuccess?: (result: { consentToken: string; preauthCode?: string; status: string }) => void;
173
+ }
174
+
175
+ const ANAESTHESIA = ['GENERAL', 'LOCAL', 'SPINAL', 'SEDATION'];
176
+ const FREQUENCY = ['TWICE_A_WEEK', 'ONCE_A_WEEK', 'ONCE_EVERY_2_WEEKS', 'ONCE_EVERY_3_WEEKS', 'ONCE_A_MONTH'];
177
+ const STAGING = ['STAGE_1', 'STAGE_2', 'STAGE_3', 'STAGE_4'];
178
+ const METASTASES = ['LUNG', 'BRAIN', 'LIVER', 'OTHER'];
179
+ const TREATMENT = ['DAY_WARD', 'RECLINING_CHAIR', 'SIDE_ROOM'];
180
+ const LENS = ['FRAMES_LENSES', 'FRAMED', 'CONTACT'];
181
+ const NEW_OR_REPL = ['NEW', 'REPLACEMENT'];
182
+
183
+ const DOC_TYPE_OPTIONS = [
184
+ 'LAB_TESTS',
185
+ 'LAB_RESULTS',
186
+ 'DISCHARGE_SUMMARY',
187
+ 'INVOICE',
188
+ 'FINAL_BILL',
189
+ 'CLINICAL_DOCUMENTATION',
190
+ 'MEDICAL_REPORT',
191
+ 'IMAGING_RESULT',
192
+ 'RADIOLOGICAL_EXAM',
193
+ 'STAGING_RESULTS',
194
+ 'TREATMENT_PLAN',
195
+ 'THEATRE_LIST',
196
+ 'DIALYSIS_CHART',
197
+ 'PREAUTH_FORM',
198
+ 'OTHER',
199
+ ];
200
+
201
+ const toIsoLocal = (d: Date = new Date()) => dayjs(d).format('YYYY-MM-DDTHH:mm:ssZ');
202
+
203
+ /** HIE regulation bodies (abbreviated): COC = Clinical Officers Council, NCK = Nursing Council of Kenya */
204
+ const REGULATION_BODIES = ['KMPDC', 'COC', 'NCK'] as const;
205
+ type RegulationBody = (typeof REGULATION_BODIES)[number];
206
+
207
+ const normalizeRegulationBody = (value?: string | null): RegulationBody => {
208
+ if (!value) return 'KMPDC';
209
+ const raw = value.trim();
210
+ const exact = REGULATION_BODIES.find((b) => b.toLowerCase() === raw.toLowerCase());
211
+ if (exact) return exact;
212
+ const upper = raw.toUpperCase();
213
+ if (upper.includes('KMPDC') || upper.includes('MEDICAL PRACTITIONER') || upper.includes('DOCTOR')) {
214
+ return 'KMPDC';
215
+ }
216
+ if (
217
+ upper.includes('CLINICAL OFFICER') ||
218
+ upper === 'COC' ||
219
+ upper.includes('CLINICAL OFFICERS COUNCIL')
220
+ ) {
221
+ return 'COC';
222
+ }
223
+ if (upper.includes('NURS') || upper === 'NCK' || upper.includes('NURSING COUNCIL')) {
224
+ return 'NCK';
225
+ }
226
+ return 'KMPDC';
227
+ };
228
+
229
+ const PreauthForm: React.FC<PreauthWorkspaceProps> = ({
230
+ closeWorkspace,
231
+ promptBeforeClosing,
232
+ consentToken: consentTokenProp,
233
+ patientUuid,
234
+ locationUuid,
235
+ isElective = false,
236
+ billItem,
237
+ intervention,
238
+ onSuccess,
239
+ }) => {
240
+ const { t } = useTranslation();
241
+ const session = useSession();
242
+ const { shaPaymentModeUuid } = useConfig<ConfigObject>();
243
+ const { lineItems: billableItems, isLoading: loadingBillableItems } = usePreauthBillableServices();
244
+ const [dirty, setDirty] = useState(false);
245
+ const [submitting, setSubmitting] = useState(false);
246
+ const [polling, setPolling] = useState(false);
247
+ const [consentMethod, setConsentMethod] = useState<'biometric' | 'otp'>('otp');
248
+ // Elective must mint a pre-visit authorization token (AUTHORIZED_PENDING_VISIT) —
249
+ // do not treat a prior visit claim token as ready consent.
250
+ const [consentDone, setConsentDone] = useState(isElective ? false : Boolean(consentTokenProp));
251
+ const [otpSent, setOtpSent] = useState(false);
252
+ const [otp, setOtp] = useState('');
253
+ const [sendingOtp, setSendingOtp] = useState(false);
254
+ /** Phase 2 step 6a — contact confirmed with patient before OTP. */
255
+ const [patientContacts, setPatientContacts] = useState<PatientContactResult[]>([]);
256
+ const [selectedContactId, setSelectedContactId] = useState('');
257
+ const [loadingContacts, setLoadingContacts] = useState(false);
258
+ const [activeConsentToken, setActiveConsentToken] = useState(isElective ? '' : consentTokenProp || '');
259
+ const [expectedServiceStartDate, setExpectedServiceStartDate] = useState(toIsoLocal());
260
+ const abortRef = useRef<AbortController | null>(null);
261
+ /** Skip unsaved-changes prompt after a successful submit (state may still say submitting/dirty). */
262
+ const allowCloseWithoutPromptRef = useRef(false);
263
+ const [existingPreauth, setExistingPreauth] = useState<ExistingPreauthMatch | null>(null);
264
+ const [checkingExistingPreauth, setCheckingExistingPreauth] = useState(false);
265
+ const [cancellingExistingPreauth, setCancellingExistingPreauth] = useState(false);
266
+
267
+ // Specialty from launch props + bill item; SHA coverage may enrich after mount
268
+ // (visit/claim payloads often omit requires_*_preauth even for oncology codes).
269
+ const [specialty, setSpecialty] = useState(() =>
270
+ mergeSpecialtyFlags(readSpecialtyFlags(intervention), readSpecialtyFlags(billItem)),
271
+ );
272
+ const specialtyLabel = preauthFormLabel(specialty);
273
+ const interventionForSubmit = useMemo(
274
+ () => ({
275
+ ...intervention,
276
+ ...specialty,
277
+ }),
278
+ [intervention, specialty],
279
+ );
280
+
281
+ const [serviceStart, setServiceStart] = useState(toIsoLocal());
282
+ const [serviceEnd, setServiceEnd] = useState(toIsoLocal(dayjs().add(30, 'minute').toDate()));
283
+ const [providerEmail, setProviderEmail] = useState(session?.user?.username?.includes('@') ? session.user.username : '');
284
+ const [unitPrice, setUnitPrice] = useState(() =>
285
+ String(billItem.item_price ?? billItem.item_total_price ?? '').trim(),
286
+ );
287
+ const [billPriceLookupDone, setBillPriceLookupDone] = useState(false);
288
+ const [hasBillUnitPrice, setHasBillUnitPrice] = useState(() =>
289
+ Boolean(String(billItem.item_price ?? billItem.item_total_price ?? '').trim()),
290
+ );
291
+ const [selectedBillableService, setSelectedBillableService] = useState<BillableServiceOption | null>(null);
292
+
293
+ // Diagnosis: prefill from ETL encounter-diagnosis; search uses concept dictionary (ICD-11).
294
+ const [visitDiagnoses, setVisitDiagnoses] = useState<AmrsVisitDiagnosis[]>([]);
295
+ const [conceptDxHits, setConceptDxHits] = useState<DiagnosisConceptHit[]>([]);
296
+ const [loadingDx, setLoadingDx] = useState(false);
297
+ const [searchingDx, setSearchingDx] = useState(false);
298
+ const [selectedDx, setSelectedDx] = useState<DiagnosisPick | null>(null);
299
+ const [icdCode, setIcdCode] = useState('');
300
+ const dxSearchTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
301
+
302
+ // Provider — National ID for doctor; HWR for regulation body
303
+ const [providerHits, setProviderHits] = useState<OpenMrsProviderHit[]>([]);
304
+ const [searchingProviders, setSearchingProviders] = useState(false);
305
+ const [selectedProvider, setSelectedProvider] = useState<OpenMrsProviderHit | null>(null);
306
+ const providerSearchTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
307
+ const [searchingHwr, setSearchingHwr] = useState(false);
308
+ const [hwrHit, setHwrHit] = useState<HwrSearchResult | null>(null);
309
+ const [doctorId, setDoctorId] = useState('');
310
+ const [regulationBody, setRegulationBody] = useState<RegulationBody>('KMPDC');
311
+
312
+ // Specialty fields — empty until Pre-authorization form obs (or bill unit price) loads.
313
+ const [clinicalIndications, setClinicalIndications] = useState('');
314
+ const [formLoadState, setFormLoadState] = useState<'idle' | 'loading' | 'done'>('idle');
315
+ const [formFound, setFormFound] = useState<Set<PreauthFormFieldKey>>(() => new Set());
316
+ const [formRelevant, setFormRelevant] = useState<Set<PreauthFormFieldKey>>(() => new Set());
317
+ const [chiefComplaint, setChiefComplaint] = useState('');
318
+ const [vitalSigns, setVitalSigns] = useState('');
319
+ const [hpi, setHpi] = useState('');
320
+ const [physicalExam, setPhysicalExam] = useState('');
321
+ const [investigations, setInvestigations] = useState('');
322
+ const [anaesthesia, setAnaesthesia] = useState('');
323
+ const [surgeryDate, setSurgeryDate] = useState('');
324
+ const [sessionsRequired, setSessionsRequired] = useState('');
325
+ const [costPerSession, setCostPerSession] = useState('');
326
+ const [frequency, setFrequency] = useState('');
327
+ const [startDate, setStartDate] = useState('');
328
+ const [isCoInsured, setIsCoInsured] = useState(false);
329
+ const [necessity, setNecessity] = useState('');
330
+ const [lensPrescription, setLensPrescription] = useState('');
331
+ const [lensAmount, setLensAmount] = useState('');
332
+ const [eyeExamAmount, setEyeExamAmount] = useState('');
333
+ const [frameAmount, setFrameAmount] = useState('');
334
+ const [newOrReplacement, setNewOrReplacement] = useState('');
335
+ const [carcinomaStaging, setCarcinomaStaging] = useState('');
336
+ const [comorbidity, setComorbidity] = useState('');
337
+ const [metastases, setMetastases] = useState('');
338
+ const [treatmentSetting, setTreatmentSetting] = useState('');
339
+ const [progressReport, setProgressReport] = useState('');
340
+ const [relatedToEmployment, setRelatedToEmployment] = useState(false);
341
+ const [relatedToAccident, setRelatedToAccident] = useState(false);
342
+ const [coInsuranceDetails, setCoInsuranceDetails] = useState('');
343
+
344
+ const formFieldStatus = (key: PreauthFormFieldKey): FormFieldLoadStatus => {
345
+ if (formLoadState === 'idle') return 'idle';
346
+ if (formLoadState === 'loading') return 'loading';
347
+ if (!formRelevant.has(key)) return 'idle';
348
+ return formFound.has(key) ? 'from_form' : 'missing';
349
+ };
350
+
351
+ const formFieldLocked = (key: PreauthFormFieldKey) => {
352
+ const s = formFieldStatus(key);
353
+ return s === 'from_form' || s === 'missing' || s === 'loading';
354
+ };
355
+
356
+ const formFieldInvalid = (key: PreauthFormFieldKey) => formFieldStatus(key) === 'missing';
357
+
358
+ const patientName = billItem.patient_name ?? '';
359
+ const crNo = billItem.cr_no ?? '';
360
+ const billableService = billItem.billable_service ?? intervention.name ?? intervention.code;
361
+ const requiredDocs = useMemo(
362
+ () => [...new Set(intervention.requiredPreauthDocumentTypes ?? [])],
363
+ [intervention.requiredPreauthDocumentTypes],
364
+ );
365
+ const optionalDocs = useMemo(() => {
366
+ const required = new Set(requiredDocs);
367
+ return [...new Set(intervention.applicableDocumentTypes ?? [])].filter((d) => !required.has(d));
368
+ }, [intervention.applicableDocumentTypes, requiredDocs]);
369
+
370
+ const [attachments, setAttachments] = useState<PreauthAttachmentRow[]>(() => {
371
+ const required = requiredDocs.map((document_type) => ({
372
+ id: crypto.randomUUID(),
373
+ document_type,
374
+ document_title: document_type.replace(/_/g, ' '),
375
+ required: true,
376
+ }));
377
+ if (required.length === 0) {
378
+ return [
379
+ {
380
+ id: crypto.randomUUID(),
381
+ document_type: 'LAB_TESTS',
382
+ document_title: 'Lab Results',
383
+ required: false,
384
+ },
385
+ ];
386
+ }
387
+ return required;
388
+ });
389
+
390
+ useEffect(() => {
391
+ // Normal Raise needs the claim visit consent token. Elective may open without one
392
+ // and resolve a pre-visit token via GET /claim-authorizations (not /claims-authorize).
393
+ if (!isElective && !consentTokenProp) {
394
+ showSnackbar({
395
+ kind: 'error',
396
+ title: t('missingConsentToken', 'Missing claim token'),
397
+ subtitle: t(
398
+ 'missingConsentTokenDetail',
399
+ 'A preexisting visit claim token is required to raise preauth.',
400
+ ),
401
+ });
402
+ closeWorkspace?.();
403
+ }
404
+ }, [consentTokenProp, isElective, closeWorkspace, t]);
405
+
406
+ // Phase 2 step 6a — GET contacts so OTP can target beneficiary_contact_id.
407
+ useEffect(() => {
408
+ if (!isElective || consentMethod !== 'otp') return;
409
+ const patientId = crNo.trim();
410
+ if (!patientId || !locationUuid) {
411
+ setPatientContacts([]);
412
+ setSelectedContactId('');
413
+ return;
414
+ }
415
+ let cancelled = false;
416
+ setLoadingContacts(true);
417
+ (async () => {
418
+ try {
419
+ const rows = await fetchElectivePatientContacts(patientId, locationUuid);
420
+ if (cancelled) return;
421
+ setPatientContacts(rows);
422
+ const preferred = rows.find((c) => c.isConfirmed) ?? rows[0];
423
+ setSelectedContactId(preferred?.id != null ? String(preferred.id) : '');
424
+ } catch {
425
+ if (!cancelled) {
426
+ setPatientContacts([]);
427
+ setSelectedContactId('');
428
+ }
429
+ } finally {
430
+ if (!cancelled) setLoadingContacts(false);
431
+ }
432
+ })();
433
+ return () => {
434
+ cancelled = true;
435
+ };
436
+ }, [isElective, consentMethod, crNo, locationUuid]);
437
+
438
+ const consentToken = activeConsentToken;
439
+
440
+ // Detect an existing active preauth for the same CR + SHA intervention code.
441
+ useEffect(() => {
442
+ const patientId = (billItem.cr_no ?? '').trim();
443
+ const code = (intervention.code || billItem.intervention_code || '').trim();
444
+ const patientIdUuid = (patientUuid || billItem.patient_uuid || '').trim();
445
+ if (!locationUuid || !code || (!patientId && !consentTokenProp && !patientIdUuid)) {
446
+ setExistingPreauth(null);
447
+ return;
448
+ }
449
+
450
+ let cancelled = false;
451
+ setCheckingExistingPreauth(true);
452
+ (async () => {
453
+ try {
454
+ const match = await findExistingPreauthForCrAndShaCode({
455
+ beneficiaryCr: patientId,
456
+ interventionCode: code,
457
+ locationUuid,
458
+ consentToken: isElective ? undefined : consentTokenProp,
459
+ patientUuid: patientIdUuid || undefined,
460
+ });
461
+ if (!cancelled) setExistingPreauth(match);
462
+ } catch {
463
+ if (!cancelled) setExistingPreauth(null);
464
+ } finally {
465
+ if (!cancelled) setCheckingExistingPreauth(false);
466
+ }
467
+ })();
468
+
469
+ return () => {
470
+ cancelled = true;
471
+ };
472
+ }, [
473
+ billItem.cr_no,
474
+ billItem.intervention_code,
475
+ billItem.patient_uuid,
476
+ consentTokenProp,
477
+ intervention.code,
478
+ isElective,
479
+ locationUuid,
480
+ patientUuid,
481
+ ]);
482
+
483
+ // Enrich specialty flags from SHA interventions coverage when launch props lack them.
484
+ useEffect(() => {
485
+ const patientId = (billItem.cr_no ?? '').trim();
486
+ const code = (intervention.code || billItem.intervention_code || '').trim();
487
+ if (!patientId || !locationUuid || !code) return;
488
+
489
+ let cancelled = false;
490
+ (async () => {
491
+ try {
492
+ const sha = await fetchShaInterventionByCode(patientId, locationUuid, code);
493
+ if (cancelled || !sha) return;
494
+ const fromSha = readSpecialtyFlags(sha);
495
+ setSpecialty((prev) => {
496
+ const next = mergeSpecialtyFlags(prev, fromSha);
497
+ if (
498
+ next.requiresSurgicalPreauth === prev.requiresSurgicalPreauth &&
499
+ next.requiresRenalPreauth === prev.requiresRenalPreauth &&
500
+ next.requiresOncologyPreauth === prev.requiresOncologyPreauth &&
501
+ next.requiresRadiologyPreauth === prev.requiresRadiologyPreauth &&
502
+ next.requiresOpticalPreauth === prev.requiresOpticalPreauth
503
+ ) {
504
+ return prev;
505
+ }
506
+ return next;
507
+ });
508
+ // Do not seed specialty field defaults — obs / bill loaders fill real values.
509
+ } catch {
510
+ // Keep launch-prop flags if coverage lookup fails
511
+ }
512
+ })();
513
+
514
+ return () => {
515
+ cancelled = true;
516
+ };
517
+ }, [billItem.cr_no, billItem.intervention_code, intervention.code, locationUuid]);
518
+
519
+ useEffect(() => {
520
+ promptBeforeClosing?.(() => {
521
+ if (allowCloseWithoutPromptRef.current) return false;
522
+ return dirty || submitting || polling;
523
+ });
524
+ }, [dirty, submitting, polling, promptBeforeClosing]);
525
+
526
+ useEffect(() => () => {
527
+ abortRef.current?.abort();
528
+ if (providerSearchTimer.current) clearTimeout(providerSearchTimer.current);
529
+ if (dxSearchTimer.current) clearTimeout(dxSearchTimer.current);
530
+ }, []);
531
+
532
+ const markDirty = () => setDirty(true);
533
+
534
+ const applyDiagnosisPick = (pick: DiagnosisPick | null, opts?: { fromUser?: boolean }) => {
535
+ // Prefill from visit load must not trip "unsaved changes" on close.
536
+ if (opts?.fromUser !== false) {
537
+ markDirty();
538
+ }
539
+ setSelectedDx(pick);
540
+ const code = diagnosisPickCode(pick);
541
+ if (code) {
542
+ setIcdCode(code);
543
+ }
544
+ if (pick?.kind === 'visit') {
545
+ const dx = pick.dx;
546
+ if (dx.practioner_nat_id) {
547
+ setDoctorId(dx.practioner_nat_id);
548
+ }
549
+ if (dx.practitioner_body) {
550
+ setRegulationBody(normalizeRegulationBody(dx.practitioner_body));
551
+ }
552
+ }
553
+ };
554
+
555
+ // Prefill from visit + maternity + encounter diagnoses (same three ETL sources as bill details).
556
+ useEffect(() => {
557
+ const uuid = patientUuid || billItem.patient_uuid;
558
+ if (!uuid || !locationUuid) return;
559
+ let cancelled = false;
560
+ const run = async () => {
561
+ setLoadingDx(true);
562
+ try {
563
+ const visitDate = billingDateToVisitDate(billItem.bill_date);
564
+ const billingDate = visitDate;
565
+ const results = await fetchPatientDiagnosesForBilling({
566
+ visitDate,
567
+ billingDate,
568
+ patientUuid: uuid,
569
+ locationUuid,
570
+ });
571
+ if (!cancelled) {
572
+ setVisitDiagnoses(results ?? []);
573
+ // Prefer dx_rank === 1; otherwise best-ranked ICD-coded row. Multiple options
574
+ // stay available in the ComboBox (already ordered by rank from the fetch).
575
+ const preferred = preferredDiagnosisForPreauth(results ?? []);
576
+ if (preferred) {
577
+ applyDiagnosisPick(visitDxPick(preferred), { fromUser: false });
578
+ }
579
+ }
580
+ } catch {
581
+ if (!cancelled) {
582
+ showSnackbar({
583
+ kind: 'warning',
584
+ title: 'Could not load diagnoses',
585
+ subtitle: 'Search the concept dictionary, or retry after the visit has ICD-11 coded diagnoses.',
586
+ });
587
+ }
588
+ } finally {
589
+ if (!cancelled) setLoadingDx(false);
590
+ }
591
+ };
592
+ run();
593
+ return () => {
594
+ cancelled = true;
595
+ };
596
+ // eslint-disable-next-line react-hooks/exhaustive-deps
597
+ }, [patientUuid, billItem.patient_uuid, billItem.bill_date, locationUuid]);
598
+
599
+ const diagnosisItems = useMemo((): DiagnosisPick[] => {
600
+ const base =
601
+ conceptDxHits.length > 0
602
+ ? conceptDxHits.map(conceptDxPick)
603
+ : visitDiagnoses.filter((d) => d.icd11_code).map(visitDxPick);
604
+ if (!selectedDx) return base;
605
+ if (!base.some((i) => i.key === selectedDx.key)) {
606
+ return [selectedDx, ...base];
607
+ }
608
+ return base.map((i) => (i.key === selectedDx.key ? selectedDx : i));
609
+ }, [conceptDxHits, visitDiagnoses, selectedDx]);
610
+
611
+ const handleDiagnosisInputChange = (inputValue: string) => {
612
+ const selectedLabel = diagnosisPickLabel(selectedDx);
613
+ if (inputValue === selectedLabel) {
614
+ return;
615
+ }
616
+ if (selectedDx) {
617
+ setSelectedDx(null);
618
+ }
619
+ if (dxSearchTimer.current) {
620
+ clearTimeout(dxSearchTimer.current);
621
+ }
622
+ const q = (inputValue ?? '').trim();
623
+ if (q.length < 2) {
624
+ setConceptDxHits([]);
625
+ setSearchingDx(false);
626
+ return;
627
+ }
628
+ dxSearchTimer.current = setTimeout(async () => {
629
+ setSearchingDx(true);
630
+ try {
631
+ const hits = await searchDiagnosisConcepts(q);
632
+ setConceptDxHits(hits);
633
+ } catch {
634
+ setConceptDxHits([]);
635
+ } finally {
636
+ setSearchingDx(false);
637
+ }
638
+ }, 300);
639
+ };
640
+ // Resolve unit_price from patient facility bill lines (match intervention + service name).
641
+ // Elective holds often have no bill line yet — fall back to billable-service catalog picker.
642
+ useEffect(() => {
643
+ const uuid = patientUuid || billItem.patient_uuid;
644
+ const code = (intervention.code || billItem.intervention_code || '').trim();
645
+ if (!uuid || !locationUuid || !code) {
646
+ setBillPriceLookupDone(true);
647
+ return;
648
+ }
649
+
650
+ let cancelled = false;
651
+ setBillPriceLookupDone(false);
652
+ (async () => {
653
+ try {
654
+ const { unitPrice: fromBills, billLine } = await resolveUnitPriceFromPatientBills({
655
+ patientUuid: uuid,
656
+ locationUuid,
657
+ billingDate: billItem.bill_date,
658
+ interventionCode: code,
659
+ serviceHint: billItem.billable_service || intervention.name || code,
660
+ });
661
+ if (cancelled) return;
662
+ if (fromBills) {
663
+ setUnitPrice(fromBills);
664
+ setHasBillUnitPrice(true);
665
+ setSelectedBillableService(null);
666
+ setCostPerSession((prev) => (prev.trim() ? prev : fromBills));
667
+ // Prefer matched billable_service label when launch used intervention name only
668
+ if (billLine?.billable_service && !(billItem.billable_service || '').trim()) {
669
+ // billItem is a prop — display uses billableService derived from it; unit price is enough
670
+ }
671
+ } else {
672
+ setHasBillUnitPrice(false);
673
+ }
674
+ } finally {
675
+ if (!cancelled) setBillPriceLookupDone(true);
676
+ }
677
+ })();
678
+
679
+ return () => {
680
+ cancelled = true;
681
+ };
682
+ }, [
683
+ patientUuid,
684
+ billItem.patient_uuid,
685
+ billItem.bill_date,
686
+ billItem.intervention_code,
687
+ billItem.billable_service,
688
+ intervention.code,
689
+ intervention.name,
690
+ locationUuid,
691
+ specialty.requiresRenalPreauth,
692
+ ]);
693
+
694
+ // Elective preauth usually has no patient bill yet — always offer the catalog picker.
695
+ const needsBillableServicePicker = isElective;
696
+
697
+ // Prefill specialty fields from POC Pre-authorization form (encounter, else latest obs).
698
+ useEffect(() => {
699
+ const needsForm =
700
+ specialty.requiresSurgicalPreauth ||
701
+ specialty.requiresRenalPreauth ||
702
+ specialty.requiresRadiologyPreauth ||
703
+ specialty.requiresOpticalPreauth;
704
+ const uuid = patientUuid || billItem.patient_uuid;
705
+ if (!needsForm || !uuid) {
706
+ setFormLoadState('idle');
707
+ setFormFound(new Set());
708
+ setFormRelevant(new Set());
709
+ return;
710
+ }
711
+
712
+ let cancelled = false;
713
+ setFormLoadState('loading');
714
+ (async () => {
715
+ const values = await fetchPreauthFormValues(uuid);
716
+ if (cancelled) return;
717
+
718
+ // Apply every mapped value found on the form — do not gate on specialty flags here.
719
+ // Specialty only controls which keys are required / shown as missing below.
720
+ // (Gating on flags raced with SHA enrichment and skipped renal fields.)
721
+ if (values.found.has('clinicalIndications')) {
722
+ setClinicalIndications(values.clinicalIndications);
723
+ }
724
+ if (values.found.has('startDate')) setStartDate(values.startDate);
725
+ if (values.found.has('sessionsRequired')) setSessionsRequired(values.sessionsRequired);
726
+ if (values.found.has('frequency')) setFrequency(values.frequency);
727
+ if (values.found.has('chiefComplaint')) setChiefComplaint(values.chiefComplaint);
728
+ if (values.found.has('hpi')) setHpi(values.hpi);
729
+ if (values.found.has('physicalExam')) setPhysicalExam(values.physicalExam);
730
+ if (values.found.has('investigations')) setInvestigations(values.investigations);
731
+ if (values.found.has('anaesthesia')) setAnaesthesia(values.anaesthesia);
732
+ if (values.found.has('surgeryDate')) setSurgeryDate(values.surgeryDate);
733
+ if (values.found.has('relatedToEmployment') && values.relatedToEmployment !== null) {
734
+ setRelatedToEmployment(values.relatedToEmployment);
735
+ }
736
+ if (values.found.has('relatedToAccident') && values.relatedToAccident !== null) {
737
+ setRelatedToAccident(values.relatedToAccident);
738
+ }
739
+ if (values.found.has('isCoInsured') && values.isCoInsured !== null) {
740
+ setIsCoInsured(values.isCoInsured);
741
+ }
742
+ if (values.found.has('coInsuranceDetails')) {
743
+ setCoInsuranceDetails(values.coInsuranceDetails);
744
+ }
745
+
746
+ // Always prefer bill line price resolved from facility patient bills (see resolve effect).
747
+ // Do not overwrite with launch billItem.item_price (often keph tariff / wrong line).
748
+
749
+ const relevant = new Set<PreauthFormFieldKey>();
750
+ if (
751
+ specialty.requiresRadiologyPreauth ||
752
+ specialty.requiresOpticalPreauth ||
753
+ specialty.requiresRenalPreauth
754
+ ) {
755
+ relevant.add('clinicalIndications');
756
+ }
757
+ if (specialty.requiresRenalPreauth) {
758
+ RENAL_FORM_KEYS.forEach((k) => relevant.add(k));
759
+ }
760
+ if (specialty.requiresSurgicalPreauth) {
761
+ SURGICAL_FORM_KEYS.forEach((k) => relevant.add(k));
762
+ }
763
+ // coInsuranceDetails only required when patient is co-insured
764
+ if (values.isCoInsured !== true) {
765
+ relevant.delete('coInsuranceDetails');
766
+ }
767
+
768
+ setFormRelevant(relevant);
769
+ setFormFound(new Set([...relevant].filter((k) => values.found.has(k))));
770
+ setFormLoadState('done');
771
+ })();
772
+
773
+ return () => {
774
+ cancelled = true;
775
+ };
776
+ }, [
777
+ patientUuid,
778
+ billItem.patient_uuid,
779
+ specialty.requiresSurgicalPreauth,
780
+ specialty.requiresRenalPreauth,
781
+ specialty.requiresRadiologyPreauth,
782
+ specialty.requiresOpticalPreauth,
783
+ ]);
784
+
785
+ const clinicalIndicationsFieldProps = {
786
+ labelText: 'Clinical indications',
787
+ value: formFieldStatus('clinicalIndications') === 'loading' ? '' : clinicalIndications,
788
+ readOnly: formFieldLocked('clinicalIndications'),
789
+ placeholder: formFieldStatus('clinicalIndications') === 'loading' ? 'Loading clinical notes…' : undefined,
790
+ invalid: formFieldInvalid('clinicalIndications'),
791
+ invalidText: formFieldInvalid('clinicalIndications') ? FORM_MISSING_HINT : undefined,
792
+ onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => {
793
+ if (formFieldLocked('clinicalIndications')) return;
794
+ markDirty();
795
+ setClinicalIndications(e.target.value);
796
+ },
797
+ };
798
+
799
+ const providerItemLabel = (item: OpenMrsProviderHit | null) =>
800
+ item ? (item.nationalId ? `${item.display} · ${item.nationalId}` : item.display) : '';
801
+
802
+ const applyProviderSelection = (hit: OpenMrsProviderHit | null) => {
803
+ setSelectedProvider(hit);
804
+ if (!hit) return;
805
+ markDirty();
806
+ if (hit.nationalId) {
807
+ setDoctorId(hit.nationalId);
808
+ void handleSearchHwr(hit.nationalId);
809
+ } else {
810
+ setDoctorId('');
811
+ setHwrHit(null);
812
+ showSnackbar({
813
+ kind: 'warning',
814
+ title: 'No National ID on provider',
815
+ subtitle: 'Enter the doctor National ID, then Search HWR for the regulation body.',
816
+ });
817
+ }
818
+ };
819
+
820
+ const handleSearchHwr = async (idOverride?: string) => {
821
+ const idValue = (idOverride ?? doctorId).trim();
822
+ if (!idValue) {
823
+ showSnackbar({
824
+ kind: 'error',
825
+ title: 'Enter National ID',
826
+ subtitle: 'Doctor National ID is required to look up the regulation body in HWR.',
827
+ });
828
+ return;
829
+ }
830
+ setSearchingHwr(true);
831
+ try {
832
+ const results = await searchHealthWorkerRegistry({
833
+ identifierType: DEFAULT_DOCTOR_ID_TYPE,
834
+ identifierValue: idValue,
835
+ locationUuid,
836
+ });
837
+ const hit = results[0];
838
+ if (!hit) {
839
+ setHwrHit(null);
840
+ showSnackbar({ kind: 'warning', title: 'No health worker found' });
841
+ return;
842
+ }
843
+ setHwrHit(hit);
844
+ // Keep doctor ID as National ID; only take regulation body (and email) from HWR
845
+ const body = normalizeRegulationBody(hit.membership?.licensing_body);
846
+ setRegulationBody(body);
847
+ if (hit.contacts?.email) {
848
+ setProviderEmail(hit.contacts.email);
849
+ }
850
+ markDirty();
851
+ showSnackbar({
852
+ kind: 'success',
853
+ title: 'Regulation body from HWR',
854
+ subtitle: `${hit.membership?.full_name || 'Match'} · ${body}`,
855
+ });
856
+ } catch (e: any) {
857
+ showSnackbar({ kind: 'error', title: 'HWR search failed', subtitle: String(e?.message ?? e) });
858
+ } finally {
859
+ setSearchingHwr(false);
860
+ }
861
+ };
862
+
863
+ const handleProviderInputChange = (inputValue: string) => {
864
+ const selectedLabel = providerItemLabel(selectedProvider);
865
+ if (inputValue === selectedLabel) {
866
+ return;
867
+ }
868
+ if (selectedProvider) {
869
+ setSelectedProvider(null);
870
+ }
871
+ if (providerSearchTimer.current) {
872
+ clearTimeout(providerSearchTimer.current);
873
+ }
874
+ if (!inputValue || inputValue.trim().length < 2) {
875
+ setProviderHits([]);
876
+ setSearchingProviders(false);
877
+ return;
878
+ }
879
+ providerSearchTimer.current = setTimeout(async () => {
880
+ setSearchingProviders(true);
881
+ try {
882
+ const hits = await searchOpenMrsProviders(inputValue);
883
+ setProviderHits(hits);
884
+ } catch (e: any) {
885
+ setProviderHits([]);
886
+ showSnackbar({ kind: 'error', title: 'Provider search failed', subtitle: String(e?.message ?? e) });
887
+ } finally {
888
+ setSearchingProviders(false);
889
+ }
890
+ }, 300);
891
+ };
892
+
893
+ const requiredAttachmentsReady = useMemo(() => {
894
+ return attachments.filter((a) => a.required).every((a) => Boolean(a.file));
895
+ }, [attachments]);
896
+
897
+ const handleSendOtp = async () => {
898
+ if (!crNo) {
899
+ showSnackbar({ kind: 'error', title: 'Missing CR number', subtitle: 'Cannot send OTP without patient CR id.' });
900
+ return;
901
+ }
902
+ if (isElective && !selectedContactId && patientContacts.length > 0) {
903
+ showSnackbar({
904
+ kind: 'error',
905
+ title: 'Select a contact',
906
+ subtitle: 'Confirm which patient contact should receive the OTP.',
907
+ });
908
+ return;
909
+ }
910
+ setSendingOtp(true);
911
+ try {
912
+ if (isElective) {
913
+ // Phase 2 step 7a — elective-only OTP helper (not normal sendClaimsOTP).
914
+ const response = await sendElectivePreauthOtp({
915
+ patientId: crNo,
916
+ locationUuid,
917
+ interventionCode: intervention.code,
918
+ beneficiaryContactId: selectedContactId || undefined,
919
+ });
920
+ if ((response as any)?.message?.includes('OTP') || response) {
921
+ setOtpSent(true);
922
+ setConsentDone(false);
923
+ showSnackbar({ kind: 'success', title: 'OTP sent' });
924
+ }
925
+ } else {
926
+ // Do not cancelAllPendingAuthorizations — that can wipe in-flight consent for this CR.
927
+ const response = await sendClaimsOTP(crNo, locationUuid, intervention.code);
928
+ if (response?.message?.includes('OTP') || response) {
929
+ setOtpSent(true);
930
+ setConsentDone(false);
931
+ showSnackbar({ kind: 'success', title: 'OTP sent' });
932
+ }
933
+ }
934
+ } catch (e: any) {
935
+ showSnackbar({ kind: 'error', title: 'Failed to send OTP', subtitle: String(e?.message ?? e) });
936
+ } finally {
937
+ setSendingOtp(false);
938
+ }
939
+ };
940
+
941
+ const resolveElectiveServiceType = (): string => {
942
+ const fromBill = String(billItem.service_type ?? '').trim().toUpperCase();
943
+ if (fromBill === 'OUTPATIENT' || fromBill === 'INPATIENT' || fromBill === 'CAPITATION') {
944
+ return fromBill;
945
+ }
946
+ try {
947
+ return getServiceType(intervention as any, 'OUTPATIENT') || 'OUTPATIENT';
948
+ } catch {
949
+ return 'OUTPATIENT';
950
+ }
951
+ };
952
+
953
+ const handleVerifyOtp = async () => {
954
+ if (!otp || otp.length < 4) {
955
+ showSnackbar({ kind: 'error', title: 'Enter OTP' });
956
+ return;
957
+ }
958
+
959
+ // Elective: Confirm OTP locally only. HIE authorize runs on Submit so Send OTP /
960
+ // Confirm never hit /claims-authorize or create a preauth.
961
+ if (isElective) {
962
+ setConsentDone(true);
963
+ showSnackbar({
964
+ kind: 'success',
965
+ title: 'OTP confirmed',
966
+ subtitle: 'Pre-visit authorization will be created when you submit the preauth.',
967
+ });
968
+ return;
969
+ }
970
+
971
+ setSendingOtp(true);
972
+ try {
973
+ const token = String(activeConsentToken || consentTokenProp || '').trim();
974
+ if (!token) {
975
+ throw new Error('A claim consent token is required.');
976
+ }
977
+ setActiveConsentToken(token);
978
+ setConsentDone(true);
979
+ showSnackbar({
980
+ kind: 'success',
981
+ title: 'Consent verified',
982
+ });
983
+ } catch (e: any) {
984
+ showSnackbar({
985
+ kind: 'error',
986
+ title: 'Consent token verification failed',
987
+ subtitle: String(e?.message ?? e),
988
+ });
989
+ } finally {
990
+ setSendingOtp(false);
991
+ }
992
+ };
993
+
994
+ const handleConfirmBiometric = async () => {
995
+ setSendingOtp(true);
996
+ try {
997
+ if (!isElective) {
998
+ const token = String(activeConsentToken || consentTokenProp || '').trim();
999
+ if (!token) {
1000
+ throw new Error('A claim consent token is required.');
1001
+ }
1002
+ setActiveConsentToken(token);
1003
+ setConsentDone(true);
1004
+ showSnackbar({ kind: 'success', title: 'Consent verified' });
1005
+ return;
1006
+ }
1007
+
1008
+ // Elective biometrics: after iframe authorize, resolve AUTHORIZED_PENDING_VISIT only
1009
+ // (do not accept a visit claim token).
1010
+ const auth = await resolvePreVisitConsentAuthorization({
1011
+ locationUuid,
1012
+ beneficiaryCode: crNo,
1013
+ });
1014
+ const token = String(auth?.token ?? '').trim();
1015
+ const status = String(auth?.status ?? '').trim().toUpperCase();
1016
+ if (!token) {
1017
+ throw new Error(
1018
+ 'No pre-visit authorization found. Finish biometric authorize, then confirm here.',
1019
+ );
1020
+ }
1021
+ if (status && status !== 'AUTHORIZED_PENDING_VISIT') {
1022
+ throw new Error(
1023
+ `Expected AUTHORIZED_PENDING_VISIT for elective preauth, got ${status}. Complete pre-visit biometric authorize first.`,
1024
+ );
1025
+ }
1026
+ setActiveConsentToken(token);
1027
+ setConsentDone(true);
1028
+ showSnackbar({
1029
+ kind: 'success',
1030
+ title: 'Pre-visit authorization verified',
1031
+ subtitle: auth?.status ? `Status: ${auth.status}` : undefined,
1032
+ });
1033
+ } catch (e: any) {
1034
+ showSnackbar({
1035
+ kind: 'error',
1036
+ title: 'Consent token verification failed',
1037
+ subtitle: String(e?.message ?? e),
1038
+ });
1039
+ } finally {
1040
+ setSendingOtp(false);
1041
+ }
1042
+ };
1043
+
1044
+ const handleAttachmentsChange = (updater: (prev: PreauthAttachmentRow[]) => PreauthAttachmentRow[]) => {
1045
+ markDirty();
1046
+ setAttachments(updater);
1047
+ };
1048
+
1049
+ const addableDocTypes = useMemo(() => {
1050
+ const attached = new Set(attachments.map((a) => a.document_type));
1051
+ const base = optionalDocs.length > 0 ? optionalDocs : DOC_TYPE_OPTIONS;
1052
+ return [...new Set(base)].filter((d) => !attached.has(d));
1053
+ }, [attachments, optionalDocs]);
1054
+
1055
+ useEffect(
1056
+ () => () => {
1057
+ attachments.forEach((a) => {
1058
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
1059
+ });
1060
+ },
1061
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1062
+ [],
1063
+ );
1064
+
1065
+ const buildPayload = (): PreauthFormPayload => {
1066
+ // Contiguous indexes: attachments[i] → attachments_i_file_blob
1067
+ const withFiles = attachments.filter((a) => a.file);
1068
+ const files: Record<string, File> = {};
1069
+ const attachmentMeta = withFiles.map((a, index) => {
1070
+ const field = preauthAttachmentFieldName(index);
1071
+ files[field] = a.file!;
1072
+ return {
1073
+ document_title: a.document_title,
1074
+ document_type: a.document_type,
1075
+ file_field_name: field,
1076
+ };
1077
+ });
1078
+
1079
+ const billUnitPrice = String(billItem.item_price ?? billItem.item_total_price ?? unitPrice ?? '').trim();
1080
+ const resolvedUnitPrice = billUnitPrice;
1081
+
1082
+ const payload: PreauthFormPayload = {
1083
+ service_start: serviceStart,
1084
+ service_end: serviceEnd,
1085
+ items: [{ unit_price: resolvedUnitPrice || '0' }],
1086
+ diagnoses: [{ consent_token: consentToken, icd_code: icdCode.trim() }],
1087
+ doctors: [
1088
+ {
1089
+ identification_number: doctorId.trim(),
1090
+ identification_type: DEFAULT_DOCTOR_ID_TYPE,
1091
+ regulation_body: normalizeRegulationBody(regulationBody),
1092
+ intervention_code: intervention.code,
1093
+ is_primary: true,
1094
+ },
1095
+ ],
1096
+ attachments: attachmentMeta,
1097
+ provider_notification_email: providerEmail.trim(),
1098
+ locationUuid,
1099
+ files,
1100
+ };
1101
+
1102
+ if (specialty.requiresSurgicalPreauth) {
1103
+ payload.chief_complaint = chiefComplaint.trim();
1104
+ payload.vital_signs = vitalSigns.trim();
1105
+ payload.history_of_present_illness = hpi.trim();
1106
+ payload.physical_examination = physicalExam.trim();
1107
+ payload.investigation_report_details = investigations.trim();
1108
+ payload.type_of_anaesthesia = anaesthesia;
1109
+ payload.surgery_date = surgeryDate;
1110
+ payload.is_condition_related_to_employment = relatedToEmployment;
1111
+ payload.is_condition_related_to_auto_or_other_accident = relatedToAccident;
1112
+ payload.is_co_insured = isCoInsured;
1113
+ if (isCoInsured && coInsuranceDetails.trim()) {
1114
+ payload.co_insurance_details = coInsuranceDetails.trim();
1115
+ }
1116
+ }
1117
+
1118
+ if (specialty.requiresRenalPreauth) {
1119
+ const sessionsNum = Number(sessionsRequired);
1120
+ payload.number_of_sessions_required = Number.isFinite(sessionsNum) ? sessionsNum : sessionsRequired;
1121
+ payload.cost_per_session = String(costPerSession || billUnitPrice || '').trim();
1122
+ payload.frequency_of_sessions = frequency;
1123
+ payload.clinical_indications = clinicalIndications.trim();
1124
+ payload.start_date = startDate;
1125
+ payload.is_co_insured = isCoInsured;
1126
+ if (isCoInsured && coInsuranceDetails.trim()) {
1127
+ payload.co_insurance_details = coInsuranceDetails.trim();
1128
+ }
1129
+ }
1130
+
1131
+ if (specialty.requiresOpticalPreauth) {
1132
+ payload.necessity_of_service = necessity.trim();
1133
+ payload.lens_prescription = lensPrescription;
1134
+ payload.lens_amount = lensAmount;
1135
+ payload.eye_examination_amount = eyeExamAmount;
1136
+ payload.frame_amount = frameAmount;
1137
+ payload.new_or_replacement = newOrReplacement;
1138
+ payload.clinical_indications = clinicalIndications.trim();
1139
+ }
1140
+
1141
+ if (specialty.requiresOncologyPreauth) {
1142
+ payload.carcinoma_staging = carcinomaStaging;
1143
+ payload.comorbidity = comorbidity.trim();
1144
+ // Always arrays — HIE: "Expected a JSON list for metastases and treatment_setting"
1145
+ payload.metastases = [metastases];
1146
+ payload.treatment_setting = [treatmentSetting];
1147
+ {
1148
+ const sessionsNum = Number(sessionsRequired);
1149
+ payload.number_of_sessions_required = Number.isFinite(sessionsNum) ? sessionsNum : sessionsRequired;
1150
+ }
1151
+ payload.cost_per_session = costPerSession;
1152
+ payload.is_co_insured = isCoInsured;
1153
+ payload.start_date = startDate;
1154
+ if (progressReport.trim()) {
1155
+ payload.progress_report = progressReport.trim();
1156
+ }
1157
+ }
1158
+
1159
+ if (specialty.requiresRadiologyPreauth) {
1160
+ payload.clinical_indications = clinicalIndications.trim();
1161
+ }
1162
+
1163
+ if (isElective) {
1164
+ payload.expected_service_start_date = expectedServiceStartDate;
1165
+ }
1166
+
1167
+ return payload;
1168
+ };
1169
+
1170
+ const handleCancelExistingPreauth = async () => {
1171
+ const code = String(intervention.code || billItem.intervention_code || '').trim();
1172
+ const token = String(
1173
+ existingPreauth?.consentToken || activeConsentToken || consentTokenProp || '',
1174
+ ).trim();
1175
+ if (!token || !code || !locationUuid) {
1176
+ showSnackbar({
1177
+ kind: 'error',
1178
+ title: 'Cannot cancel preauth',
1179
+ subtitle: 'Missing consent token or intervention code for this preauth.',
1180
+ });
1181
+ return;
1182
+ }
1183
+ setCancellingExistingPreauth(true);
1184
+ try {
1185
+ await cancelPreauth({
1186
+ consentToken: token,
1187
+ interventionCode: code,
1188
+ locationUuid,
1189
+ });
1190
+ setExistingPreauth(null);
1191
+ showSnackbar({
1192
+ kind: 'success',
1193
+ title: 'Preauth cancelled',
1194
+ subtitle: `${code} was cancelled. You can raise a new preauth.`,
1195
+ });
1196
+ } catch (e: any) {
1197
+ showSnackbar({
1198
+ kind: 'error',
1199
+ title: 'Cancel failed',
1200
+ subtitle: extractHieErrorMessage(e, 'Failed to cancel preauth'),
1201
+ });
1202
+ } finally {
1203
+ setCancellingExistingPreauth(false);
1204
+ }
1205
+ };
1206
+
1207
+ const handleSubmit = async () => {
1208
+ if (!consentDone) {
1209
+ showSnackbar({ kind: 'error', title: 'Consent required', subtitle: 'Complete OTP or biometric consent first.' });
1210
+ return;
1211
+ }
1212
+ if (existingPreauth?.blocking) {
1213
+ showSnackbar({
1214
+ kind: 'error',
1215
+ title: 'Existing preauth found',
1216
+ subtitle: `An active preauth already exists for ${intervention.code} (status: ${existingPreauth.status}${
1217
+ existingPreauth.preauthCode ? `, code: ${existingPreauth.preauthCode}` : ''
1218
+ }).`,
1219
+ });
1220
+ return;
1221
+ }
1222
+ if (isElective && consentMethod === 'otp' && (!otp || otp.length < 4)) {
1223
+ showSnackbar({
1224
+ kind: 'error',
1225
+ title: 'Missing OTP',
1226
+ subtitle: 'Send OTP, enter the code, confirm it, then submit.',
1227
+ });
1228
+ return;
1229
+ }
1230
+ if (!isElective && !consentToken) {
1231
+ showSnackbar({
1232
+ kind: 'error',
1233
+ title: 'Missing authorization',
1234
+ subtitle: 'A claim token is required.',
1235
+ });
1236
+ return;
1237
+ }
1238
+ const billUnitPrice = String(billItem.item_price ?? billItem.item_total_price ?? unitPrice ?? '').trim();
1239
+ const resolvedUnitPrice = billUnitPrice;
1240
+ const needsClinicalIndications =
1241
+ specialty.requiresRadiologyPreauth ||
1242
+ specialty.requiresRenalPreauth ||
1243
+ specialty.requiresOpticalPreauth;
1244
+ const missingFormFields = [...formRelevant].filter((k) => !formFound.has(k));
1245
+ const missingRequired = [
1246
+ !icdCode.trim() && 'ICD-11 diagnosis',
1247
+ !doctorId.trim() && 'Doctor National ID',
1248
+ !providerEmail.trim() && 'Provider notification email',
1249
+ !resolvedUnitPrice &&
1250
+ (needsBillableServicePicker ? 'Unit price (select a billable service)' : 'Unit price (from bill)'),
1251
+ isElective && !expectedServiceStartDate && 'Expected service start date',
1252
+ needsClinicalIndications &&
1253
+ !clinicalIndications.trim() &&
1254
+ 'Clinical indications (complete Pre-authorization form on the patient chart)',
1255
+ missingFormFields.length > 0 &&
1256
+ `Pre-authorization form fields (${missingFormFields.join(', ')})`,
1257
+ ].filter(Boolean);
1258
+ if (missingRequired.length) {
1259
+ showSnackbar({
1260
+ kind: 'error',
1261
+ title: 'Missing required fields',
1262
+ subtitle: `Fill: ${missingRequired.join(', ')}`,
1263
+ });
1264
+ return;
1265
+ }
1266
+ if (!requiredAttachmentsReady) {
1267
+ showSnackbar({
1268
+ kind: 'error',
1269
+ title: 'Attachments required',
1270
+ subtitle: 'Generate or add a file for every required document, then Submit preauth.',
1271
+ });
1272
+ return;
1273
+ }
1274
+
1275
+ setSubmitting(true);
1276
+ abortRef.current = new AbortController();
1277
+ try {
1278
+ let tokenForPreauth = consentToken;
1279
+
1280
+ if (isElective) {
1281
+ // Elective is pre-visit: mint AUTHORIZED_PENDING_VISIT via authorize, then POST /preauths.
1282
+ // Do not close/reject existing open claim visits.
1283
+ if (consentMethod === 'otp') {
1284
+ const auth = await resolveOrCreateElectivePreVisitAuthorization({
1285
+ patientId: crNo,
1286
+ otp,
1287
+ interventions: [intervention.code],
1288
+ serviceType: resolveElectiveServiceType(),
1289
+ locationUuid,
1290
+ beneficiaryContactId: selectedContactId || undefined,
1291
+ });
1292
+ tokenForPreauth = extractElectiveAuthorizationToken(auth);
1293
+ if (!tokenForPreauth) {
1294
+ throw new Error('Authorize succeeded but no authorization token was returned.');
1295
+ }
1296
+ setActiveConsentToken(tokenForPreauth);
1297
+ } else if (!tokenForPreauth) {
1298
+ throw new Error(
1299
+ 'Missing pre-visit authorization token. Confirm biometric consent, then submit.',
1300
+ );
1301
+ }
1302
+ } else {
1303
+ await ensureInterventionOnVisit(tokenForPreauth, intervention.code, locationUuid);
1304
+ }
1305
+
1306
+ const payload = buildPayload();
1307
+ if (isElective && tokenForPreauth) {
1308
+ payload.diagnoses = (payload.diagnoses ?? []).map((d) => ({
1309
+ ...d,
1310
+ consent_token: tokenForPreauth,
1311
+ }));
1312
+ }
1313
+ if (isElective) {
1314
+ await createElectivePreauth(
1315
+ payload,
1316
+ {
1317
+ code: interventionForSubmit.code,
1318
+ requiresRadiologyPreauth: interventionForSubmit.requiresRadiologyPreauth,
1319
+ requiresOncologyPreauth: interventionForSubmit.requiresOncologyPreauth,
1320
+ requiresOpticalPreauth: interventionForSubmit.requiresOpticalPreauth,
1321
+ requiresRenalPreauth: interventionForSubmit.requiresRenalPreauth,
1322
+ requiresSurgicalPreauth: interventionForSubmit.requiresSurgicalPreauth,
1323
+ },
1324
+ tokenForPreauth,
1325
+ );
1326
+ } else {
1327
+ await createPreauth(
1328
+ payload,
1329
+ {
1330
+ code: interventionForSubmit.code,
1331
+ requiresRadiologyPreauth: interventionForSubmit.requiresRadiologyPreauth,
1332
+ requiresOncologyPreauth: interventionForSubmit.requiresOncologyPreauth,
1333
+ requiresOpticalPreauth: interventionForSubmit.requiresOpticalPreauth,
1334
+ requiresRenalPreauth: interventionForSubmit.requiresRenalPreauth,
1335
+ requiresSurgicalPreauth: interventionForSubmit.requiresSurgicalPreauth,
1336
+ },
1337
+ tokenForPreauth,
1338
+ );
1339
+ }
1340
+
1341
+ setPolling(true);
1342
+ const polled = await pollPreauthUntilSubmitted(
1343
+ tokenForPreauth,
1344
+ locationUuid,
1345
+ intervention.code,
1346
+ { signal: abortRef.current.signal },
1347
+ );
1348
+ const status = polled.status;
1349
+ const preauthCode = polled.preauthCode;
1350
+
1351
+ if (preauthCode) {
1352
+ storePreauthCode(tokenForPreauth, intervention.code, preauthCode);
1353
+ }
1354
+
1355
+ // Sync selected ICD onto the claim (normal / post-claim only).
1356
+ if (!isElective) {
1357
+ try {
1358
+ const visitDx = selectedDx?.kind === 'visit' ? selectedDx.dx : null;
1359
+ await addClaimDiagnosis({
1360
+ consentToken: tokenForPreauth,
1361
+ interventionCode: intervention.code,
1362
+ icdCode: icdCode.trim(),
1363
+ locationUuid,
1364
+ practitionerIdentificationNumber: (visitDx?.practioner_nat_id || doctorId || '').trim(),
1365
+ practitionerIdentificationType: DEFAULT_DOCTOR_ID_TYPE,
1366
+ practitionerRegulationBody: normalizeRegulationBody(
1367
+ visitDx?.practitioner_body || regulationBody,
1368
+ ),
1369
+ });
1370
+ } catch {
1371
+ // Preauth succeeded; claim diagnosis can still be added from bill details.
1372
+ }
1373
+ }
1374
+
1375
+ const awaitingDoctor = status === 'PENDING_DOCTOR_APPROVAL';
1376
+ showSnackbar({
1377
+ kind: 'success',
1378
+ title: awaitingDoctor
1379
+ ? isElective
1380
+ ? 'Elective preauth submitted'
1381
+ : 'Preauth submitted'
1382
+ : status === 'FINALISED' || status === 'FINALIZED'
1383
+ ? 'Preauth finalised'
1384
+ : 'Preauth submitted',
1385
+ subtitle: awaitingDoctor
1386
+ ? 'Awaiting doctor approval. Track progress on the Preauthorizations Status tab.'
1387
+ : preauthCode
1388
+ ? `Code: ${preauthCode}`
1389
+ : `Status: ${status}`,
1390
+ });
1391
+ onSuccess?.({ consentToken: tokenForPreauth, preauthCode, status });
1392
+ allowCloseWithoutPromptRef.current = true;
1393
+ setDirty(false);
1394
+ setSubmitting(false);
1395
+ setPolling(false);
1396
+ closeWorkspace?.({ discardUnsavedChanges: true } as any);
1397
+ } catch (e: any) {
1398
+ showSnackbar({
1399
+ kind: 'error',
1400
+ title: 'Preauth failed',
1401
+ subtitle: extractHieErrorMessage(e, 'Failed to create preauth'),
1402
+ });
1403
+ } finally {
1404
+ if (!allowCloseWithoutPromptRef.current) {
1405
+ setSubmitting(false);
1406
+ setPolling(false);
1407
+ }
1408
+ }
1409
+ };
1410
+
1411
+ if (!isElective && !consentToken) {
1412
+ return null;
1413
+ }
1414
+
1415
+ return (
1416
+ <Form className={styles.form}>
1417
+ <div className={styles.body}>
1418
+ <div className={styles.header}>
1419
+ <div>
1420
+ <h4>{t('raisePreauth', 'Raise preauth')}</h4>
1421
+ <p className={styles.muted}>
1422
+ {patientName} · {intervention.code} · {billableService}
1423
+ </p>
1424
+ </div>
1425
+ <Tag type={isElective ? 'magenta' : 'blue'} size="md">
1426
+ {isElective ? 'Elective' : specialtyLabel} preauth
1427
+ </Tag>
1428
+ </div>
1429
+
1430
+ {checkingExistingPreauth ? (
1431
+ <InlineLoading description="Checking for existing preauth…" />
1432
+ ) : existingPreauth ? (
1433
+ <div
1434
+ className={`${styles.existingBanner} ${
1435
+ existingPreauth.blocking ? styles.existingBannerError : styles.existingBannerWarning
1436
+ }`}
1437
+ role="status"
1438
+ >
1439
+ <p className={styles.existingBannerTitle}>
1440
+ {existingPreauth.blocking ? 'Existing active preauth' : 'Prior preauth found'}
1441
+ </p>
1442
+ <p className={styles.existingBannerMeta}>
1443
+ {[
1444
+ `SHA ${intervention.code}`,
1445
+ `Status: ${existingPreauth.status}`,
1446
+ existingPreauth.preauthCode ? `Code: ${existingPreauth.preauthCode}` : null,
1447
+ ]
1448
+ .filter(Boolean)
1449
+ .join(' · ')}
1450
+ </p>
1451
+ <p className={styles.existingBannerHint}>
1452
+ {existingPreauth.blocking
1453
+ ? 'Submit is blocked to avoid duplicates.'
1454
+ : String(existingPreauth.status).toUpperCase().includes('EXPIRED')
1455
+ ? 'This preauth has expired. You can raise a new one.'
1456
+ : 'You may resubmit after correcting the prior request.'}
1457
+ </p>
1458
+ <div className={styles.existingBannerActions}>
1459
+ <button
1460
+ type="button"
1461
+ className={styles.existingBannerCancel}
1462
+ onClick={() => void handleCancelExistingPreauth()}
1463
+ disabled={submitting || polling || cancellingExistingPreauth}
1464
+ >
1465
+ {cancellingExistingPreauth ? 'Cancelling…' : 'Cancel preauth'}
1466
+ </button>
1467
+ </div>
1468
+ </div>
1469
+ ) : null}
1470
+
1471
+ {isElective ? (
1472
+ <section className={styles.section}>
1473
+ <h5>Expected service start</h5>
1474
+ <DatePicker
1475
+ datePickerType="single"
1476
+ dateFormat="Y-m-d"
1477
+ value={expectedServiceStartDate ? dayjs(expectedServiceStartDate).toDate() : undefined}
1478
+ onChange={(dates: Date[]) => {
1479
+ markDirty();
1480
+ if (dates?.[0]) setExpectedServiceStartDate(dateToServiceIso(dates[0]));
1481
+ }}
1482
+ >
1483
+ <DatePickerInput
1484
+ id="expected-service-start"
1485
+ labelText="Expected service start date"
1486
+ placeholder="yyyy-mm-dd"
1487
+ />
1488
+ </DatePicker>
1489
+ </section>
1490
+ ) : null}
1491
+
1492
+ <section className={styles.section}>
1493
+ <h5>Consent</h5>
1494
+ <RadioButtonGroup
1495
+ legendText="Consent method"
1496
+ name="consent-method"
1497
+ valueSelected={consentMethod}
1498
+ onChange={(v) => {
1499
+ setConsentMethod(v as 'biometric' | 'otp');
1500
+ setConsentDone(false);
1501
+ }}
1502
+ >
1503
+ <RadioButton labelText="OTP" value="otp" id="preauth-otp" />
1504
+ <RadioButton labelText="Biometrics" value="biometric" id="preauth-bio" />
1505
+ </RadioButtonGroup>
1506
+ {consentMethod === 'otp' ? (
1507
+ <div className={styles.row}>
1508
+ {isElective ? (
1509
+ loadingContacts ? (
1510
+ <InlineLoading description="Loading contacts…" />
1511
+ ) : (
1512
+ <Dropdown
1513
+ id="preauth-otp-contact"
1514
+ titleText="Patient contact (OTP)"
1515
+ label="Select contact"
1516
+ items={patientContacts}
1517
+ itemToString={(item: PatientContactResult | null) =>
1518
+ item
1519
+ ? `${item.contactType || 'Contact'}: ${item.contactValue}${
1520
+ item.isConfirmed ? ' (confirmed)' : ''
1521
+ }`
1522
+ : ''
1523
+ }
1524
+ selectedItem={
1525
+ patientContacts.find((c) => String(c.id) === selectedContactId) ?? null
1526
+ }
1527
+ onChange={({ selectedItem }) => {
1528
+ setSelectedContactId(selectedItem?.id != null ? String(selectedItem.id) : '');
1529
+ setOtpSent(false);
1530
+ setConsentDone(false);
1531
+ setOtp('');
1532
+ }}
1533
+ />
1534
+ )
1535
+ ) : null}
1536
+ <Button
1537
+ type="button"
1538
+ kind="tertiary"
1539
+ size="sm"
1540
+ onClick={handleSendOtp}
1541
+ disabled={
1542
+ sendingOtp ||
1543
+ consentDone ||
1544
+ (isElective && (loadingContacts || (!selectedContactId && patientContacts.length > 0)))
1545
+ }
1546
+ >
1547
+ {sendingOtp ? <InlineLoading description="Sending…" /> : 'Send OTP'}
1548
+ </Button>
1549
+ <TextInput
1550
+ id="preauth-otp-input"
1551
+ labelText="OTP"
1552
+ value={otp}
1553
+ disabled={!otpSent || consentDone}
1554
+ onChange={(e) => setOtp(e.target.value)}
1555
+ />
1556
+ <Button
1557
+ type="button"
1558
+ kind="primary"
1559
+ size="sm"
1560
+ onClick={handleVerifyOtp}
1561
+ disabled={!otpSent || consentDone}
1562
+ >
1563
+ Confirm OTP
1564
+ </Button>
1565
+ </div>
1566
+ ) : (
1567
+ <div className={styles.row}>
1568
+ <p className={styles.muted}>
1569
+ Complete biometric capture on the patient device, then confirm below.
1570
+ </p>
1571
+ <Button kind="primary" size="sm" onClick={handleConfirmBiometric} disabled={consentDone}>
1572
+ Confirm biometric consent
1573
+ </Button>
1574
+ </div>
1575
+ )}
1576
+ {consentDone ? <Tag type="green" size="sm">Consent complete</Tag> : null}
1577
+ </section>
1578
+
1579
+ <section className={styles.section}>
1580
+ <h5>Service &amp; provider</h5>
1581
+ <div className={styles.row}>
1582
+ <DatePicker
1583
+ datePickerType="single"
1584
+ dateFormat="Y-m-d"
1585
+ value={serviceStart ? dayjs(serviceStart).toDate() : undefined}
1586
+ onChange={([date]) => {
1587
+ if (!date) return;
1588
+ markDirty();
1589
+ setServiceStart(dateToServiceIso(date, dayjs(serviceStart).format('HH:mm:ss')));
1590
+ }}
1591
+ >
1592
+ <DatePickerInput id="preauth-service-start" labelText="Service start" placeholder="yyyy-mm-dd" />
1593
+ </DatePicker>
1594
+ <TextInput
1595
+ id="preauth-service-start-time"
1596
+ type="time"
1597
+ labelText="Start time"
1598
+ value={dayjs(serviceStart).format('HH:mm')}
1599
+ onChange={(e) => {
1600
+ markDirty();
1601
+ setServiceStart(dateToServiceIso(dayjs(serviceStart).toDate(), `${e.target.value}:00`));
1602
+ }}
1603
+ />
1604
+ </div>
1605
+ <div className={styles.row}>
1606
+ <DatePicker
1607
+ datePickerType="single"
1608
+ dateFormat="Y-m-d"
1609
+ value={serviceEnd ? dayjs(serviceEnd).toDate() : undefined}
1610
+ onChange={([date]) => {
1611
+ if (!date) return;
1612
+ markDirty();
1613
+ setServiceEnd(dateToServiceIso(date, dayjs(serviceEnd).format('HH:mm:ss')));
1614
+ }}
1615
+ >
1616
+ <DatePickerInput id="preauth-service-end" labelText="Service end" placeholder="yyyy-mm-dd" />
1617
+ </DatePicker>
1618
+ <TextInput
1619
+ id="preauth-service-end-time"
1620
+ type="time"
1621
+ labelText="End time"
1622
+ value={dayjs(serviceEnd).format('HH:mm')}
1623
+ onChange={(e) => {
1624
+ markDirty();
1625
+ setServiceEnd(dateToServiceIso(dayjs(serviceEnd).toDate(), `${e.target.value}:00`));
1626
+ }}
1627
+ />
1628
+ </div>
1629
+
1630
+ <TextInput
1631
+ id="provider-email"
1632
+ labelText="Provider notification email"
1633
+ value={providerEmail}
1634
+ onChange={(e) => {
1635
+ markDirty();
1636
+ setProviderEmail(e.target.value);
1637
+ }}
1638
+ />
1639
+
1640
+ {needsBillableServicePicker ? (
1641
+ <div className={styles.searchBlock}>
1642
+ <p className={styles.fieldHint}>
1643
+ No unit price on a patient bill for this intervention. Select a billable service to
1644
+ use its SHA tariff as the preauth unit price.
1645
+ </p>
1646
+ {loadingBillableItems ? (
1647
+ <InlineLoading description="Loading billable services…" />
1648
+ ) : (
1649
+ <ComboBox
1650
+ id="preauth-billable-service"
1651
+ titleText="Billable service"
1652
+ placeholder="Search billable service"
1653
+ items={(billableItems ?? []) as BillableServiceOption[]}
1654
+ itemToString={(item: BillableServiceOption | null) => item?.name ?? ''}
1655
+ selectedItem={selectedBillableService}
1656
+ onChange={({ selectedItem }) => {
1657
+ markDirty();
1658
+ const item = (selectedItem as BillableServiceOption | null) ?? null;
1659
+ setSelectedBillableService(item);
1660
+ const price = unitPriceFromBillableService(item, shaPaymentModeUuid);
1661
+ setUnitPrice(price);
1662
+ if (price) {
1663
+ setCostPerSession((prev) => (prev.trim() ? prev : price));
1664
+ }
1665
+ }}
1666
+ />
1667
+ )}
1668
+ {selectedBillableService && !unitPrice.trim() ? (
1669
+ <InlineNotification
1670
+ kind="warning"
1671
+ lowContrast
1672
+ hideCloseButton
1673
+ title="No price on service"
1674
+ subtitle="This billable service has no SHA (or other) price configured."
1675
+ />
1676
+ ) : null}
1677
+ </div>
1678
+ ) : null}
1679
+ {!billPriceLookupDone && isElective ? (
1680
+ <InlineLoading description="Looking up unit price from patient bills…" />
1681
+ ) : null}
1682
+ <TextInput
1683
+ id="unit-price"
1684
+ labelText={
1685
+ needsBillableServicePicker ? 'Unit price (from billable service)' : 'Unit price (from bill)'
1686
+ }
1687
+ type="number"
1688
+ value={unitPrice}
1689
+ readOnly
1690
+ />
1691
+
1692
+ <div className={styles.searchBlock}>
1693
+ <p className={styles.fieldHint}>
1694
+ Diagnosis (ICD-11). Prefills rank 1 when available; choose another if needed.
1695
+ </p>
1696
+ {loadingDx ? (
1697
+ <InlineLoading description="Loading patient diagnoses…" />
1698
+ ) : (
1699
+ <ComboBox
1700
+ id="preauth-diagnosis"
1701
+ titleText="Select diagnosis"
1702
+ placeholder="Search diagnosis / ICD-11 code"
1703
+ items={diagnosisItems}
1704
+ itemToString={diagnosisPickLabel}
1705
+ selectedItem={selectedDx}
1706
+ shouldFilterItem={() => true}
1707
+ onInputChange={handleDiagnosisInputChange}
1708
+ onChange={({ selectedItem }) => {
1709
+ applyDiagnosisPick(selectedItem as DiagnosisPick | null, { fromUser: true });
1710
+ if (selectedItem) {
1711
+ setConceptDxHits([]);
1712
+ }
1713
+ }}
1714
+ />
1715
+ )}
1716
+ {searchingDx ? <InlineLoading description="Searching concepts…" /> : null}
1717
+ <TextInput
1718
+ id="preauth-icd11"
1719
+ labelText="ICD-11 code"
1720
+ placeholder="Filled from selection (or type manually)"
1721
+ value={icdCode}
1722
+ onChange={(e) => {
1723
+ markDirty();
1724
+ setIcdCode(e.target.value.trim());
1725
+ }}
1726
+ />
1727
+ </div>
1728
+
1729
+ <div className={styles.searchBlock}>
1730
+ <p className={styles.fieldHint}>Search OpenMRS providers</p>
1731
+ <ComboBox
1732
+ id="preauth-provider"
1733
+ titleText="Provider"
1734
+ placeholder="Type at least 2 characters to search"
1735
+ items={providerHits}
1736
+ itemToString={providerItemLabel}
1737
+ selectedItem={selectedProvider}
1738
+ shouldFilterItem={() => true}
1739
+ onInputChange={handleProviderInputChange}
1740
+ onChange={({ selectedItem }) => applyProviderSelection(selectedItem as OpenMrsProviderHit | null)}
1741
+ />
1742
+ {searchingProviders ? <InlineLoading description="Searching providers…" /> : null}
1743
+ </div>
1744
+
1745
+ <div className={styles.row}>
1746
+ <TextInput
1747
+ id="doctor-id"
1748
+ labelText="Doctor National ID"
1749
+ value={doctorId}
1750
+ onChange={(e) => {
1751
+ markDirty();
1752
+ setDoctorId(e.target.value);
1753
+ setHwrHit(null);
1754
+ }}
1755
+ />
1756
+ <TextInput
1757
+ id="doctor-id-type"
1758
+ labelText="Doctor ID type"
1759
+ value={DEFAULT_DOCTOR_ID_TYPE}
1760
+ readOnly
1761
+ />
1762
+ <Button kind="tertiary" size="md" onClick={() => handleSearchHwr()} disabled={searchingHwr || !doctorId.trim()}>
1763
+ {searchingHwr ? 'Searching HWR…' : 'Search HWR (body)'}
1764
+ </Button>
1765
+ </div>
1766
+ {hwrHit ? (
1767
+ <Tag type="green" size="sm">
1768
+ {hwrHit.membership?.full_name || 'Match'} · {normalizeRegulationBody(hwrHit.membership?.licensing_body)}
1769
+ </Tag>
1770
+ ) : null}
1771
+ <div className={styles.row}>
1772
+ <Dropdown
1773
+ id="reg-body"
1774
+ titleText="Regulation body"
1775
+ label="Select regulation body"
1776
+ items={[...REGULATION_BODIES]}
1777
+ selectedItem={normalizeRegulationBody(regulationBody)}
1778
+ onChange={({ selectedItem }) => {
1779
+ markDirty();
1780
+ setRegulationBody(normalizeRegulationBody(selectedItem));
1781
+ }}
1782
+ />
1783
+ <p className={styles.fieldHint}>COC = Clinical Officers Council · NCK = Nursing Council of Kenya</p>
1784
+ </div>
1785
+ </section>
1786
+
1787
+ {specialty.requiresSurgicalPreauth ? (
1788
+ <section className={styles.section}>
1789
+ <h5>Surgical preauth</h5>
1790
+ <TextArea
1791
+ id="chief-complaint"
1792
+ labelText="Chief complaint"
1793
+ value={formFieldStatus('chiefComplaint') === 'loading' ? '' : chiefComplaint}
1794
+ readOnly={formFieldLocked('chiefComplaint')}
1795
+ invalid={formFieldInvalid('chiefComplaint')}
1796
+ invalidText={formFieldInvalid('chiefComplaint') ? FORM_MISSING_HINT : undefined}
1797
+ onChange={(e) => {
1798
+ if (formFieldLocked('chiefComplaint')) return;
1799
+ markDirty();
1800
+ setChiefComplaint(e.target.value);
1801
+ }}
1802
+ />
1803
+ <TextInput
1804
+ id="vital-signs"
1805
+ labelText="Vital signs"
1806
+ value={vitalSigns}
1807
+ onChange={(e) => {
1808
+ markDirty();
1809
+ setVitalSigns(e.target.value);
1810
+ }}
1811
+ />
1812
+ <TextArea
1813
+ id="hpi"
1814
+ labelText="History of present illness"
1815
+ value={formFieldStatus('hpi') === 'loading' ? '' : hpi}
1816
+ readOnly={formFieldLocked('hpi')}
1817
+ invalid={formFieldInvalid('hpi')}
1818
+ invalidText={formFieldInvalid('hpi') ? FORM_MISSING_HINT : undefined}
1819
+ onChange={(e) => {
1820
+ if (formFieldLocked('hpi')) return;
1821
+ markDirty();
1822
+ setHpi(e.target.value);
1823
+ }}
1824
+ />
1825
+ <TextArea
1826
+ id="physical-exam"
1827
+ labelText="Physical examination"
1828
+ value={formFieldStatus('physicalExam') === 'loading' ? '' : physicalExam}
1829
+ readOnly={formFieldLocked('physicalExam')}
1830
+ invalid={formFieldInvalid('physicalExam')}
1831
+ invalidText={formFieldInvalid('physicalExam') ? FORM_MISSING_HINT : undefined}
1832
+ onChange={(e) => {
1833
+ if (formFieldLocked('physicalExam')) return;
1834
+ markDirty();
1835
+ setPhysicalExam(e.target.value);
1836
+ }}
1837
+ />
1838
+ <TextArea
1839
+ id="investigations"
1840
+ labelText="Investigation report details"
1841
+ value={formFieldStatus('investigations') === 'loading' ? '' : investigations}
1842
+ readOnly={formFieldLocked('investigations')}
1843
+ invalid={formFieldInvalid('investigations')}
1844
+ invalidText={formFieldInvalid('investigations') ? FORM_MISSING_HINT : undefined}
1845
+ onChange={(e) => {
1846
+ if (formFieldLocked('investigations')) return;
1847
+ markDirty();
1848
+ setInvestigations(e.target.value);
1849
+ }}
1850
+ />
1851
+ <div className={styles.row}>
1852
+ <Dropdown
1853
+ id="anaesthesia"
1854
+ titleText="Type of anaesthesia"
1855
+ label="Select"
1856
+ items={ANAESTHESIA}
1857
+ selectedItem={anaesthesia || null}
1858
+ disabled={formFieldLocked('anaesthesia')}
1859
+ invalid={formFieldInvalid('anaesthesia')}
1860
+ invalidText={formFieldInvalid('anaesthesia') ? FORM_MISSING_HINT : undefined}
1861
+ onChange={({ selectedItem }) => {
1862
+ if (formFieldLocked('anaesthesia')) return;
1863
+ markDirty();
1864
+ setAnaesthesia(selectedItem ?? '');
1865
+ }}
1866
+ />
1867
+ <DatePicker
1868
+ datePickerType="single"
1869
+ dateFormat="Y-m-d"
1870
+ value={surgeryDate ? dayjs(surgeryDate).toDate() : undefined}
1871
+ onChange={(dates: Date[]) => {
1872
+ if (formFieldLocked('surgeryDate')) return;
1873
+ markDirty();
1874
+ if (dates?.[0]) setSurgeryDate(dateToServiceIso(dates[0]));
1875
+ }}
1876
+ >
1877
+ <DatePickerInput
1878
+ id="surgery-date"
1879
+ labelText="Surgery date"
1880
+ placeholder="yyyy-mm-dd"
1881
+ readOnly={formFieldLocked('surgeryDate')}
1882
+ invalid={formFieldInvalid('surgeryDate')}
1883
+ invalidText={formFieldInvalid('surgeryDate') ? FORM_MISSING_HINT : undefined}
1884
+ />
1885
+ </DatePicker>
1886
+ </div>
1887
+ <div className={styles.row}>
1888
+ <Checkbox
1889
+ id="related-employment"
1890
+ labelText="Related to employment"
1891
+ checked={relatedToEmployment}
1892
+ readOnly={formFieldLocked('relatedToEmployment')}
1893
+ onChange={(_, { checked }) => {
1894
+ if (formFieldLocked('relatedToEmployment')) return;
1895
+ markDirty();
1896
+ setRelatedToEmployment(checked);
1897
+ }}
1898
+ />
1899
+ <Checkbox
1900
+ id="related-accident"
1901
+ labelText="Related to auto/other accident"
1902
+ checked={relatedToAccident}
1903
+ readOnly={formFieldLocked('relatedToAccident')}
1904
+ onChange={(_, { checked }) => {
1905
+ if (formFieldLocked('relatedToAccident')) return;
1906
+ markDirty();
1907
+ setRelatedToAccident(checked);
1908
+ }}
1909
+ />
1910
+ <Checkbox
1911
+ id="surgical-co-insured"
1912
+ labelText="Is co-insured"
1913
+ checked={isCoInsured}
1914
+ readOnly={formFieldLocked('isCoInsured')}
1915
+ onChange={(_, { checked }) => {
1916
+ if (formFieldLocked('isCoInsured')) return;
1917
+ markDirty();
1918
+ setIsCoInsured(checked);
1919
+ }}
1920
+ />
1921
+ </div>
1922
+ {formFieldInvalid('relatedToEmployment') ||
1923
+ formFieldInvalid('relatedToAccident') ||
1924
+ formFieldInvalid('isCoInsured') ? (
1925
+ <InlineNotification
1926
+ kind="error"
1927
+ lowContrast
1928
+ hideCloseButton
1929
+ title="Missing Pre-authorization form answers"
1930
+ subtitle={FORM_MISSING_HINT}
1931
+ />
1932
+ ) : null}
1933
+ {isCoInsured ? (
1934
+ <TextArea
1935
+ id="co-insurance-details"
1936
+ labelText="Co-insurance details"
1937
+ value={formFieldStatus('coInsuranceDetails') === 'loading' ? '' : coInsuranceDetails}
1938
+ readOnly={formFieldLocked('coInsuranceDetails')}
1939
+ invalid={formFieldInvalid('coInsuranceDetails')}
1940
+ invalidText={formFieldInvalid('coInsuranceDetails') ? FORM_MISSING_HINT : undefined}
1941
+ onChange={(e) => {
1942
+ if (formFieldLocked('coInsuranceDetails')) return;
1943
+ markDirty();
1944
+ setCoInsuranceDetails(e.target.value);
1945
+ }}
1946
+ />
1947
+ ) : null}
1948
+ </section>
1949
+ ) : null}
1950
+
1951
+ {specialty.requiresRenalPreauth ? (
1952
+ <section className={styles.section}>
1953
+ <h5>Renal preauth</h5>
1954
+ <div className={styles.row}>
1955
+ <TextInput
1956
+ id="sessions-required"
1957
+ labelText="Number of sessions required"
1958
+ value={formFieldStatus('sessionsRequired') === 'loading' ? '' : sessionsRequired}
1959
+ readOnly={formFieldLocked('sessionsRequired')}
1960
+ invalid={formFieldInvalid('sessionsRequired')}
1961
+ invalidText={formFieldInvalid('sessionsRequired') ? FORM_MISSING_HINT : undefined}
1962
+ onChange={(e) => {
1963
+ if (formFieldLocked('sessionsRequired')) return;
1964
+ markDirty();
1965
+ setSessionsRequired(e.target.value);
1966
+ }}
1967
+ />
1968
+ <TextInput
1969
+ id="cost-per-session"
1970
+ labelText="Cost per session"
1971
+ value={costPerSession}
1972
+ onChange={(e) => {
1973
+ markDirty();
1974
+ setCostPerSession(e.target.value);
1975
+ }}
1976
+ />
1977
+ <Dropdown
1978
+ id="frequency"
1979
+ titleText="Frequency of sessions"
1980
+ label="Select"
1981
+ items={FREQUENCY}
1982
+ selectedItem={frequency || null}
1983
+ disabled={formFieldLocked('frequency')}
1984
+ invalid={formFieldInvalid('frequency')}
1985
+ invalidText={formFieldInvalid('frequency') ? FORM_MISSING_HINT : undefined}
1986
+ onChange={({ selectedItem }) => {
1987
+ if (formFieldLocked('frequency')) return;
1988
+ markDirty();
1989
+ setFrequency(selectedItem ?? '');
1990
+ }}
1991
+ />
1992
+ </div>
1993
+ <TextArea id="renal-indications" {...clinicalIndicationsFieldProps} />
1994
+ <div className={styles.row}>
1995
+ <DatePicker
1996
+ datePickerType="single"
1997
+ dateFormat="Y-m-d"
1998
+ value={startDate ? dayjs(startDate).toDate() : undefined}
1999
+ onChange={(dates: Date[]) => {
2000
+ if (formFieldLocked('startDate')) return;
2001
+ markDirty();
2002
+ if (dates?.[0]) setStartDate(dateToServiceIso(dates[0]));
2003
+ }}
2004
+ >
2005
+ <DatePickerInput
2006
+ id="renal-start"
2007
+ labelText="Start date"
2008
+ placeholder="yyyy-mm-dd"
2009
+ readOnly={formFieldLocked('startDate')}
2010
+ invalid={formFieldInvalid('startDate')}
2011
+ invalidText={formFieldInvalid('startDate') ? FORM_MISSING_HINT : undefined}
2012
+ />
2013
+ </DatePicker>
2014
+ <Checkbox
2015
+ id="renal-co-insured"
2016
+ labelText="Is co-insured"
2017
+ checked={isCoInsured}
2018
+ readOnly={formFieldLocked('isCoInsured')}
2019
+ onChange={(_, { checked }) => {
2020
+ if (formFieldLocked('isCoInsured')) return;
2021
+ markDirty();
2022
+ setIsCoInsured(checked);
2023
+ }}
2024
+ />
2025
+ </div>
2026
+ {formFieldInvalid('isCoInsured') ? (
2027
+ <InlineNotification
2028
+ kind="error"
2029
+ lowContrast
2030
+ hideCloseButton
2031
+ title="Missing co-insured answer"
2032
+ subtitle={FORM_MISSING_HINT}
2033
+ />
2034
+ ) : null}
2035
+ {isCoInsured ? (
2036
+ <TextArea
2037
+ id="renal-co-insurance-details"
2038
+ labelText="Co-insurance details"
2039
+ value={formFieldStatus('coInsuranceDetails') === 'loading' ? '' : coInsuranceDetails}
2040
+ readOnly={formFieldLocked('coInsuranceDetails')}
2041
+ invalid={formFieldInvalid('coInsuranceDetails')}
2042
+ invalidText={formFieldInvalid('coInsuranceDetails') ? FORM_MISSING_HINT : undefined}
2043
+ onChange={(e) => {
2044
+ if (formFieldLocked('coInsuranceDetails')) return;
2045
+ markDirty();
2046
+ setCoInsuranceDetails(e.target.value);
2047
+ }}
2048
+ />
2049
+ ) : null}
2050
+ </section>
2051
+ ) : null}
2052
+
2053
+ {specialty.requiresOpticalPreauth ? (
2054
+ <section className={styles.section}>
2055
+ <h5>Optical preauth</h5>
2056
+ <TextArea id="optical-indications" {...clinicalIndicationsFieldProps} />
2057
+ <TextArea
2058
+ id="necessity"
2059
+ labelText="Necessity of service"
2060
+ value={necessity}
2061
+ onChange={(e) => {
2062
+ markDirty();
2063
+ setNecessity(e.target.value);
2064
+ }}
2065
+ />
2066
+ <div className={styles.row}>
2067
+ <Dropdown
2068
+ id="lens-rx"
2069
+ titleText="Lens prescription"
2070
+ label="Select"
2071
+ items={LENS}
2072
+ selectedItem={lensPrescription || null}
2073
+ onChange={({ selectedItem }) => {
2074
+ markDirty();
2075
+ setLensPrescription(selectedItem ?? '');
2076
+ }}
2077
+ />
2078
+ <Dropdown
2079
+ id="new-or-repl"
2080
+ titleText="New or replacement"
2081
+ label="Select"
2082
+ items={NEW_OR_REPL}
2083
+ selectedItem={newOrReplacement || null}
2084
+ onChange={({ selectedItem }) => {
2085
+ markDirty();
2086
+ setNewOrReplacement(selectedItem ?? '');
2087
+ }}
2088
+ />
2089
+ </div>
2090
+ <div className={styles.row}>
2091
+ <TextInput
2092
+ id="lens-amount"
2093
+ labelText="Lens amount"
2094
+ value={lensAmount}
2095
+ onChange={(e) => {
2096
+ markDirty();
2097
+ setLensAmount(e.target.value);
2098
+ }}
2099
+ />
2100
+ <TextInput
2101
+ id="eye-exam-amount"
2102
+ labelText="Eye examination amount"
2103
+ value={eyeExamAmount}
2104
+ onChange={(e) => {
2105
+ markDirty();
2106
+ setEyeExamAmount(e.target.value);
2107
+ }}
2108
+ />
2109
+ <TextInput
2110
+ id="frame-amount"
2111
+ labelText="Frame amount"
2112
+ value={frameAmount}
2113
+ onChange={(e) => {
2114
+ markDirty();
2115
+ setFrameAmount(e.target.value);
2116
+ }}
2117
+ />
2118
+ </div>
2119
+ </section>
2120
+ ) : null}
2121
+
2122
+ {specialty.requiresOncologyPreauth ? (
2123
+ <section className={styles.section}>
2124
+ <h5>Oncology preauth</h5>
2125
+ <div className={styles.row}>
2126
+ <Dropdown
2127
+ id="carcinoma-staging"
2128
+ titleText="Carcinoma staging"
2129
+ label="Select"
2130
+ items={STAGING}
2131
+ selectedItem={carcinomaStaging || null}
2132
+ onChange={({ selectedItem }) => {
2133
+ markDirty();
2134
+ setCarcinomaStaging(selectedItem ?? '');
2135
+ }}
2136
+ />
2137
+ <Dropdown
2138
+ id="metastases"
2139
+ titleText="Metastases"
2140
+ label="Select"
2141
+ items={METASTASES}
2142
+ selectedItem={metastases || null}
2143
+ onChange={({ selectedItem }) => {
2144
+ markDirty();
2145
+ setMetastases(selectedItem ?? '');
2146
+ }}
2147
+ />
2148
+ <Dropdown
2149
+ id="treatment-setting"
2150
+ titleText="Treatment setting"
2151
+ label="Select"
2152
+ items={TREATMENT}
2153
+ selectedItem={treatmentSetting || null}
2154
+ onChange={({ selectedItem }) => {
2155
+ markDirty();
2156
+ setTreatmentSetting(selectedItem ?? '');
2157
+ }}
2158
+ />
2159
+ </div>
2160
+ <TextArea
2161
+ id="comorbidity"
2162
+ labelText="Comorbidity"
2163
+ value={comorbidity}
2164
+ onChange={(e) => {
2165
+ markDirty();
2166
+ setComorbidity(e.target.value);
2167
+ }}
2168
+ />
2169
+ <TextArea
2170
+ id="progress-report"
2171
+ labelText="Progress report"
2172
+ value={progressReport}
2173
+ onChange={(e) => {
2174
+ markDirty();
2175
+ setProgressReport(e.target.value);
2176
+ }}
2177
+ />
2178
+ <div className={styles.row}>
2179
+ <DatePicker
2180
+ datePickerType="single"
2181
+ dateFormat="Y-m-d"
2182
+ value={startDate ? dayjs(startDate).toDate() : undefined}
2183
+ onChange={(dates: Date[]) => {
2184
+ markDirty();
2185
+ if (dates?.[0]) setStartDate(dateToServiceIso(dates[0]));
2186
+ }}
2187
+ >
2188
+ <DatePickerInput id="oncology-start" labelText="Start date" placeholder="yyyy-mm-dd" />
2189
+ </DatePicker>
2190
+ </div>
2191
+ <div className={styles.row}>
2192
+ <TextInput
2193
+ id="onc-sessions"
2194
+ labelText="Number of sessions required"
2195
+ value={sessionsRequired}
2196
+ onChange={(e) => {
2197
+ markDirty();
2198
+ setSessionsRequired(e.target.value);
2199
+ }}
2200
+ />
2201
+ <TextInput
2202
+ id="onc-cost"
2203
+ labelText="Cost per session"
2204
+ value={costPerSession}
2205
+ onChange={(e) => {
2206
+ markDirty();
2207
+ setCostPerSession(e.target.value);
2208
+ }}
2209
+ />
2210
+ <Checkbox
2211
+ id="onc-co-insured"
2212
+ labelText="Is co-insured"
2213
+ checked={isCoInsured}
2214
+ onChange={(_, { checked }) => {
2215
+ markDirty();
2216
+ setIsCoInsured(checked);
2217
+ }}
2218
+ />
2219
+ </div>
2220
+ </section>
2221
+ ) : null}
2222
+
2223
+ {specialty.requiresRadiologyPreauth ? (
2224
+ <section className={styles.section}>
2225
+ <h5>Imaging / radiology preauth</h5>
2226
+ <TextArea id="imaging-indications" {...clinicalIndicationsFieldProps} />
2227
+ </section>
2228
+ ) : null}
2229
+
2230
+ <PreauthAttachments
2231
+ attachments={attachments}
2232
+ onChange={handleAttachmentsChange}
2233
+ addableDocTypes={addableDocTypes}
2234
+ billItem={billItem as Record<string, unknown>}
2235
+ intervention={interventionForSubmit}
2236
+ disabled={submitting || polling}
2237
+ />
2238
+ </div>
2239
+
2240
+ <ButtonSet className={styles.buttonSet}>
2241
+ <Button kind="secondary" onClick={() => closeWorkspace()} disabled={submitting || polling}>
2242
+ Cancel
2243
+ </Button>
2244
+ <Button
2245
+ kind="primary"
2246
+ onClick={handleSubmit}
2247
+ disabled={!consentDone || submitting || polling || Boolean(existingPreauth?.blocking) || checkingExistingPreauth}
2248
+ >
2249
+ {polling ? (
2250
+ <InlineLoading description="Confirming submission…" />
2251
+ ) : submitting ? (
2252
+ <InlineLoading description="Submitting…" />
2253
+ ) : (
2254
+ t('submitPreauth', 'Submit preauth')
2255
+ )}
2256
+ </Button>
2257
+ </ButtonSet>
2258
+ </Form>
2259
+ );
2260
+ };
2261
+
2262
+ const PreauthWorkspace: React.FC<PreauthWorkspaceProps> = (props) => (
2263
+ <PreauthForm key={`${props.consentToken}::${props.intervention?.code ?? ''}`} {...props} />
2264
+ );
2265
+
2266
+ export default PreauthWorkspace;