@ampath/esm-dha-workflow-app 4.0.0-next.34 → 4.0.0-next.340

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